v4l2h264dec0: Too old frames, bug in decoder -- please file a bug
An example that triggers this bug is attached below. This 1.21.0.1 master. The bug is there in V4L2 in 1.20 and 1.18; no problems with other decoders.
This videostream starts with an SPS NAL followed by a PPS NAL followed by 125 video NALS of Big Buck Bunny (from youtube)
Here is a dump (videodump.1.h264) of 5 secs of a stream of Big Buck Bunny (15Mb, streamed over Apple Airplay to the gstreamer pipeline (in the shell script pipeline.sh)
#!/bin/sh
gst-launch-1.0 filesrc location=$1 ! video/x-h264 ! h264parse ! capssetter caps="video/x-h264, colorimetry=bt709" ! v4l2h264dec ! v4l2convert ! glimagesink name=video_sink sync=false
At line 644 of gstv4l2videodec.c
static void
gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
{
GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
GstV4l2BufferPool *v4l2_pool;
GstBufferPool *pool;
GstVideoCodecFrame *frame;
GstBuffer *buffer = NULL;
GstFlowReturn ret;
GST_VIDEO_DECODER_STREAM_LOCK (decoder);
if (g_atomic_int_get (&self->capture_configuration_change)) {
gst_v4l2_object_stop (self->v4l2capture);
ret = gst_v4l2_video_dec_setup_capture (decoder);
if (ret != GST_FLOW_OK) {
GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
return;
}
g_atomic_int_set (&self->capture_configuration_change, FALSE);
}
GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture)))
return;
GST_LOG_OBJECT (decoder, "Allocate output buffer");
v4l2_pool = GST_V4L2_BUFFER_POOL (self->v4l2capture->pool);
self->output_flow = GST_FLOW_OK;
<================================================only frames 4,5,6..... make it past here.
do {
/* We cannot use the base class allotate helper since it taking the internal
* stream lock. we know that the acquire may need to poll until more frames
* comes in and holding this lock would prevent that.
*/
pool = gst_video_decoder_get_buffer_pool (decoder);
/* Pool may be NULL if we started going to READY state */
if (pool == NULL) {
ret = GST_FLOW_FLUSHING;
goto beach;
}
ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
g_object_unref (pool);
if (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE) {
GST_WARNING_OBJECT (decoder, "Received resolution change");
g_atomic_int_set (&self->capture_configuration_change, TRUE);
return;
}
if (ret != GST_FLOW_OK)
goto beach;
GST_LOG_OBJECT (decoder, "Process output buffer");
ret = gst_v4l2_buffer_pool_process (v4l2_pool, &buffer, NULL);
} while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER);
if (ret != GST_FLOW_OK)
goto beach;
if (GST_BUFFER_TIMESTAMP (buffer) % GST_SECOND != 0)
GST_ERROR_OBJECT (decoder,
"Driver bug detected - check driver with v4l2-compliance from http://git.linuxtv.org/v4l-utils.git");
GST_LOG_OBJECT (decoder, "Got buffer for frame number %u",
(guint32) (GST_BUFFER_TIMESTAMP (buffer) / GST_SECOND));
frame =
gst_video_decoder_get_frame (decoder,
GST_BUFFER_TIMESTAMP (buffer) / GST_SECOND);
if (frame) {
GstVideoCodecFrame *oldest_frame;
gboolean warned = FALSE;
/* Garbage collect old frames in case of codec bugs */
while ((oldest_frame = gst_video_decoder_get_oldest_frame (decoder)) &&
check_system_frame_number_too_old (frame->system_frame_number,
oldest_frame->system_frame_number)) {
gst_video_decoder_drop_frame (decoder, oldest_frame);
oldest_frame = NULL;
if (!warned) {
g_warning ("%s: Too old frames, bug in decoder -- please file a bug",
GST_ELEMENT_NAME (decoder));
warned = TRUE;
}
}
The first few NAL units (typically 4) dont get handled by the decoder loop and are left for garbage collection after 100 frames. One single return happens before the loop starts working.