gstffmpegdec: Handle durations in reordered frames

The buffer durations were not being reordered along with the timestamp
and offset of the buffers, resulting in buffers using the duration of the
latest incoming frame instead of their original frame.

Fixes #611398
This commit is contained in:
Edward Hervey 2010-02-28 15:10:34 +01:00
parent c9a7e76f46
commit bb2acca229

View file

@ -48,6 +48,7 @@ typedef struct _GstDataPassThrough GstDataPassThrough;
struct _GstDataPassThrough struct _GstDataPassThrough
{ {
guint64 ts; guint64 ts;
guint64 duration;
guint64 offset; guint64 offset;
}; };
@ -58,6 +59,9 @@ struct _GstTSMap
/* timestamp */ /* timestamp */
guint64 ts; guint64 ts;
/* duration */
guint64 duration;
/* offset */ /* offset */
gint64 offset; gint64 offset;
@ -236,7 +240,7 @@ static void gst_ts_handler_append (GstFFMpegDec * ffmpegdec,
GstBuffer * buffer); GstBuffer * buffer);
static void gst_ts_handler_consume (GstFFMpegDec * ffmpegdec, gint size); static void gst_ts_handler_consume (GstFFMpegDec * ffmpegdec, gint size);
static guint64 gst_ts_handler_get_ts (GstFFMpegDec * ffmpegdec, static guint64 gst_ts_handler_get_ts (GstFFMpegDec * ffmpegdec,
gint64 * offset); gint64 * offset, guint64 * duration);
#define GST_FFDEC_PARAMS_QDATA g_quark_from_static_string("ffdec-params") #define GST_FFDEC_PARAMS_QDATA g_quark_from_static_string("ffdec-params")
@ -1566,34 +1570,43 @@ flush_queued (GstFFMpegDec * ffmpegdec)
} }
static gpointer static gpointer
opaque_store (GstFFMpegDec * ffmpegdec, guint64 ts, guint64 offset) opaque_store (GstFFMpegDec * ffmpegdec, guint64 ts, guint64 duration,
guint64 offset)
{ {
GstDataPassThrough *opaque = g_slice_new0 (GstDataPassThrough); GstDataPassThrough *opaque = g_slice_new0 (GstDataPassThrough);
opaque->ts = ts; opaque->ts = ts;
opaque->duration = duration;
opaque->offset = offset; opaque->offset = offset;
ffmpegdec->opaque = g_list_append (ffmpegdec->opaque, (gpointer) opaque); ffmpegdec->opaque = g_list_append (ffmpegdec->opaque, (gpointer) opaque);
GST_DEBUG_OBJECT (ffmpegdec, "Stored ts:%" GST_TIME_FORMAT ", offset:%" GST_DEBUG_OBJECT (ffmpegdec,
G_GUINT64_FORMAT " as opaque %p", GST_TIME_ARGS (ts), offset, "Stored ts:%" GST_TIME_FORMAT ", duration:%" GST_TIME_FORMAT ", offset:%"
(gpointer) opaque); G_GUINT64_FORMAT " as opaque %p", GST_TIME_ARGS (ts),
GST_TIME_ARGS (duration), offset, (gpointer) opaque);
return opaque; return opaque;
} }
static gboolean static gboolean
opaque_find (GstFFMpegDec * ffmpegdec, gpointer opaque_val, guint64 * _ts, opaque_find (GstFFMpegDec * ffmpegdec, gpointer opaque_val, guint64 * _ts,
gint64 * _offset) guint64 * _duration, gint64 * _offset)
{ {
GstClockTime ts = GST_CLOCK_TIME_NONE; GstClockTime ts = GST_CLOCK_TIME_NONE;
GstClockTime duration = GST_CLOCK_TIME_NONE;
gint64 offset = GST_BUFFER_OFFSET_NONE; gint64 offset = GST_BUFFER_OFFSET_NONE;
GList *i; GList *i;
for (i = ffmpegdec->opaque; i != NULL; i = g_list_next (i)) { for (i = ffmpegdec->opaque; i != NULL; i = g_list_next (i)) {
if (i->data == (gpointer) opaque_val) { if (i->data == (gpointer) opaque_val) {
ts = ((GstDataPassThrough *) i->data)->ts; ts = ((GstDataPassThrough *) i->data)->ts;
duration = ((GstDataPassThrough *) i->data)->duration;
offset = ((GstDataPassThrough *) i->data)->offset; offset = ((GstDataPassThrough *) i->data)->offset;
GST_DEBUG_OBJECT (ffmpegdec, GST_DEBUG_OBJECT (ffmpegdec,
"Found opaque %p - ts:%" GST_TIME_FORMAT ", offset:%" G_GINT64_FORMAT, "Found opaque %p - ts:%" GST_TIME_FORMAT ", duration:%"
i->data, GST_TIME_ARGS (ts), offset); GST_TIME_FORMAT ", offset:%" G_GINT64_FORMAT, i->data,
GST_TIME_ARGS (ts), GST_TIME_ARGS (duration), offset);
if (_ts) if (_ts)
*_ts = ts; *_ts = ts;
if (_duration)
*_duration = duration;
if (_offset) if (_offset)
*_offset = offset; *_offset = offset;
g_slice_free (GstDataPassThrough, i->data); g_slice_free (GstDataPassThrough, i->data);
@ -1673,14 +1686,15 @@ gst_ffmpegdec_video_frame (GstFFMpegDec * ffmpegdec,
"Going to store opaque values, current ts:%" GST_TIME_FORMAT ", offset: %" "Going to store opaque values, current ts:%" GST_TIME_FORMAT ", offset: %"
G_GINT64_FORMAT, GST_TIME_ARGS (in_timestamp), in_offset); G_GINT64_FORMAT, GST_TIME_ARGS (in_timestamp), in_offset);
out_timestamp = gst_ts_handler_get_ts (ffmpegdec, &out_offset); out_timestamp = gst_ts_handler_get_ts (ffmpegdec, &out_offset, &out_duration);
/* Never do this at home... /* Never do this at home...
* 1) We know that ffmpegdec->context->reordered_opaque is 64-bit, and thus * 1) We know that ffmpegdec->context->reordered_opaque is 64-bit, and thus
* is capable of holding virtually anything including pointers * is capable of holding virtually anything including pointers
* (unless we're on 128-bit platform...) * (unless we're on 128-bit platform...)
*/ */
ffmpegdec->context->reordered_opaque = (gint64) ffmpegdec->context->reordered_opaque = (gint64)
GPOINTER_TO_SIZE (opaque_store (ffmpegdec, out_timestamp, out_offset)); GPOINTER_TO_SIZE (opaque_store (ffmpegdec, out_timestamp, out_duration,
out_offset));
/* now decode the frame */ /* now decode the frame */
len = avcodec_decode_video (ffmpegdec->context, len = avcodec_decode_video (ffmpegdec->context,
@ -1715,10 +1729,11 @@ gst_ffmpegdec_video_frame (GstFFMpegDec * ffmpegdec,
/* recuperate the reordered timestamp */ /* recuperate the reordered timestamp */
if (!opaque_find (ffmpegdec, if (!opaque_find (ffmpegdec,
(gpointer) (gulong) ffmpegdec->picture->reordered_opaque, &out_pts, (gpointer) (gulong) ffmpegdec->picture->reordered_opaque, &out_pts,
&out_offset)) { &out_duration, &out_offset)) {
GST_DEBUG_OBJECT (ffmpegdec, "Failed to find opaque %p", GST_DEBUG_OBJECT (ffmpegdec, "Failed to find opaque %p",
(gpointer) (gulong) ffmpegdec->picture->reordered_opaque); (gpointer) (gulong) ffmpegdec->picture->reordered_opaque);
out_pts = -1; out_pts = -1;
out_duration = -1;
out_offset = GST_BUFFER_OFFSET_NONE; out_offset = GST_BUFFER_OFFSET_NONE;
} else { } else {
GST_DEBUG_OBJECT (ffmpegdec, GST_DEBUG_OBJECT (ffmpegdec,
@ -1726,7 +1741,9 @@ gst_ffmpegdec_video_frame (GstFFMpegDec * ffmpegdec,
G_GINT64_FORMAT, GST_TIME_ARGS (in_timestamp), in_offset); G_GINT64_FORMAT, GST_TIME_ARGS (in_timestamp), in_offset);
} }
GST_DEBUG_OBJECT (ffmpegdec, "ts-handler: pts %" G_GUINT64_FORMAT, out_pts); GST_DEBUG_OBJECT (ffmpegdec,
"ts-handler: pts %" G_GUINT64_FORMAT " duration %" G_GUINT64_FORMAT,
out_pts, out_duration);
GST_DEBUG_OBJECT (ffmpegdec, "picture: pts %" G_GUINT64_FORMAT, GST_DEBUG_OBJECT (ffmpegdec, "picture: pts %" G_GUINT64_FORMAT,
(guint64) ffmpegdec->picture->pts); (guint64) ffmpegdec->picture->pts);
GST_DEBUG_OBJECT (ffmpegdec, "picture: num %d", GST_DEBUG_OBJECT (ffmpegdec, "picture: num %d",
@ -1857,12 +1874,15 @@ gst_ffmpegdec_video_frame (GstFFMpegDec * ffmpegdec,
/* /*
* Duration: * Duration:
* *
* 1) Copy input duration if valid * 1) Use reordered input duration if valid
* 2) else use input framerate * 2) Else use input duration
* 3) else use ffmpeg framerate * 3) else use input framerate
* 4) else use ffmpeg framerate
*/ */
out_duration = -1; if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
if (!GST_CLOCK_TIME_IS_VALID (in_duration)) { /* We have a valid (reordered) duration */
GST_LOG_OBJECT (ffmpegdec, "We have a valid (reordered) duration");
} else if (!GST_CLOCK_TIME_IS_VALID (in_duration)) {
/* if we have an input framerate, use that */ /* if we have an input framerate, use that */
if (ffmpegdec->format.video.fps_n != -1 && if (ffmpegdec->format.video.fps_n != -1 &&
(ffmpegdec->format.video.fps_n != 1000 && (ffmpegdec->format.video.fps_n != 1000 &&
@ -2916,6 +2936,7 @@ gst_ts_handler_append (GstFFMpegDec * ffmpegdec, GstBuffer * buffer)
{ {
GstTSHandler *ts_handler = &ffmpegdec->ts_handler; GstTSHandler *ts_handler = &ffmpegdec->ts_handler;
guint64 ts = GST_BUFFER_TIMESTAMP (buffer); guint64 ts = GST_BUFFER_TIMESTAMP (buffer);
guint64 duration = GST_BUFFER_DURATION (buffer);
gint size = GST_BUFFER_SIZE (buffer); gint size = GST_BUFFER_SIZE (buffer);
guint64 offset = GST_BUFFER_OFFSET (buffer); guint64 offset = GST_BUFFER_OFFSET (buffer);
gint ind = ts_handler->buf_head; gint ind = ts_handler->buf_head;
@ -2929,9 +2950,11 @@ gst_ts_handler_append (GstFFMpegDec * ffmpegdec, GstBuffer * buffer)
ts != -1 ? (double) ts / GST_SECOND : -1.0, size); ts != -1 ? (double) ts / GST_SECOND : -1.0, size);
**/ **/
GST_LOG_OBJECT (ffmpegdec, "store timestamp @ index [%02X] buf_count: %d" GST_LOG_OBJECT (ffmpegdec, "store timestamp @ index [%02X] buf_count: %d"
" ts: %" GST_TIME_FORMAT ", offset: %" G_GUINT64_FORMAT ", size: %d", " ts: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT ", offset: %"
ind, ts_handler->buf_count, GST_TIME_ARGS (ts), offset, size); G_GUINT64_FORMAT ", size: %d", ind, ts_handler->buf_count,
GST_TIME_ARGS (ts), GST_TIME_ARGS (duration), offset, size);
ts_handler->buffers[ind].ts = ts; ts_handler->buffers[ind].ts = ts;
ts_handler->buffers[ind].duration = duration;
ts_handler->buffers[ind].offset = offset; ts_handler->buffers[ind].offset = offset;
ts_handler->buffers[ind].size = size; ts_handler->buffers[ind].size = size;
ts_handler->buf_head = ind; ts_handler->buf_head = ind;
@ -2989,17 +3012,24 @@ gst_ts_handler_consume (GstFFMpegDec * ffmpegdec, gint size)
/** get the timestamp from the tail of the list */ /** get the timestamp from the tail of the list */
static guint64 static guint64
gst_ts_handler_get_ts (GstFFMpegDec * ffmpegdec, gint64 * _offset) gst_ts_handler_get_ts (GstFFMpegDec * ffmpegdec, gint64 * _offset,
guint64 * _duration)
{ {
GstTSHandler *ts_handler = &ffmpegdec->ts_handler; GstTSHandler *ts_handler = &ffmpegdec->ts_handler;
guint64 ts = ts_handler->buffers[ts_handler->buf_tail].ts; guint64 ts = ts_handler->buffers[ts_handler->buf_tail].ts;
guint64 duration = ts_handler->buffers[ts_handler->buf_tail].duration;
gint64 offset = ts_handler->buffers[ts_handler->buf_tail].offset; gint64 offset = ts_handler->buffers[ts_handler->buf_tail].offset;
GST_LOG_OBJECT (ffmpegdec, "Index %d yielded ts %" GST_TIME_FORMAT
" offset %" G_GINT64_FORMAT, ts_handler->buf_tail, GST_LOG_OBJECT (ffmpegdec, "Index %d yielded ts: %" GST_TIME_FORMAT
GST_TIME_ARGS (ts), offset); ", duration: %" GST_TIME_FORMAT ", offset: %" G_GINT64_FORMAT,
ts_handler->buf_tail, GST_TIME_ARGS (ts), GST_TIME_ARGS (duration),
offset);
if (_offset) if (_offset)
*_offset = offset; *_offset = offset;
if (_duration)
*_duration = duration;
ts_handler->buffers[ts_handler->buf_tail].ts = -1; ts_handler->buffers[ts_handler->buf_tail].ts = -1;
ts_handler->buffers[ts_handler->buf_tail].duration = -1;
ts_handler->buffers[ts_handler->buf_tail].offset = -1; ts_handler->buffers[ts_handler->buf_tail].offset = -1;
return ts; return ts;
} }