ext/ffmpeg/gstffmpegdec.c: Mark outgoing buffers with DISCONT when needed.

Original commit message from CVS:
* ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_open),
(gst_ffmpegdec_negotiate), (clip_video_buffer),
(clip_audio_buffer), (gst_ffmpegdec_audio_frame),
(gst_ffmpegdec_frame), (gst_ffmpegdec_sink_event),
(gst_ffmpegdec_chain):
Mark outgoing buffers with DISCONT when needed.
Small cleanups.
Fix timestamps on audio decoders.
better audio clipping.
Fixes #352579.
This commit is contained in:
Wim Taymans 2006-08-29 09:28:20 +00:00
parent d3f7b8582b
commit bb45e68275
3 changed files with 166 additions and 71 deletions

View file

@ -1,3 +1,16 @@
2006-08-29 Wim Taymans <wim@fluendo.com>
* ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_open),
(gst_ffmpegdec_negotiate), (clip_video_buffer),
(clip_audio_buffer), (gst_ffmpegdec_audio_frame),
(gst_ffmpegdec_frame), (gst_ffmpegdec_sink_event),
(gst_ffmpegdec_chain):
Mark outgoing buffers with DISCONT when needed.
Small cleanups.
Fix timestamps on audio decoders.
better audio clipping.
Fixes #352579.
2006-08-21 Wim Taymans <wim@fluendo.com> 2006-08-21 Wim Taymans <wim@fluendo.com>
* ext/ffmpeg/gstffmpegcodecmap.c: (gst_ffmpeg_caps_with_codecid): * ext/ffmpeg/gstffmpegcodecmap.c: (gst_ffmpeg_caps_with_codecid):

2
common

@ -1 +1 @@
Subproject commit e9ea99f6e89d7e1af3a0a859bfeb0ed6ecf2e3a9 Subproject commit d287125f93da692bc25d53b0b7b0e2f90424a212

View file

@ -63,10 +63,12 @@ struct _GstFFMpegDec
} video; } video;
struct struct
{ {
gint channels, samplerate; gint channels;
gint samplerate;
} audio; } audio;
} format; } format;
gboolean waiting_for_key; gboolean waiting_for_key;
gboolean discont;
guint64 next_ts; guint64 next_ts;
/* parsing */ /* parsing */
@ -503,7 +505,7 @@ gst_ffmpegdec_open (GstFFMpegDec * ffmpegdec)
default: default:
break; break;
} }
ffmpegdec->next_ts = 0; ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
ffmpegdec->last_buffer = NULL; ffmpegdec->last_buffer = NULL;
/* FIXME, reset_qos holds the LOCK */ /* FIXME, reset_qos holds the LOCK */
ffmpegdec->proportion = 0.0; ffmpegdec->proportion = 0.0;
@ -847,6 +849,8 @@ gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
if (caps == NULL) if (caps == NULL)
goto no_caps; goto no_caps;
switch (oclass->in_plugin->type) {
case CODEC_TYPE_VIDEO:
/* If a demuxer provided a framerate then use it (#313970) */ /* If a demuxer provided a framerate then use it (#313970) */
if (ffmpegdec->format.video.fps_n != -1) { if (ffmpegdec->format.video.fps_n != -1) {
gst_caps_set_simple (caps, "framerate", gst_caps_set_simple (caps, "framerate",
@ -855,6 +859,14 @@ gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
} }
gst_ffmpegdec_add_pixel_aspect_ratio (ffmpegdec, gst_ffmpegdec_add_pixel_aspect_ratio (ffmpegdec,
gst_caps_get_structure (caps, 0)); gst_caps_get_structure (caps, 0));
break;
case CODEC_TYPE_AUDIO:
{
break;
}
default:
break;
}
if (!gst_pad_set_caps (ffmpegdec->srcpad, caps)) if (!gst_pad_set_caps (ffmpegdec->srcpad, caps))
goto caps_failed; goto caps_failed;
@ -1002,7 +1014,7 @@ clip_video_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts,
GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur)); GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));
/* can't clip without TIME segment */ /* can't clip without TIME segment */
if (dec->segment.format != GST_FORMAT_TIME) if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME))
goto beach; goto beach;
/* we need a start time */ /* we need a start time */
@ -1014,8 +1026,10 @@ clip_video_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts,
GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE; GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;
/* now clip */ /* now clip */
if (G_UNLIKELY (!(res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, res =
in_ts, stop, &cstart, &cstop)))) gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts, stop, &cstart,
&cstop);
if (G_UNLIKELY (!res))
goto beach; goto beach;
/* update timestamp and possibly duration if the clipped stop time is /* update timestamp and possibly duration if the clipped stop time is
@ -1309,6 +1323,75 @@ clipped:
} }
} }
/* returns TRUE if buffer is within segment, else FALSE.
* if Buffer is on segment border, it's timestamp and duration will be clipped */
static gboolean
clip_audio_buffer (GstFFMpegDec * dec, GstBuffer * buf, GstClockTime in_ts,
GstClockTime in_dur)
{
GstClockTime stop;
gint64 diff, ctime, cstop;
gboolean res = TRUE;
GST_LOG_OBJECT (dec,
"timestamp:%" GST_TIME_FORMAT ", duration:%" GST_TIME_FORMAT
", size %u", GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur),
GST_BUFFER_SIZE (buf));
/* can't clip without TIME segment */
if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME))
goto beach;
/* we need a start time */
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts)))
goto beach;
/* trust duration */
stop = in_ts + in_dur;
res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts, stop, &ctime,
&cstop);
if (G_UNLIKELY (!res))
goto out_of_segment;
/* see if some clipping happened */
if (G_UNLIKELY ((diff = ctime - in_ts) > 0)) {
/* bring clipped time to bytes */
diff =
gst_util_uint64_scale_int (diff, dec->format.audio.samplerate,
GST_SECOND) * (2 * dec->format.audio.channels);
GST_DEBUG_OBJECT (dec, "clipping start to %" GST_TIME_FORMAT " %"
G_GINT64_FORMAT " bytes", GST_TIME_ARGS (ctime), diff);
GST_BUFFER_SIZE (buf) -= diff;
GST_BUFFER_DATA (buf) += diff;
}
if (G_UNLIKELY ((diff = stop - cstop) > 0)) {
/* bring clipped time to bytes */
diff =
gst_util_uint64_scale_int (diff, dec->format.audio.samplerate,
GST_SECOND) * (2 * dec->format.audio.channels);
GST_DEBUG_OBJECT (dec, "clipping stop to %" GST_TIME_FORMAT " %"
G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff);
GST_BUFFER_SIZE (buf) -= diff;
}
GST_BUFFER_TIMESTAMP (buf) = ctime;
GST_BUFFER_DURATION (buf) = cstop - ctime;
beach:
GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
return res;
/* ERRORS */
out_of_segment:
{
GST_LOG_OBJECT (dec, "out of segment");
goto beach;
}
}
static gint static gint
gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec, gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec,
@ -1317,10 +1400,13 @@ gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec,
{ {
gint len = -1; gint len = -1;
gint have_data; gint have_data;
GstClockTime in_ts, in_dur;
in_ts = GST_BUFFER_TIMESTAMP (inbuf);
GST_DEBUG_OBJECT (ffmpegdec, GST_DEBUG_OBJECT (ffmpegdec,
"size:%d, inbuf.ts %" GST_TIME_FORMAT " , ffmpegdec->next_ts:%" "size:%d, inbuf.ts %" GST_TIME_FORMAT " , ffmpegdec->next_ts:%"
GST_TIME_FORMAT, size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)), GST_TIME_FORMAT, size, GST_TIME_ARGS (in_ts),
GST_TIME_ARGS (ffmpegdec->next_ts)); GST_TIME_ARGS (ffmpegdec->next_ts));
/* outgoing buffer */ /* outgoing buffer */
@ -1345,62 +1431,42 @@ gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec,
goto beach; goto beach;
} }
/* Buffer size timestamp and duration */ /* Buffer size */
GST_BUFFER_SIZE (*outbuf) = have_data; GST_BUFFER_SIZE (*outbuf) = have_data;
GST_BUFFER_TIMESTAMP (*outbuf) = ffmpegdec->next_ts;
GST_BUFFER_DURATION (*outbuf) = (have_data * GST_SECOND) / /*
(2 * ffmpegdec->context->channels * ffmpegdec->context->sample_rate); * Timestamps:
*
* 1) Copy input timestamp if valid
* 2) else interpolate from previous input timestamp
*/
/* always take timestamps from the input buffer if any */
if (!GST_CLOCK_TIME_IS_VALID (in_ts)) {
in_ts = ffmpegdec->next_ts;
}
/*
* Duration:
*
* 1) calculate based on number of samples
*/
in_dur = gst_util_uint64_scale_int (have_data, GST_SECOND,
2 * ffmpegdec->context->channels * ffmpegdec->context->sample_rate);
GST_DEBUG_OBJECT (ffmpegdec, GST_DEBUG_OBJECT (ffmpegdec,
"Buffer created. Size:%d , timestamp:%" GST_TIME_FORMAT " , duration:%" "Buffer created. Size:%d , timestamp:%" GST_TIME_FORMAT " , duration:%"
GST_TIME_FORMAT, GST_BUFFER_SIZE (*outbuf), GST_TIME_FORMAT, have_data,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*outbuf)), GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));
GST_TIME_ARGS (GST_BUFFER_DURATION (*outbuf)));
ffmpegdec->next_ts = GST_BUFFER_TIMESTAMP (*outbuf) = in_ts;
GST_BUFFER_TIMESTAMP (*outbuf) + GST_BUFFER_DURATION (*outbuf); GST_BUFFER_DURATION (*outbuf) = in_dur;
/* clipping/dropping of outgoing audio buffers */ /* the next timestamp we'll use when interpolating */
if (ffmpegdec->segment.format == GST_FORMAT_TIME) { ffmpegdec->next_ts = in_ts + in_dur;
gint64 start, stop, cstart, cstop, diff;
start = GST_BUFFER_TIMESTAMP (*outbuf); /* now see if we need to clip the buffer against the segment boundaries. */
stop = ffmpegdec->next_ts; if (G_UNLIKELY (!clip_audio_buffer (ffmpegdec, *outbuf, in_ts, in_dur)))
goto clipped;
if (!gst_segment_clip (&ffmpegdec->segment, GST_FORMAT_TIME, start, stop,
&cstart, &cstop)) {
GST_DEBUG_OBJECT (ffmpegdec, "Dropping buffer outside segment");
gst_buffer_unref (*outbuf);
*outbuf = NULL;
} else {
diff = cstart - start;
if (diff > 0) {
GST_BUFFER_TIMESTAMP (*outbuf) = cstart;
GST_BUFFER_DURATION (*outbuf) -= diff;
/* time -> bytes *//* FIXME : should go to samples first */
diff =
gst_util_uint64_scale (diff,
(2 * ffmpegdec->context->channels *
ffmpegdec->context->sample_rate), GST_SECOND);
GST_BUFFER_DATA (*outbuf) += diff;
GST_BUFFER_SIZE (*outbuf) -= diff;
}
diff = stop - cstop;
if (diff > 0) {
GST_BUFFER_DURATION (*outbuf) -= diff;
diff =
gst_util_uint64_scale (diff,
(2 * ffmpegdec->context->channels *
ffmpegdec->context->sample_rate), GST_SECOND);
GST_BUFFER_SIZE (*outbuf) -= diff;
}
GST_DEBUG_OBJECT (ffmpegdec,
"clipped buffer to %" GST_TIME_FORMAT " / duration:%"
GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (*outbuf)));
}
}
} else if (len > 0 && have_data == 0) { } else if (len > 0 && have_data == 0) {
/* cache output, because it may be used for caching (in-place) */ /* cache output, because it may be used for caching (in-place) */
@ -1412,7 +1478,18 @@ gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec,
} }
beach: beach:
GST_DEBUG_OBJECT (ffmpegdec, "return flow %d, out %p, len %d",
*ret, *outbuf, len);
return len; return len;
/* ERRORS */
clipped:
{
GST_DEBUG_OBJECT (ffmpegdec, "buffer clipped");
gst_buffer_unref (*outbuf);
*outbuf = NULL;
goto beach;
}
} }
@ -1481,7 +1558,7 @@ gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
goto beach; goto beach;
} else { } else {
/* this is where I lost my last clue on ffmpeg... */ /* this is where I lost my last clue on ffmpeg... */
*got_data = 1; //(ffmpegdec->pctx || have_data) ? 1 : 0; *got_data = 1;
} }
if (outbuf) { if (outbuf) {
@ -1491,7 +1568,14 @@ gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
/* mark pending discont */
if (ffmpegdec->discont) {
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
ffmpegdec->discont = FALSE;
}
/* set caps */
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegdec->srcpad)); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegdec->srcpad));
/* and off we go */
*ret = gst_pad_push (ffmpegdec->srcpad, outbuf); *ret = gst_pad_push (ffmpegdec->srcpad, outbuf);
} else { } else {
GST_DEBUG_OBJECT (ffmpegdec, "We didn't get a decoded buffer"); GST_DEBUG_OBJECT (ffmpegdec, "We didn't get a decoded buffer");
@ -1561,6 +1645,7 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
if (ffmpegdec->opened) { if (ffmpegdec->opened) {
avcodec_flush_buffers (ffmpegdec->context); avcodec_flush_buffers (ffmpegdec->context);
} }
ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
gst_ffmpegdec_reset_qos (ffmpegdec); gst_ffmpegdec_reset_qos (ffmpegdec);
gst_ffmpegdec_flush_pcache (ffmpegdec); gst_ffmpegdec_flush_pcache (ffmpegdec);
ffmpegdec->waiting_for_key = TRUE; ffmpegdec->waiting_for_key = TRUE;
@ -1622,7 +1707,7 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
} }
GST_DEBUG_OBJECT (ffmpegdec, GST_DEBUG_OBJECT (ffmpegdec,
"NEWSEGMENT in time (next_ts) %" GST_TIME_FORMAT " -- %" "NEWSEGMENT in time start %" GST_TIME_FORMAT " -- stop %"
GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
/* and store the values */ /* and store the values */
@ -1671,7 +1756,6 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
guint8 *bdata, *data; guint8 *bdata, *data;
gint bsize, size, len, have_data; gint bsize, size, len, have_data;
guint64 in_ts; guint64 in_ts;
guint64 next_ts = GST_CLOCK_TIME_NONE;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
ffmpegdec = (GstFFMpegDec *) (GST_PAD_PARENT (pad)); ffmpegdec = (GstFFMpegDec *) (GST_PAD_PARENT (pad));
@ -1693,6 +1777,8 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
gst_ffmpegdec_flush_pcache (ffmpegdec); gst_ffmpegdec_flush_pcache (ffmpegdec);
avcodec_flush_buffers (ffmpegdec->context); avcodec_flush_buffers (ffmpegdec->context);
ffmpegdec->waiting_for_key = TRUE; ffmpegdec->waiting_for_key = TRUE;
ffmpegdec->discont = TRUE;
ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
} }
/* do early keyframe check pretty bad to rely on the keyframe flag in the /* do early keyframe check pretty bad to rely on the keyframe flag in the
@ -1739,8 +1825,7 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
ffmpegdec->pcache = NULL; ffmpegdec->pcache = NULL;
/* If we're decoding something else, we'll use next_ts */ in_ts = timestamp;
next_ts = in_ts = timestamp;
} }
/* workarounds, functions write to buffers: /* workarounds, functions write to buffers:
* libavcodec/svq1.c:svq1_decode_frame writes to the given buffer. * libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
@ -1810,9 +1895,6 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
/* now we don;t know the timestamp anymore, the parser should help */ /* now we don;t know the timestamp anymore, the parser should help */
GST_BUFFER_TIMESTAMP (inbuf) = in_ts = GST_CLOCK_TIME_NONE; GST_BUFFER_TIMESTAMP (inbuf) = in_ts = GST_CLOCK_TIME_NONE;
if (!GST_CLOCK_TIME_IS_VALID (next_ts))
next_ts = ffmpegdec->next_ts;
GST_LOG_OBJECT (ffmpegdec, "Before (while bsize>0). bsize:%d , bdata:%p", GST_LOG_OBJECT (ffmpegdec, "Before (while bsize>0). bsize:%d , bdata:%p",
bsize, bdata); bsize, bdata);
} while (bsize > 0); } while (bsize > 0);