v4l2codecs: Rework handling of queues and pending requests

Starting from this patch, all queue and dequeue operation happening
on V4L2 is now abstracted with the request. Buffers are dequeued
automatically when pending requests are marked done and only 1 in-flight
request is now used.

Along with fixing issues with request not being reused with slice
decoders, this change reduces the memory footprint by allocating only
two bitstream buffers.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1881>
This commit is contained in:
Nicolas Dufresne 2020-12-14 17:07:01 -05:00 committed by GStreamer Merge Bot
parent 180ab8377f
commit 3db6f45ca9
4 changed files with 236 additions and 172 deletions

View File

@ -337,7 +337,7 @@ gst_v4l2_codec_h264_dec_decide_allocation (GstVideoDecoder * decoder,
min = MAX (2, min); min = MAX (2, min);
self->sink_allocator = gst_v4l2_codec_allocator_new (self->decoder, self->sink_allocator = gst_v4l2_codec_allocator_new (self->decoder,
GST_PAD_SINK, self->min_pool_size + 2); GST_PAD_SINK, 2);
self->src_allocator = gst_v4l2_codec_allocator_new (self->decoder, self->src_allocator = gst_v4l2_codec_allocator_new (self->decoder,
GST_PAD_SRC, self->min_pool_size + min + 4); GST_PAD_SRC, self->min_pool_size + min + 4);
self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo);
@ -825,24 +825,6 @@ fail:
return FALSE; return FALSE;
} }
static gboolean
gst_v4l2_codec_h264_dec_wait (GstV4l2CodecH264Dec * self,
GstV4l2Request * request)
{
gint ret = gst_v4l2_request_poll (request, GST_SECOND);
if (ret == 0) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Decoding frame took too long"), (NULL));
return FALSE;
} else if (ret < 0) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Decoding request failed: %s", g_strerror (errno)), (NULL));
return FALSE;
}
return TRUE;
}
static GstFlowReturn static GstFlowReturn
gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder, gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder,
GstVideoCodecFrame * frame, GstH264Picture * picture) GstVideoCodecFrame * frame, GstH264Picture * picture)
@ -850,41 +832,35 @@ gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder,
GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder); GstV4l2CodecH264Dec *self = GST_V4L2_CODEC_H264_DEC (decoder);
GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder); GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
GstV4l2Request *request = gst_h264_picture_get_user_data (picture); GstV4l2Request *request = gst_h264_picture_get_user_data (picture);
gint ret;
GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number); GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
if (gst_v4l2_request_is_done (request)) if (gst_v4l2_request_is_done (request))
goto finish_frame; goto finish_frame;
if (!gst_v4l2_codec_h264_dec_wait (self, request)) ret = gst_v4l2_request_poll (request, GST_SECOND);
if (ret == 0) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Decoding frame %u took too long", picture->system_frame_number),
(NULL));
goto error;
} else if (ret < 0) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Decoding request failed: %s", g_strerror (errno)), (NULL));
goto error; goto error;
while (TRUE) {
guint32 frame_num;
GstH264Picture *other_pic;
GstV4l2Request *other_request;
if (!gst_v4l2_decoder_dequeue_src (self->decoder, &frame_num)) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Decoder did not produce a frame"), (NULL));
goto error;
}
if (frame_num == picture->system_frame_number)
break;
other_pic = gst_h264_decoder_get_picture (decoder, frame_num);
if (other_pic) {
other_request = gst_h264_picture_get_user_data (other_pic);
gst_v4l2_request_set_done (other_request);
gst_h264_picture_unref (other_pic);
}
} }
finish_frame:
gst_v4l2_request_set_done (request); gst_v4l2_request_set_done (request);
g_return_val_if_fail (frame->output_buffer, GST_FLOW_ERROR); g_return_val_if_fail (frame->output_buffer, GST_FLOW_ERROR);
finish_frame:
if (gst_v4l2_request_failed (request)) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Failed to decode frame %u", picture->system_frame_number), (NULL));
goto error;
}
/* Hold on reference buffers for the rest of the picture lifetime */ /* Hold on reference buffers for the rest of the picture lifetime */
gst_h264_picture_set_user_data (picture, gst_h264_picture_set_user_data (picture,
gst_buffer_ref (frame->output_buffer), (GDestroyNotify) gst_buffer_unref); gst_buffer_ref (frame->output_buffer), (GDestroyNotify) gst_buffer_unref);
@ -937,14 +913,6 @@ gst_v4l2_codec_h264_dec_ensure_output_buffer (GstV4l2CodecH264Dec * self,
return FALSE; return FALSE;
} }
if (!gst_v4l2_decoder_queue_src_buffer (self->decoder, buffer,
frame->system_frame_number)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not accept the picture buffer."), (NULL));
gst_buffer_unref (buffer);
return FALSE;
}
frame->output_buffer = buffer; frame->output_buffer = buffer;
return TRUE; return TRUE;
} }
@ -953,8 +921,7 @@ static gboolean
gst_v4l2_codec_h264_dec_submit_bitstream (GstV4l2CodecH264Dec * self, gst_v4l2_codec_h264_dec_submit_bitstream (GstV4l2CodecH264Dec * self,
GstH264Picture * picture, guint flags) GstH264Picture * picture, guint flags)
{ {
GstVideoCodecFrame *frame; GstV4l2Request *prev_request, *request = NULL;
GstV4l2Request *prev_request, *request;
gsize bytesused; gsize bytesused;
gboolean ret = FALSE; gboolean ret = FALSE;
@ -989,23 +956,38 @@ gst_v4l2_codec_h264_dec_submit_bitstream (GstV4l2CodecH264Dec * self,
}; };
/* *INDENT-ON* */ /* *INDENT-ON* */
request = gst_v4l2_decoder_alloc_request (self->decoder); prev_request = gst_h264_picture_get_user_data (picture);
bytesused = self->bitstream_map.size;
gst_memory_unmap (self->bitstream, &self->bitstream_map);
self->bitstream_map = (GstMapInfo) GST_MAP_INFO_INIT;
gst_memory_resize (self->bitstream, 0, bytesused);
if (prev_request) {
request = gst_v4l2_decoder_alloc_sub_request (self->decoder, prev_request,
self->bitstream);
} else {
GstVideoCodecFrame *frame;
frame = gst_video_decoder_get_frame (GST_VIDEO_DECODER (self),
picture->system_frame_number);
g_return_val_if_fail (frame, FALSE);
if (!gst_v4l2_codec_h264_dec_ensure_output_buffer (self, frame))
goto done;
request = gst_v4l2_decoder_alloc_request (self->decoder,
picture->system_frame_number, self->bitstream, frame->output_buffer);
gst_video_codec_frame_unref (frame);
}
if (!request) { if (!request) {
GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
("Failed to allocate a media request object."), (NULL)); ("Failed to allocate a media request object."), (NULL));
goto done; goto done;
} }
frame = gst_video_decoder_get_frame (GST_VIDEO_DECODER (self),
picture->system_frame_number);
g_return_val_if_fail (frame, FALSE);
if (!gst_v4l2_codec_h264_dec_ensure_output_buffer (self, frame))
goto done;
gst_video_codec_frame_unref (frame);
if (!gst_v4l2_decoder_set_controls (self->decoder, request, control, if (!gst_v4l2_decoder_set_controls (self->decoder, request, control,
G_N_ELEMENTS (control))) { G_N_ELEMENTS (control))) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE, GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
@ -1013,30 +995,12 @@ gst_v4l2_codec_h264_dec_submit_bitstream (GstV4l2CodecH264Dec * self,
goto done; goto done;
} }
bytesused = self->bitstream_map.size; if (!gst_v4l2_request_queue (request, flags)) {
gst_memory_unmap (self->bitstream, &self->bitstream_map);
self->bitstream_map = (GstMapInfo) GST_MAP_INFO_INIT;
if (!gst_v4l2_decoder_queue_sink_mem (self->decoder, request, self->bitstream,
picture->system_frame_number, bytesused, flags)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not accept the bitstream data."), (NULL));
goto done;
}
if (!gst_v4l2_request_queue (request)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE, GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not accept the decode request."), (NULL)); ("Driver did not accept the decode request."), (NULL));
goto done; goto done;
} }
prev_request = gst_h264_picture_get_user_data (picture);
if (prev_request) {
if (!gst_v4l2_codec_h264_dec_wait (self, prev_request))
goto done;
gst_v4l2_request_set_done (prev_request);
}
gst_h264_picture_set_user_data (picture, g_steal_pointer (&request), gst_h264_picture_set_user_data (picture, g_steal_pointer (&request),
(GDestroyNotify) gst_v4l2_request_free); (GDestroyNotify) gst_v4l2_request_free);
ret = TRUE; ret = TRUE;
@ -1044,6 +1008,7 @@ gst_v4l2_codec_h264_dec_submit_bitstream (GstV4l2CodecH264Dec * self,
done: done:
if (request) if (request)
gst_v4l2_request_free (request); gst_v4l2_request_free (request);
gst_v4l2_codec_h264_dec_reset_picture (self); gst_v4l2_codec_h264_dec_reset_picture (self);
return ret; return ret;

View File

@ -246,7 +246,7 @@ gst_v4l2_codec_vp8_dec_decide_allocation (GstVideoDecoder * decoder,
min = MAX (2, min); min = MAX (2, min);
self->sink_allocator = gst_v4l2_codec_allocator_new (self->decoder, self->sink_allocator = gst_v4l2_codec_allocator_new (self->decoder,
GST_PAD_SINK, self->min_pool_size + 2); GST_PAD_SINK, 2);
self->src_allocator = gst_v4l2_codec_allocator_new (self->decoder, self->src_allocator = gst_v4l2_codec_allocator_new (self->decoder,
GST_PAD_SRC, self->min_pool_size + min + 4); GST_PAD_SRC, self->min_pool_size + min + 4);
self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo);
@ -539,15 +539,10 @@ gst_v4l2_codec_vp8_dec_end_picture (GstVp8Decoder * decoder,
}; };
/* *INDENT-ON* */ /* *INDENT-ON* */
request = gst_v4l2_decoder_alloc_request (self->decoder); bytesused = self->bitstream_map.size;
if (!request) { gst_memory_unmap (self->bitstream, &self->bitstream_map);
GST_ELEMENT_ERROR (decoder, RESOURCE, NO_SPACE_LEFT, self->bitstream_map = (GstMapInfo) GST_MAP_INFO_INIT;
("Failed to allocate a media request object."), (NULL)); gst_memory_resize (self->bitstream, 0, bytesused);
goto fail;
}
gst_vp8_picture_set_user_data (picture, request,
(GDestroyNotify) gst_v4l2_request_free);
flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool), flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool),
&buffer, NULL); &buffer, NULL);
@ -567,6 +562,17 @@ gst_v4l2_codec_vp8_dec_end_picture (GstVp8Decoder * decoder,
frame->output_buffer = buffer; frame->output_buffer = buffer;
gst_video_codec_frame_unref (frame); gst_video_codec_frame_unref (frame);
request = gst_v4l2_decoder_alloc_request (self->decoder,
picture->system_frame_number, self->bitstream, buffer);
if (!request) {
GST_ELEMENT_ERROR (decoder, RESOURCE, NO_SPACE_LEFT,
("Failed to allocate a media request object."), (NULL));
goto fail;
}
gst_vp8_picture_set_user_data (picture, request,
(GDestroyNotify) gst_v4l2_request_free);
if (!gst_v4l2_decoder_set_controls (self->decoder, request, control, if (!gst_v4l2_decoder_set_controls (self->decoder, request, control,
G_N_ELEMENTS (control))) { G_N_ELEMENTS (control))) {
GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE, GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
@ -574,26 +580,7 @@ gst_v4l2_codec_vp8_dec_end_picture (GstVp8Decoder * decoder,
goto fail; goto fail;
} }
bytesused = self->bitstream_map.size; if (!gst_v4l2_request_queue (request, 0)) {
gst_memory_unmap (self->bitstream, &self->bitstream_map);
self->bitstream_map = (GstMapInfo) GST_MAP_INFO_INIT;
if (!gst_v4l2_decoder_queue_sink_mem (self->decoder, request, self->bitstream,
picture->system_frame_number, bytesused, 0)) {
GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
("Driver did not accept the bitstream data."), (NULL));
goto fail;
}
if (!gst_v4l2_decoder_queue_src_buffer (self->decoder, buffer,
picture->system_frame_number)) {
GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
("Driver did not accept the picture buffer."), (NULL));
goto fail;
}
if (!gst_v4l2_request_queue (request)) {
GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE, GST_ELEMENT_ERROR (decoder, RESOURCE, WRITE,
("Driver did not accept the decode request."), (NULL)); ("Driver did not accept the decode request."), (NULL));
goto fail; goto fail;
@ -663,7 +650,6 @@ gst_v4l2_codec_vp8_dec_output_picture (GstVp8Decoder * decoder,
GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder); GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
GstV4l2Request *request = gst_vp8_picture_get_user_data (picture); GstV4l2Request *request = gst_vp8_picture_get_user_data (picture);
gint ret; gint ret;
guint32 frame_num;
GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number); GST_DEBUG_OBJECT (self, "Output picture %u", picture->system_frame_number);
@ -682,18 +668,16 @@ gst_v4l2_codec_vp8_dec_output_picture (GstVp8Decoder * decoder,
goto error; goto error;
} }
do {
if (!gst_v4l2_decoder_dequeue_src (self->decoder, &frame_num)) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Decoder did not produce a frame"), (NULL));
goto error;
}
} while (frame_num != picture->system_frame_number);
finish_frame:
gst_v4l2_request_set_done (request); gst_v4l2_request_set_done (request);
g_return_val_if_fail (frame->output_buffer, GST_FLOW_ERROR); g_return_val_if_fail (frame->output_buffer, GST_FLOW_ERROR);
finish_frame:
if (gst_v4l2_request_failed (request)) {
GST_ELEMENT_ERROR (self, STREAM, DECODE,
("Failed to decode frame %u", picture->system_frame_number), (NULL));
goto error;
}
/* Hold on reference buffers for the rest of the picture lifetime */ /* Hold on reference buffers for the rest of the picture lifetime */
gst_vp8_picture_set_user_data (picture, gst_vp8_picture_set_user_data (picture,
gst_buffer_ref (frame->output_buffer), (GDestroyNotify) gst_buffer_unref); gst_buffer_ref (frame->output_buffer), (GDestroyNotify) gst_buffer_unref);

View File

@ -49,10 +49,17 @@ struct _GstV4l2Request
{ {
GstV4l2Decoder *decoder; GstV4l2Decoder *decoder;
gint fd; gint fd;
guint32 frame_num;
GstMemory *bitstream; GstMemory *bitstream;
GstBuffer *pic_buf;
GstPoll *poll; GstPoll *poll;
GstPollFD pollfd; GstPollFD pollfd;
/* request state */
gboolean pending; gboolean pending;
gboolean failed;
gboolean hold_pic_buf;
gboolean sub_request;
}; };
struct _GstV4l2Decoder struct _GstV4l2Decoder
@ -225,11 +232,12 @@ gst_v4l2_decoder_streamon (GstV4l2Decoder * self, GstPadDirection direction)
gboolean gboolean
gst_v4l2_decoder_streamoff (GstV4l2Decoder * self, GstPadDirection direction) gst_v4l2_decoder_streamoff (GstV4l2Decoder * self, GstPadDirection direction)
{ {
GstV4l2Request *pending_req;
guint32 type = direction_to_buffer_type (self, direction); guint32 type = direction_to_buffer_type (self, direction);
gint ret; gint ret;
if (direction == GST_PAD_SRC) { if (direction == GST_PAD_SRC) {
GstV4l2Request *pending_req;
/* STREAMOFF have the effect of cancelling all requests and unqueuing all /* STREAMOFF have the effect of cancelling all requests and unqueuing all
* buffers, so clear the pending request list */ * buffers, so clear the pending request list */
while ((pending_req = gst_queue_array_pop_head (self->pending_requests))) { while ((pending_req = gst_queue_array_pop_head (self->pending_requests))) {
@ -530,12 +538,13 @@ gst_v4l2_decoder_export_buffer (GstV4l2Decoder * self,
return TRUE; return TRUE;
} }
gboolean static gboolean
gst_v4l2_decoder_queue_sink_mem (GstV4l2Decoder * self, gst_v4l2_decoder_queue_sink_mem (GstV4l2Decoder * self,
GstV4l2Request * request, GstMemory * mem, guint32 frame_num, GstV4l2Request * request, GstMemory * mem, guint32 frame_num,
gsize bytesused, guint flags) guint flags)
{ {
gint ret; gint ret;
gsize bytesused = gst_memory_get_sizes (mem, NULL, NULL);
struct v4l2_plane plane = { struct v4l2_plane plane = {
.bytesused = bytesused, .bytesused = bytesused,
}; };
@ -563,14 +572,11 @@ gst_v4l2_decoder_queue_sink_mem (GstV4l2Decoder * self,
return FALSE; return FALSE;
} }
request->bitstream = gst_memory_ref (mem);
return TRUE; return TRUE;
} }
gboolean static gboolean
gst_v4l2_decoder_queue_src_buffer (GstV4l2Decoder * self, GstBuffer * buffer, gst_v4l2_decoder_queue_src_buffer (GstV4l2Decoder * self, GstBuffer * buffer)
guint32 frame_num)
{ {
gint i, ret; gint i, ret;
struct v4l2_plane planes[GST_VIDEO_MAX_PLANES]; struct v4l2_plane planes[GST_VIDEO_MAX_PLANES];
@ -606,7 +612,7 @@ gst_v4l2_decoder_queue_src_buffer (GstV4l2Decoder * self, GstBuffer * buffer,
return TRUE; return TRUE;
} }
gboolean static gboolean
gst_v4l2_decoder_dequeue_sink (GstV4l2Decoder * self) gst_v4l2_decoder_dequeue_sink (GstV4l2Decoder * self)
{ {
gint ret; gint ret;
@ -632,7 +638,7 @@ gst_v4l2_decoder_dequeue_sink (GstV4l2Decoder * self)
return TRUE; return TRUE;
} }
gboolean static gboolean
gst_v4l2_decoder_dequeue_src (GstV4l2Decoder * self, guint32 * out_frame_num) gst_v4l2_decoder_dequeue_src (GstV4l2Decoder * self, guint32 * out_frame_num)
{ {
gint ret; gint ret;
@ -806,8 +812,23 @@ gst_v4l2_decoder_register (GstPlugin * plugin,
g_free (type_name); g_free (type_name);
} }
/*
* gst_v4l2_decoder_alloc_request:
* @self a #GstV4l2Decoder pointer
* @frame_num: Used as a timestamp to identify references
* @bitstream the #GstMemory that holds the bitstream data
* @pic_buf the #GstBuffer holding the decoded picture
*
* Allocate a Linux media request file descriptor. This request wrapper will
* hold a reference to the requested bitstream memory to decoded and the
* picture buffer this request will decode to. This will be used for
* transparent management of the V4L2 queues.
*
* Returns: a new #GstV4l2Request
*/
GstV4l2Request * GstV4l2Request *
gst_v4l2_decoder_alloc_request (GstV4l2Decoder * self) gst_v4l2_decoder_alloc_request (GstV4l2Decoder * self, guint32 frame_num,
GstMemory * bitstream, GstBuffer * pic_buf)
{ {
GstV4l2Request *request = gst_queue_array_pop_head (self->request_pool); GstV4l2Request *request = gst_queue_array_pop_head (self->request_pool);
gint ret; gint ret;
@ -830,9 +851,60 @@ gst_v4l2_decoder_alloc_request (GstV4l2Decoder * self)
} }
request->decoder = g_object_ref (self); request->decoder = g_object_ref (self);
request->bitstream = gst_memory_ref (bitstream);
request->pic_buf = gst_buffer_ref (pic_buf);
request->frame_num = frame_num;
return request; return request;
} }
/*
* gst_v4l2_decoder_alloc_sub_request:
* @self a #GstV4l2Decoder pointer
* @prev_request the #GstV4l2Request this request continue
* @bitstream the #GstMemory that holds the bitstream data
*
* Allocate a Linux media request file descriptor. Similar to
* gst_v4l2_decoder_alloc_request(), but used when a request is the
* continuation of the decoding of the same picture. This is notably the case
* for subsequent slices or for second field of a frame.
*
* Returns: a new #GstV4l2Request
*/
GstV4l2Request *
gst_v4l2_decoder_alloc_sub_request (GstV4l2Decoder * self,
GstV4l2Request * prev_request, GstMemory * bitstream)
{
GstV4l2Request *request = gst_queue_array_pop_head (self->request_pool);
gint ret;
if (!request) {
request = g_new0 (GstV4l2Request, 1);
ret = ioctl (self->media_fd, MEDIA_IOC_REQUEST_ALLOC, &request->fd);
if (ret < 0) {
GST_ERROR_OBJECT (self, "MEDIA_IOC_REQUEST_ALLOC failed: %s",
g_strerror (errno));
return NULL;
}
request->poll = gst_poll_new (FALSE);
gst_poll_fd_init (&request->pollfd);
request->pollfd.fd = request->fd;
gst_poll_add_fd (request->poll, &request->pollfd);
gst_poll_fd_ctl_pri (request->poll, &request->pollfd, TRUE);
}
request->decoder = g_object_ref (self);
request->bitstream = gst_memory_ref (bitstream);
request->pic_buf = gst_buffer_ref (prev_request->pic_buf);
request->frame_num = prev_request->frame_num;
request->sub_request = TRUE;
return request;
}
void void
gst_v4l2_request_free (GstV4l2Request * request) gst_v4l2_request_free (GstV4l2Request * request)
{ {
@ -847,6 +919,11 @@ gst_v4l2_request_free (GstV4l2Request * request)
} }
g_clear_pointer (&request->bitstream, gst_memory_unref); g_clear_pointer (&request->bitstream, gst_memory_unref);
g_clear_pointer (&request->pic_buf, gst_buffer_unref);
request->frame_num = G_MAXUINT32;
request->failed = FALSE;
request->hold_pic_buf = FALSE;
request->sub_request = FALSE;
request->decoder = NULL; request->decoder = NULL;
if (request->pending) { if (request->pending) {
@ -879,21 +956,48 @@ gst_v4l2_request_free (GstV4l2Request * request)
} }
gboolean gboolean
gst_v4l2_request_queue (GstV4l2Request * request) gst_v4l2_request_queue (GstV4l2Request * request, guint flags)
{ {
GstV4l2Decoder *decoder = request->decoder;
gint ret; gint ret;
GST_TRACE_OBJECT (request->decoder, "Queuing request %p.", request); GST_TRACE_OBJECT (decoder, "Queuing request %p.", request);
if (!gst_v4l2_decoder_queue_sink_mem (decoder, request,
request->bitstream, request->frame_num, flags)) {
GST_ERROR_OBJECT (decoder, "Driver did not accept the bitstream data.");
return FALSE;
}
if (!request->sub_request &&
!gst_v4l2_decoder_queue_src_buffer (decoder, request->pic_buf)) {
GST_ERROR_OBJECT (decoder, "Driver did not accept the picture buffer.");
return FALSE;
}
ret = ioctl (request->fd, MEDIA_REQUEST_IOC_QUEUE, NULL); ret = ioctl (request->fd, MEDIA_REQUEST_IOC_QUEUE, NULL);
if (ret < 0) { if (ret < 0) {
GST_ERROR_OBJECT (request->decoder, "MEDIA_REQUEST_IOC_QUEUE, failed: %s", GST_ERROR_OBJECT (decoder, "MEDIA_REQUEST_IOC_QUEUE, failed: %s",
g_strerror (errno)); g_strerror (errno));
return FALSE; return FALSE;
} }
if (flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF)
request->hold_pic_buf = TRUE;
request->pending = TRUE; request->pending = TRUE;
gst_queue_array_push_tail (request->decoder->pending_requests, request); gst_queue_array_push_tail (decoder->pending_requests, request);
/* FIXME to support more then one pending requests, we need the request to
* be refcounted */
if (gst_queue_array_get_length (decoder->pending_requests) > 1) {
GstV4l2Request *pending_req;
pending_req = gst_queue_array_peek_head (decoder->pending_requests);
ret = gst_v4l2_request_poll (pending_req, GST_SECOND);
if (ret > 0)
gst_v4l2_request_set_done (pending_req);
}
return TRUE; return TRUE;
} }
@ -907,28 +1011,38 @@ gst_v4l2_request_poll (GstV4l2Request * request, GstClockTime timeout)
void void
gst_v4l2_request_set_done (GstV4l2Request * request) gst_v4l2_request_set_done (GstV4l2Request * request)
{ {
if (request->bitstream) { GstV4l2Decoder *decoder = request->decoder;
GstV4l2Decoder *dec = request->decoder; GstV4l2Request *pending_req = NULL;
GstV4l2Request *pending_req;
while ((pending_req = gst_queue_array_pop_head (dec->pending_requests))) { if (!request->pending)
gst_v4l2_decoder_dequeue_sink (request->decoder); return;
g_clear_pointer (&pending_req->bitstream, gst_memory_unref);
pending_req->pending = FALSE;
if (pending_req == request) while ((pending_req = gst_queue_array_pop_head (decoder->pending_requests))) {
break; gst_v4l2_decoder_dequeue_sink (decoder);
g_clear_pointer (&pending_req->bitstream, gst_memory_unref);
if (!pending_req->hold_pic_buf) {
guint32 frame_num = G_MAXUINT32;
if (!gst_v4l2_decoder_dequeue_src (decoder, &frame_num)) {
pending_req->failed = TRUE;
} else if (frame_num != pending_req->frame_num) {
GST_WARNING_OBJECT (decoder,
"Requested frame %u, but driver returned frame %u.",
pending_req->frame_num, frame_num);
pending_req->failed = TRUE;
}
} }
/* Pending request should always be found in the fifo */ g_clear_pointer (&pending_req->pic_buf, gst_buffer_unref);
if (pending_req != request) { pending_req->pending = FALSE;
g_warning ("Pending request not found in the pending list.");
gst_v4l2_decoder_dequeue_sink (request->decoder); if (pending_req == request)
g_clear_pointer (&pending_req->bitstream, gst_memory_unref); break;
}
} }
request->pending = FALSE; /* Pending request must be in the pending request list */
g_assert (pending_req == request);
} }
gboolean gboolean
@ -936,3 +1050,9 @@ gst_v4l2_request_is_done (GstV4l2Request * request)
{ {
return !request->pending; return !request->pending;
} }
gboolean
gst_v4l2_request_failed (GstV4l2Request * request)
{
return request->failed;
}

View File

@ -72,22 +72,6 @@ gboolean gst_v4l2_decoder_export_buffer (GstV4l2Decoder * self,
gsize * offsets, gsize * offsets,
guint *num_fds); guint *num_fds);
gboolean gst_v4l2_decoder_queue_sink_mem (GstV4l2Decoder * self,
GstV4l2Request * request,
GstMemory * mem,
guint32 frame_num,
gsize bytesused,
guint flags);
gboolean gst_v4l2_decoder_dequeue_sink (GstV4l2Decoder * self);
gboolean gst_v4l2_decoder_queue_src_buffer (GstV4l2Decoder * self,
GstBuffer * buffer,
guint32 frame_num);
gboolean gst_v4l2_decoder_dequeue_src (GstV4l2Decoder * self,
guint32 *out_frame_num);
gboolean gst_v4l2_decoder_set_controls (GstV4l2Decoder * self, gboolean gst_v4l2_decoder_set_controls (GstV4l2Decoder * self,
GstV4l2Request * request, GstV4l2Request * request,
struct v4l2_ext_control *control, struct v4l2_ext_control *control,
@ -115,18 +99,29 @@ void gst_v4l2_decoder_register (GstPlugin * plugin,
GstV4l2CodecDevice * device, GstV4l2CodecDevice * device,
guint rank); guint rank);
GstV4l2Request *gst_v4l2_decoder_alloc_request (GstV4l2Decoder * self); GstV4l2Request *gst_v4l2_decoder_alloc_request (GstV4l2Decoder * self,
guint32 frame_num,
GstMemory *bitstream,
GstBuffer * pic_buf);
GstV4l2Request *gst_v4l2_decoder_alloc_sub_request (GstV4l2Decoder * self,
GstV4l2Request * prev_request,
GstMemory *bitstream);
void gst_v4l2_request_free (GstV4l2Request * request); void gst_v4l2_request_free (GstV4l2Request * request);
gboolean gst_v4l2_request_queue (GstV4l2Request * request); gboolean gst_v4l2_request_queue (GstV4l2Request * request,
guint flags);
gint gst_v4l2_request_poll (GstV4l2Request * request, GstClockTime timeout); gint gst_v4l2_request_poll (GstV4l2Request * request,
GstClockTime timeout);
void gst_v4l2_request_set_done (GstV4l2Request * request); void gst_v4l2_request_set_done (GstV4l2Request * request);
gboolean gst_v4l2_request_is_done (GstV4l2Request * request); gboolean gst_v4l2_request_is_done (GstV4l2Request * request);
gboolean gst_v4l2_request_failed (GstV4l2Request * request);
G_END_DECLS G_END_DECLS
#endif /* __GST_V4L2_DECODER_H__ */ #endif /* __GST_V4L2_DECODER_H__ */