Merge branch 'VP9_drc' into 'main'

Draft: v4l2codecs: Replace VIDIOC_REQBUFS calls by VIDIOC_CREATE_BUFS

See merge request gstreamer/gstreamer!4919
This commit is contained in:
Benjamin Gaignard 2024-05-03 16:06:53 +00:00
commit 6e3bdedc92
12 changed files with 353 additions and 51 deletions

View file

@ -7161,6 +7161,20 @@ This method does not preserve the original order of @caps.</doc>
</instance-parameter>
</parameters>
</method>
<method name="sort" c:identifier="gst_caps_sort" introspectable="0">
<source-position filename="../subprojects/gstreamer/gst/gstcaps.h"/>
<return-value transfer-ownership="full">
<type name="Caps" c:type="GstCaps*"/>
</return-value>
<parameters>
<instance-parameter name="caps" transfer-ownership="none">
<type name="Caps" c:type="GstCaps*"/>
</instance-parameter>
<parameter name="func" transfer-ownership="none">
<type name="CapsSortFunc" c:type="GstCapsSortFunc"/>
</parameter>
</parameters>
</method>
<method name="steal_structure" c:identifier="gst_caps_steal_structure">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstcaps.c">Retrieves the structure with the given index from the list of structures
contained in @caps. The caller becomes the owner of the returned structure.</doc>
@ -7837,6 +7851,25 @@ the map operation should stop with %FALSE.</doc>
</parameter>
</parameters>
</callback>
<callback name="CapsSortFunc" c:type="GstCapsSortFunc">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstcaps.h">Returns; negative value if a &lt; b; zero if a = b; positive value if a &gt; b.4
Since 1.24</doc>
<source-position filename="../subprojects/gstreamer/gst/gstcaps.h"/>
<return-value transfer-ownership="none">
<type name="gint" c:type="int"/>
</return-value>
<parameters>
<parameter name="a" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstcaps.h">a structure.</doc>
<type name="Structure" c:type="const GstStructure*"/>
</parameter>
<parameter name="b" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstcaps.h">a structure to compare with.</doc>
<type name="Structure" c:type="const GstStructure*"/>
</parameter>
</parameters>
</callback>
<interface name="ChildProxy" c:symbol-prefix="child_proxy" c:type="GstChildProxy" glib:type-name="GstChildProxy" glib:get-type="gst_child_proxy_get_type" glib:type-struct="ChildProxyInterface">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstchildproxy.c">This interface abstracts handling of property sets for elements with
children. Imagine elements such as mixers or polyphonic generators. They all

View file

@ -4718,6 +4718,18 @@ and can be used as a binary.</doc>
<field name="size" readable="0" private="1">
<type name="gsize" c:type="gsize"/>
</field>
<field name="discont_state" readable="0" private="1">
<type name="GstVideo.VideoCodecState" c:type="GstVideoCodecState*"/>
</field>
<field name="user_data" readable="0" private="1">
<type name="gpointer" c:type="gpointer"/>
</field>
<field name="notify" readable="0" private="1">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</field>
<field name="check_strides" readable="0" private="1">
<type name="gboolean" c:type="gboolean"/>
</field>
<constructor name="new" c:identifier="gst_vp9_picture_new">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/codecs/gstvp9picture.c">Create new #GstVp9Picture</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/codecs/gstvp9picture.h"/>

View file

@ -44,6 +44,8 @@ struct _GstVp9Picture
/* raw data and size (does not have ownership) */
const guint8 * data;
gsize size;
gboolean check_strides;
};
GST_CODECS_API

View file

@ -146,6 +146,8 @@ gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
{
GstMemory *mem = GST_MEMORY_CAST (mini_object);
GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (mem->allocator);
GstV4l2Decoder *decoder = self->decoder;
GstPadDirection direction = self->direction;
GstV4l2CodecBuffer *buf;
GST_OBJECT_LOCK (self);
@ -154,9 +156,15 @@ gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
gst_memory_ref (mem);
if (gst_v4l2_codec_buffer_release_mem (buf)) {
GST_DEBUG_OBJECT (self, "Placing back buffer %i into pool", buf->index);
g_queue_push_tail (&self->pool, buf);
g_cond_signal (&self->buffer_cond);
if (self->detached) {
GST_DEBUG_OBJECT (self, "Detached pool freeing buffer %d", buf->index);
gst_v4l2_decoder_remove_buffer (decoder, direction, buf->index);
gst_v4l2_codec_buffer_free (buf);
} else {
GST_DEBUG_OBJECT (self, "Placing back buffer %i into pool", buf->index);
g_queue_push_tail (&self->pool, buf);
g_cond_signal (&self->buffer_cond);
}
}
GST_OBJECT_UNLOCK (self);
@ -173,28 +181,27 @@ gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self)
{
GstV4l2Decoder *decoder = self->decoder;
GstPadDirection direction = self->direction;
gint ret;
guint i;
ret = gst_v4l2_decoder_request_buffers (decoder, direction, self->pool_size);
if (ret < self->pool_size) {
if (ret >= 0)
GST_ERROR_OBJECT (self,
"%i buffer was needed, but only %i could be allocated",
self->pool_size, ret);
goto failed;
}
GST_DEBUG_OBJECT (self, "Try to create %d buffers", self->pool_size);
for (i = 0; i < self->pool_size; i++) {
GstV4l2CodecBuffer *buf = gst_v4l2_codec_buffer_new (GST_ALLOCATOR (self),
decoder, direction, i);
GstV4l2CodecBuffer *buf;
gint index = gst_v4l2_decoder_create_buffer (decoder, direction);
if (index < 0) {
GST_ERROR_OBJECT (self, "Failed to create buffer (ret =%d)", index);
goto failed;
}
buf =
gst_v4l2_codec_buffer_new (GST_ALLOCATOR (self), decoder, direction,
index);
g_queue_push_tail (&self->pool, buf);
}
return TRUE;
failed:
gst_v4l2_decoder_request_buffers (decoder, direction, 0);
return FALSE;
}
@ -208,10 +215,14 @@ static void
gst_v4l2_codec_allocator_dispose (GObject * object)
{
GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
GstV4l2Decoder *decoder = self->decoder;
GstPadDirection direction = self->direction;
GstV4l2CodecBuffer *buf;
while ((buf = g_queue_pop_head (&self->pool)))
while ((buf = g_queue_pop_head (&self->pool))) {
gst_v4l2_decoder_remove_buffer (decoder, direction, buf->index);
gst_v4l2_codec_buffer_free (buf);
}
if (self->decoder) {
gst_v4l2_codec_allocator_detach (self);
@ -345,10 +356,10 @@ gst_v4l2_codec_allocator_get_pool_size (GstV4l2CodecAllocator * self)
void
gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self)
{
GST_OBJECT_LOCK (self);
if (!self->detached) {
self->detached = TRUE;
gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
}
GST_OBJECT_UNLOCK (self);
}

View file

@ -400,9 +400,7 @@ gst_v4l2_codec_vp9_dec_open (GstVideoDecoder * decoder)
gst_v4l2_decoder_query_control_size (self->decoder,
V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, NULL);
/* V4L2 does not support non-keyframe resolution change, this will ask the
* base class to drop frame until the next keyframe as a workaround. */
gst_vp9_decoder_set_non_keyframe_format_change_support (vp9dec, FALSE);
gst_vp9_decoder_set_non_keyframe_format_change_support (vp9dec, TRUE);
return TRUE;
}
@ -476,10 +474,6 @@ gst_v4l2_codec_vp9_dec_negotiate (GstVideoDecoder * decoder)
GstCaps *peer_caps, *filter, *caps;
GstStaticCaps *static_filter;
/* Ignore downstream renegotiation request. */
if (self->streaming)
goto done;
GST_DEBUG_OBJECT (self, "Negotiate");
gst_v4l2_codec_vp9_dec_reset_allocation (self);
@ -528,14 +522,18 @@ gst_v4l2_codec_vp9_dec_negotiate (GstVideoDecoder * decoder)
}
gst_caps_unref (caps);
done:
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
if (!self->streaming) {
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
self->output_state =
self->output_state =
gst_v4l2_decoder_set_output_state (GST_VIDEO_DECODER (self), &self->vinfo,
&self->vinfo_drm, self->width, self->height, vp9dec->input_state);
self->output_state->caps =
gst_video_info_to_caps (&self->output_state->info);
}
if (GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder)) {
if (self->streaming)
return TRUE;
@ -571,11 +569,6 @@ gst_v4l2_codec_vp9_dec_decide_allocation (GstVideoDecoder * decoder,
guint min = 0;
guint num_bitstream;
/* If we are streaming here, then it means there is nothing allocation
* related in the new state and allocation can be ignored */
if (self->streaming)
goto no_internal_changes;
g_clear_object (&self->src_pool);
g_clear_object (&self->src_allocator);
@ -621,7 +614,6 @@ gst_v4l2_codec_vp9_dec_decide_allocation (GstVideoDecoder * decoder,
self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo);
no_internal_changes:
/* Our buffer pool is internal, we will let the base class create a video
* pool, and use it if we are running out of buffers or if downstream does
* not support GstVideoMeta */
@ -629,6 +621,66 @@ no_internal_changes:
(decoder, query);
}
static gboolean
_check_strides (GstV4l2CodecVp9Dec * self, GstVp9Picture * picture)
{
GstVideoInfo ref_vinfo;
gint i;
gst_video_info_set_format (&ref_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo),
self->width, self->height);
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->vinfo); i++) {
if (self->vinfo.stride[i] != ref_vinfo.stride[i] ||
self->vinfo.offset[i] != ref_vinfo.offset[i]) {
/* Strides are differents we need to copy the frame */
return TRUE;
}
}
return FALSE;
}
static GstFlowReturn
_check_resolution_change (GstV4l2CodecVp9Dec * self, GstVp9Picture * picture)
{
const GstVp9FrameHeader *frame_hdr = &picture->frame_hdr;
if ((self->width == frame_hdr->width) && self->height == frame_hdr->height)
return GST_FLOW_OK;
GST_VIDEO_INFO_WIDTH (&self->vinfo) = self->width = frame_hdr->width;
GST_VIDEO_INFO_HEIGHT (&self->vinfo) = self->height = frame_hdr->height;
GST_INFO_OBJECT (self, "Negotiate new resolution %dx%d",
self->width, self->height);
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_ERROR_OBJECT (self,
"Resolution changed, but failed to negotiate with downstream");
return GST_FLOW_NOT_NEGOTIATED;
}
GST_INFO_OBJECT (self, "Resolution changed to %dx%d for frame %d",
self->width, self->height, picture->parent.system_frame_number);
/* Because the resolution has changed strides need to be checked */
picture->check_strides = TRUE;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_v4l2_codec_vp9_dec_new_picture (GstVp9Decoder * decoder,
GstVideoCodecFrame * frame, GstVp9Picture * picture)
{
GstV4l2CodecVp9Dec *self = GST_V4L2_CODEC_VP9_DEC (decoder);
GST_INFO_OBJECT (self, "gst_v4l2_codec_vp9_dec_new_picture");
return _check_resolution_change (self, picture);
}
static GstFlowReturn
gst_v4l2_codec_vp9_dec_new_sequence (GstVp9Decoder * decoder,
const GstVp9FrameHeader * frame_hdr, gint max_dpb_size)
@ -945,6 +997,11 @@ gst_v4l2_codec_vp9_dec_duplicate_picture (GstVp9Decoder * decoder,
GST_DEBUG_OBJECT (decoder, "Duplicate picture %u",
GST_CODEC_PICTURE_FRAME_NUMBER (picture));
if (_check_resolution_change (GST_V4L2_CODEC_VP9_DEC (decoder), picture) !=
GST_FLOW_OK) {
return NULL;
}
new_picture = gst_vp9_picture_new ();
new_picture->frame_hdr = picture->frame_hdr;
GST_CODEC_PICTURE_FRAME_NUMBER (new_picture) = frame->system_frame_number;
@ -981,6 +1038,7 @@ gst_v4l2_codec_vp9_dec_output_picture (GstVp9Decoder * decoder,
{
GstV4l2CodecVp9Dec *self = GST_V4L2_CODEC_VP9_DEC (decoder);
GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
GstVp9Decoder *vp9dec = GST_VP9_DECODER (vdec);
GstV4l2Request *request = NULL;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
gint ret;
@ -1034,6 +1092,21 @@ gst_v4l2_codec_vp9_dec_output_picture (GstVp9Decoder * decoder,
goto error;
}
if (picture->check_strides) {
self->copy_frames = _check_strides (self, picture);
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
self->output_state =
gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
self->vinfo.finfo->format, self->width,
self->height, vp9dec->input_state);
self->output_state->caps =
gst_video_info_to_caps (&self->output_state->info);
}
if (self->copy_frames)
gst_v4l2_codec_vp9_dec_copy_output_buffer (self, frame);
@ -1194,6 +1267,8 @@ gst_v4l2_codec_vp9_dec_subclass_init (GstV4l2CodecVp9DecClass * klass,
vp9decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp9_dec_new_sequence);
vp9decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp9_dec_new_picture);
vp9decoder_class->start_picture =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp9_dec_start_picture);
vp9decoder_class->decode_picture =

View file

@ -78,6 +78,7 @@ struct _GstV4l2Decoder
GstQueueArray *request_pool;
GstQueueArray *pending_requests;
guint version;
GstVideoInfo *info;
enum v4l2_buf_type src_buf_type;
enum v4l2_buf_type sink_buf_type;
@ -90,6 +91,7 @@ struct _GstV4l2Decoder
/* detected features */
gboolean supports_holding_capture;
gboolean supports_remove_buffer;
};
G_DEFINE_TYPE_WITH_CODE (GstV4l2Decoder, gst_v4l2_decoder, GST_TYPE_OBJECT,
@ -167,6 +169,10 @@ gst_v4l2_decoder_open (GstV4l2Decoder * self)
{
gint ret;
struct v4l2_capability querycap;
struct v4l2_remove_buffers remove_bufs = {
.index = 0,
.count = 0,
};
guint32 capabilities;
self->media_fd = open (self->media_device, 0);
@ -211,6 +217,12 @@ gst_v4l2_decoder_open (GstV4l2Decoder * self)
return FALSE;
}
self->supports_remove_buffer = TRUE;
remove_bufs.type = self->sink_buf_type;
ret = ioctl (self->video_fd, VIDIOC_REMOVE_BUFS, &remove_bufs);
if (ret < 0 && errno == ENOTTY)
self->supports_remove_buffer = FALSE;
self->opened = TRUE;
return TRUE;
@ -512,7 +524,32 @@ gst_v4l2_decoder_enum_src_formats (GstV4l2Decoder * self,
GST_DEBUG_OBJECT (self, "Probed caps: %" GST_PTR_FORMAT, caps);
return caps;
return gst_v4l2_sort_caps (caps);
}
gboolean
gst_v4l2_decoder_remove_buffer (GstV4l2Decoder * self,
GstPadDirection direction, guint index)
{
gint ret;
struct v4l2_remove_buffers remove_bufs = {
.type = direction_to_buffer_type (self, direction),
.index = index,
.count = 1,
};
if (!self->supports_remove_buffer)
return TRUE;
GST_DEBUG_OBJECT (self, "remove buffers %d from index %d", remove_bufs.count,
remove_bufs.index);
ret = ioctl (self->video_fd, VIDIOC_REMOVE_BUFS, &remove_bufs);
if (ret < 0) {
GST_ERROR_OBJECT (self, "VIDIOC_REMOVE_BUF failed: %s", g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
@ -592,6 +629,10 @@ gst_v4l2_decoder_select_src_format (GstV4l2Decoder * self, GstCaps * caps,
gst_video_format_to_string (vinfo->finfo->format),
vinfo->width, vinfo->height);
if (self->info)
gst_video_info_free (self->info);
self->info = gst_video_info_copy (vinfo);
return TRUE;
}
@ -620,32 +661,44 @@ gst_v4l2_decoder_set_output_state (GstVideoDecoder * decoder,
}
gint
gst_v4l2_decoder_request_buffers (GstV4l2Decoder * self,
GstPadDirection direction, guint num_buffers)
gst_v4l2_decoder_create_buffer (GstV4l2Decoder * self,
GstPadDirection direction)
{
gint ret;
struct v4l2_requestbuffers reqbufs = {
.count = num_buffers,
int ret;
struct v4l2_create_buffers createbufs = {
.count = 1,
.memory = V4L2_MEMORY_MMAP,
.type = direction_to_buffer_type (self, direction),
};
GST_DEBUG_OBJECT (self, "Requesting %u buffers", num_buffers);
if (direction == GST_PAD_SINK) {
createbufs.format.type = self->sink_buf_type;
} else {
createbufs.format.type = self->src_buf_type;
}
ret = ioctl (self->video_fd, VIDIOC_REQBUFS, &reqbufs);
ret = ioctl (self->video_fd, VIDIOC_G_FMT, &createbufs.format);
if (ret < 0) {
GST_ERROR_OBJECT (self, "VIDIOC_REQBUFS failed: %s", g_strerror (errno));
GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno));
return FALSE;
}
GST_DEBUG_OBJECT (self, "Creating 1 buffer");
ret = ioctl (self->video_fd, VIDIOC_CREATE_BUFS, &createbufs);
if (ret < 0) {
GST_ERROR_OBJECT (self, "VIDIOC_CREATE_BUFS failed: %s",
g_strerror (errno));
return ret;
}
if (direction == GST_PAD_SINK) {
if (reqbufs.capabilities & V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF)
if (createbufs.capabilities & V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF)
self->supports_holding_capture = TRUE;
else
self->supports_holding_capture = FALSE;
}
return reqbufs.count;
return createbufs.index;
}
gboolean

View file

@ -82,9 +82,12 @@ GstVideoCodecState * gst_v4l2_decoder_set_output_state (GstVideoDecoder * decode
guint height,
GstVideoCodecState * reference);
gint gst_v4l2_decoder_request_buffers (GstV4l2Decoder * self,
GstPadDirection direction,
guint num_buffers);
gint gst_v4l2_decoder_create_buffer (GstV4l2Decoder * self,
GstPadDirection direction);
gint gst_v4l2_decoder_remove_buffer (GstV4l2Decoder * self,
GstPadDirection direction,
guint index);
gboolean gst_v4l2_decoder_export_buffer (GstV4l2Decoder * self,
GstPadDirection directon,

View file

@ -77,6 +77,19 @@ lookup_gst_fmt (GstVideoFormat gst_fmt)
return ret;
}
static int
gst_fmt_index (GstVideoFormat gst_fmt)
{
gint i;
for (i = 0; format_map[i].v4l2_pix_fmt; i++) {
if (format_map[i].gst_fmt == gst_fmt)
return i;
}
return i;
}
static void
set_stride (GstVideoInfo * info, gint plane, gint stride)
{
@ -203,3 +216,23 @@ gst_v4l2_format_from_video_format (GstVideoFormat format, guint32 * out_pix_fmt)
*out_pix_fmt = entry->v4l2_pix_fmt;
return TRUE;
}
static gint
gst_v4l2_caps_compare_structures (const GstStructure * a,
const GstStructure * b)
{
const GValue *formata = gst_structure_get_value (a, "format");
const GValue *formatb = gst_structure_get_value (b, "format");
GstVideoFormat vformata =
gst_video_format_from_string (g_value_get_string (formata));
GstVideoFormat vformatb =
gst_video_format_from_string (g_value_get_string (formatb));
return gst_fmt_index (vformata) - gst_fmt_index (vformatb);
}
GstCaps *
gst_v4l2_sort_caps (GstCaps * caps)
{
return gst_caps_sort (caps, gst_v4l2_caps_compare_structures);
}

View file

@ -38,4 +38,6 @@ gboolean gst_v4l2_format_to_video_format (guint32 pix_fmt,
gboolean gst_v4l2_format_from_video_format (GstVideoFormat format,
guint32 * out_pix_fmt);
GstCaps * gst_v4l2_sort_caps (GstCaps * caps);
#endif /* __GST_V4L2_FORMAT_H__ */

View file

@ -990,6 +990,8 @@ struct v4l2_requestbuffers {
#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4)
#define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5)
#define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6)
#define V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS (1 << 7)
#define V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS (1 << 8)
/**
* struct v4l2_plane - plane info for multi-planar buffers
@ -2544,6 +2546,9 @@ struct v4l2_dbg_chip_info {
* @flags: additional buffer management attributes (ignored unless the
* queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability
* and configured for MMAP streaming I/O).
* @max_num_buffers: if V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS capability flag is set
* this field indicate the maximum possible number of buffers
* for this queue.
* @reserved: future extensions
*/
struct v4l2_create_buffers {
@ -2553,7 +2558,22 @@ struct v4l2_create_buffers {
struct v4l2_format format;
__u32 capabilities;
__u32 flags;
__u32 reserved[6];
__u32 max_num_buffers;
__u32 reserved[5];
};
/**
* struct v4l2_remove_buffers - VIDIOC_REMOVE_BUFS argument
* @index: the first buffer to be removed
* @count: number of buffers to removed
* @type: enum v4l2_buf_type
* @reserved: future extensions
*/
struct v4l2_remove_buffers {
__u32 index;
__u32 count;
__u32 type;
__u32 reserved[13];
};
/*
@ -2655,6 +2675,7 @@ struct v4l2_create_buffers {
#define VIDIOC_DBG_G_CHIP_INFO _IOWR('V', 102, struct v4l2_dbg_chip_info)
#define VIDIOC_QUERY_EXT_CTRL _IOWR('V', 103, struct v4l2_query_ext_ctrl)
#define VIDIOC_REMOVE_BUFS _IOWR('V', 104, struct v4l2_remove_buffers)
/* Reminder: when adding new ioctls please add support for them to
drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */

View file

@ -2361,7 +2361,7 @@ caps_serialize (const GstCaps * caps, GstSerializeFlags flags)
}
/**
* gst_caps_to_string:
* gst_caps_to_string
* @caps: a #GstCaps
*
* Converts @caps to a string representation. This string representation
@ -2809,3 +2809,45 @@ gst_caps_take (GstCaps ** old_caps, GstCaps * new_caps)
return gst_mini_object_take ((GstMiniObject **) old_caps,
(GstMiniObject *) new_caps);
}
static gint
gst_caps_sort_structures (gconstpointer one, gconstpointer two,
gpointer user_data)
{
const GstStructure *struct1 = ((const GstCapsArrayElement *) one)->structure;
const GstStructure *struct2 = ((const GstCapsArrayElement *) two)->structure;
GstCapsSortFunc sort_func = (GstCapsSortFunc) user_data;
return sort_func (struct1, struct2);
}
/**
* gst_caps_sort: sorts caps using compare function
* @caps: caps to be sorted
* @func: compare function
*
* Returns: sorted caps.
*
* Since 1.24
*/
GstCaps *
gst_caps_sort (GstCaps * caps, GstCapsSortFunc func)
{
if (!func)
return caps;
/* empty/any caps, nothing to sort */
if (GST_CAPS_LEN (caps) == 0)
return caps;
/* one caps, nothing to sort */
if (GST_CAPS_LEN (caps) == 1)
return caps;
caps = gst_caps_make_writable (caps);
g_array_sort_with_data (GST_CAPS_ARRAY (caps), gst_caps_sort_structures,
func);
return caps;
}

View file

@ -357,6 +357,17 @@ typedef gboolean (*GstCapsFilterMapFunc) (GstCapsFeatures *features,
gpointer user_data);
/**
* GstCapsSortFunc:
* @a: a structure.
* @b: a structure to compare with.
*
* Returns; negative value if a < b; zero if a = b; positive value if a > b.4
*
* Since 1.24
*/
typedef int (*GstCapsSortFunc) (const GstStructure *a, const GstStructure *b);
GST_API
GType gst_caps_get_type (void);
@ -526,6 +537,10 @@ GstCaps * gst_caps_simplify (GstCaps *caps) G_GNUC_WARN_U
GST_API
GstCaps * gst_caps_fixate (GstCaps *caps) G_GNUC_WARN_UNUSED_RESULT;
GST_API
GstCaps * gst_caps_sort (GstCaps *caps, GstCapsSortFunc func);
/* utility */
GST_API