gboolean ts_is_dts;
gboolean has_b_frames;
+ /* Incremented by 1 each time ffmpeg consumes an encoded frame but does not
+ * give a decoded frame back. Gradually reduced to 0 while draining.
+ */
+ gint64 delay_offset;
+
+ /* Last valid offset received from upstream. Used with delay_offset to
+ * calculate offsets while draining.
+ */
+ gint64 last_offset;
+
/* parsing */
AVCodecParserContext *pctx;
GstBuffer *pcache;
ffmpegdec->do_padding = DEFAULT_DO_PADDING;
ffmpegdec->debug_mv = DEFAULT_DEBUG_MV;
ffmpegdec->crop = DEFAULT_CROP;
+ ffmpegdec->delay_offset = 0;
+ ffmpegdec->last_offset = GST_BUFFER_OFFSET_NONE;
gst_ts_handler_init (ffmpegdec);
res =
gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts, stop, &cstart,
&cstop);
- if (G_UNLIKELY (!res))
+ if (G_UNLIKELY (!res && in_ts != dec->segment.stop))
goto beach;
+ /* Special case: last buffer has zero duration */
+ if (G_UNLIKELY (in_ts == dec->segment.stop))
+ {
+ res = TRUE;
+ cstart = in_ts - 1;
+ stop = cstop = in_ts;
+ }
+
/* we're pretty sure the duration of this buffer is not till the end of this
* segment (which _clip will assume when the stop is -1) */
if (stop == GST_CLOCK_TIME_NONE)
GstBuffer *buf = GST_BUFFER_CAST (ffmpegdec->queued->data);
GST_LOG_OBJECT (ffmpegdec, "pushing buffer %p, offset %"
- G_GINT64_FORMAT ", timestamp %"
+ G_GUINT64_FORMAT ", timestamp %"
GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
GST_BUFFER_OFFSET (buf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
gst_ts_handler_consume (ffmpegdec, len);
+ if (len < 0 || have_data <= 0)
+ ffmpegdec->delay_offset++;
+
/* restore previous state */
if (!decode)
ffmpegdec->context->hurry_up = hurry_up;
* Offset:
*
* 1) Use input offset if valid
+ * 2) Use value converted from timestamp if valid
*/
- if (in_offset == GST_BUFFER_OFFSET_NONE) {
+ if (in_offset != GST_BUFFER_OFFSET_NONE) {
+ GST_LOG_OBJECT (ffmpegdec, "using in_offset %" G_GINT64_FORMAT, in_offset -
+ ffmpegdec->delay_offset);
+ out_offset = in_offset - ffmpegdec->delay_offset;
+ ffmpegdec->last_offset = in_offset;
+ } else if (ffmpegdec->last_offset != GST_BUFFER_OFFSET_NONE) {
+ GST_LOG_OBJECT (ffmpegdec, "using last_offset %" G_GINT64_FORMAT, ffmpegdec->last_offset -
+ ffmpegdec->delay_offset);
+ out_offset = ffmpegdec->last_offset - ffmpegdec->delay_offset;
+ } else if (out_timestamp >= 0) {
+ GstFormat out_fmt = GST_FORMAT_DEFAULT;
+ GST_LOG_OBJECT (ffmpegdec, "Using offset converted from timestamp");
+ gst_pad_query_peer_convert (ffmpegdec->sinkpad,
+ GST_FORMAT_TIME, out_timestamp, &out_fmt, &out_offset);
+ } else {
GST_LOG_OBJECT (ffmpegdec, "no valid offset found");
out_offset = GST_BUFFER_OFFSET_NONE;
- } else {
- GST_LOG_OBJECT (ffmpegdec, "using in_offset %" G_GINT64_FORMAT, in_offset);
- out_offset = in_offset;
}
GST_BUFFER_OFFSET (*outbuf) = out_offset;
do {
GstFlowReturn ret;
+ if (ffmpegdec->delay_offset > 0)
+ ffmpegdec->delay_offset--;
+
len = gst_ffmpegdec_frame (ffmpegdec, NULL, 0, &have_data,
GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, -1, &ret);
if (len < 0 || have_data == 0)
/* if we have some queued frames for reverse playback, flush them now */
flush_queued (ffmpegdec);
}
+ ffmpegdec->delay_offset = 0;
+ ffmpegdec->last_offset = GST_BUFFER_OFFSET_NONE;
}
static void