mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 22:36:33 +00:00
ext/ffmpeg/gstffmpegdec.c: Split out audio and video frame decoding.
Original commit message from CVS: * ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_init), (gst_ffmpegdec_query), (gst_ffmpegdec_update_qos), (gst_ffmpegdec_reset_qos), (gst_ffmpegdec_read_qos), (gst_ffmpegdec_open), (gst_ffmpegdec_setcaps), (gst_ffmpegdec_get_buffer), (gst_ffmpegdec_release_buffer), (gst_ffmpegdec_add_pixel_aspect_ratio), (gst_ffmpegdec_negotiate), (gst_ffmpegdec_do_qos), (gst_ffmpegdec_video_frame), (gst_ffmpegdec_audio_frame), (gst_ffmpegdec_frame), (gst_ffmpegdec_flush_pcache), (gst_ffmpegdec_sink_event), (gst_ffmpegdec_chain): Split out audio and video frame decoding. Added dropping/clipping of decoded buffers. Ran gst-indent on code. Small non-invasive code cleanups.
This commit is contained in:
parent
4c30f87706
commit
280d6dd420
2 changed files with 551 additions and 354 deletions
17
ChangeLog
17
ChangeLog
|
@ -1,3 +1,20 @@
|
|||
2006-06-02 Edward Hervey <edward@fluendo.com>
|
||||
|
||||
* ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_init),
|
||||
(gst_ffmpegdec_query), (gst_ffmpegdec_update_qos),
|
||||
(gst_ffmpegdec_reset_qos), (gst_ffmpegdec_read_qos),
|
||||
(gst_ffmpegdec_open), (gst_ffmpegdec_setcaps),
|
||||
(gst_ffmpegdec_get_buffer), (gst_ffmpegdec_release_buffer),
|
||||
(gst_ffmpegdec_add_pixel_aspect_ratio), (gst_ffmpegdec_negotiate),
|
||||
(gst_ffmpegdec_do_qos), (gst_ffmpegdec_video_frame),
|
||||
(gst_ffmpegdec_audio_frame), (gst_ffmpegdec_frame),
|
||||
(gst_ffmpegdec_flush_pcache), (gst_ffmpegdec_sink_event),
|
||||
(gst_ffmpegdec_chain):
|
||||
Split out audio and video frame decoding.
|
||||
Added dropping/clipping of decoded buffers.
|
||||
Ran gst-indent on code.
|
||||
Small non-invasive code cleanups.
|
||||
|
||||
2006-06-02 Michael Smith <msmith@fluendo.com>
|
||||
|
||||
* ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_register):
|
||||
|
|
|
@ -67,7 +67,7 @@ struct _GstFFMpegDec
|
|||
} audio;
|
||||
} format;
|
||||
gboolean waiting_for_key;
|
||||
guint64 next_ts, synctime;
|
||||
guint64 next_ts;
|
||||
|
||||
/* parsing */
|
||||
AVCodecParserContext *pctx;
|
||||
|
@ -337,10 +337,10 @@ gst_ffmpegdec_query (GstPad * pad, GstQuery * query)
|
|||
res = gst_pad_query (peer, query);
|
||||
gst_object_unref (peer);
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
GstFormat bfmt;
|
||||
|
||||
bfmt = GST_FORMAT_BYTES;
|
||||
|
||||
/* ok, do bitrate calc... */
|
||||
|
@ -362,7 +362,8 @@ gst_ffmpegdec_query (GstPad * pad, GstQuery * query)
|
|||
|
||||
/* FIXME, make me ATOMIC */
|
||||
static void
|
||||
gst_ffmpegdec_update_qos (GstFFMpegDec *ffmpegdec, gdouble proportion, GstClockTime time)
|
||||
gst_ffmpegdec_update_qos (GstFFMpegDec * ffmpegdec, gdouble proportion,
|
||||
GstClockTime time)
|
||||
{
|
||||
GST_OBJECT_LOCK (ffmpegdec);
|
||||
ffmpegdec->proportion = proportion;
|
||||
|
@ -377,7 +378,8 @@ gst_ffmpegdec_reset_qos (GstFFMpegDec *ffmpegdec)
|
|||
}
|
||||
|
||||
static void
|
||||
gst_ffmpegdec_read_qos (GstFFMpegDec *ffmpegdec, gdouble *proportion, GstClockTime *time)
|
||||
gst_ffmpegdec_read_qos (GstFFMpegDec * ffmpegdec, gdouble * proportion,
|
||||
GstClockTime * time)
|
||||
{
|
||||
GST_OBJECT_LOCK (ffmpegdec);
|
||||
*proportion = ffmpegdec->proportion;
|
||||
|
@ -474,11 +476,9 @@ gst_ffmpegdec_open (GstFFMpegDec * ffmpegdec)
|
|||
|
||||
GST_LOG_OBJECT (ffmpegdec, "Opened ffmpeg codec %s", oclass->in_plugin->name);
|
||||
|
||||
/* open a parser if we can - exclude mpeg4, because it is already
|
||||
* framed (divx), mp3 because it doesn't work (?) and mjpeg because
|
||||
* of $(see mpeg4)... */
|
||||
if (oclass->in_plugin->id != CODEC_ID_MPEG4 &&
|
||||
oclass->in_plugin->id != CODEC_ID_MJPEG &&
|
||||
/* open a parser if we can - exclude mp3 because it doesn't work (?),
|
||||
* and mjpeg because ... */
|
||||
if (oclass->in_plugin->id != CODEC_ID_MJPEG &&
|
||||
oclass->in_plugin->id != CODEC_ID_MP3 &&
|
||||
oclass->in_plugin->id != CODEC_ID_H264) {
|
||||
ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id);
|
||||
|
@ -498,7 +498,6 @@ gst_ffmpegdec_open (GstFFMpegDec * ffmpegdec)
|
|||
break;
|
||||
}
|
||||
ffmpegdec->next_ts = 0;
|
||||
ffmpegdec->synctime = GST_CLOCK_TIME_NONE;
|
||||
ffmpegdec->last_buffer = NULL;
|
||||
/* FIXME, reset_qos holds the LOCK */
|
||||
ffmpegdec->proportion = 0.0;
|
||||
|
@ -653,8 +652,9 @@ gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
|
|||
return avcodec_default_get_buffer (context, picture);
|
||||
}
|
||||
|
||||
if (gst_pad_alloc_buffer_and_set_caps (ffmpegdec->srcpad, GST_BUFFER_OFFSET_NONE,
|
||||
bufsize, GST_PAD_CAPS (ffmpegdec->srcpad), &buf) != GST_FLOW_OK)
|
||||
if (gst_pad_alloc_buffer_and_set_caps (ffmpegdec->srcpad,
|
||||
GST_BUFFER_OFFSET_NONE, bufsize, GST_PAD_CAPS (ffmpegdec->srcpad),
|
||||
&buf) != GST_FLOW_OK)
|
||||
return -1;
|
||||
ffmpegdec->last_buffer = buf;
|
||||
|
||||
|
@ -726,7 +726,8 @@ gst_ffmpegdec_add_pixel_aspect_ratio (GstFFMpegDec * ffmpegdec,
|
|||
demuxer_num = gst_value_get_fraction_numerator (ffmpegdec->par);
|
||||
demuxer_denom = gst_value_get_fraction_denominator (ffmpegdec->par);
|
||||
demuxer_par_set = TRUE;
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Demuxer PAR: %d:%d", demuxer_num, demuxer_denom);
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Demuxer PAR: %d:%d", demuxer_num,
|
||||
demuxer_denom);
|
||||
}
|
||||
|
||||
if (ffmpegdec->context->sample_aspect_ratio.num &&
|
||||
|
@ -734,7 +735,8 @@ gst_ffmpegdec_add_pixel_aspect_ratio (GstFFMpegDec * ffmpegdec,
|
|||
decoder_num = ffmpegdec->context->sample_aspect_ratio.num;
|
||||
decoder_denom = ffmpegdec->context->sample_aspect_ratio.den;
|
||||
decoder_par_set = TRUE;
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Decoder PAR: %d:%d", decoder_num, decoder_denom);
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Decoder PAR: %d:%d", decoder_num,
|
||||
decoder_denom);
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (ffmpegdec);
|
||||
|
@ -761,24 +763,27 @@ gst_ffmpegdec_add_pixel_aspect_ratio (GstFFMpegDec * ffmpegdec,
|
|||
/* fall through and use decoder pixel aspect ratio */
|
||||
use_decoder_par:
|
||||
{
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Setting decoder provided pixel-aspect-ratio of %u:%u",
|
||||
decoder_num, decoder_denom);
|
||||
gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
||||
decoder_num, decoder_denom, NULL);
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Setting decoder provided pixel-aspect-ratio of %u:%u", decoder_num,
|
||||
decoder_denom);
|
||||
gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, decoder_num,
|
||||
decoder_denom, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
use_demuxer_par:
|
||||
{
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Setting demuxer provided pixel-aspect-ratio of %u:%u",
|
||||
demuxer_num, demuxer_denom);
|
||||
gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
||||
demuxer_num, demuxer_denom, NULL);
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Setting demuxer provided pixel-aspect-ratio of %u:%u", demuxer_num,
|
||||
demuxer_denom);
|
||||
gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, demuxer_num,
|
||||
demuxer_denom, NULL);
|
||||
return;
|
||||
}
|
||||
no_par:
|
||||
{
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Neither demuxer nor codec provide a pixel-aspect-ratio");
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Neither demuxer nor codec provide a pixel-aspect-ratio");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -816,7 +821,8 @@ gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
|
|||
ffmpegdec->context->sample_rate &&
|
||||
ffmpegdec->format.audio.channels == ffmpegdec->context->channels)
|
||||
return TRUE;
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Renegotiating audio from %dHz@%dchannels to %dHz@%dchannels",
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Renegotiating audio from %dHz@%dchannels to %dHz@%dchannels",
|
||||
ffmpegdec->format.audio.samplerate, ffmpegdec->format.audio.channels,
|
||||
ffmpegdec->context->sample_rate, ffmpegdec->context->channels);
|
||||
ffmpegdec->format.audio.samplerate = ffmpegdec->context->sample_rate;
|
||||
|
@ -876,7 +882,8 @@ caps_failed:
|
|||
* entirely.
|
||||
*/
|
||||
static gboolean
|
||||
gst_ffmpegdec_do_qos (GstFFMpegDec *ffmpegdec, GstClockTime timestamp, gboolean *mode_switch)
|
||||
gst_ffmpegdec_do_qos (GstFFMpegDec * ffmpegdec, GstClockTime timestamp,
|
||||
gboolean * mode_switch)
|
||||
{
|
||||
GstClockTimeDiff diff;
|
||||
gdouble proportion;
|
||||
|
@ -914,16 +921,15 @@ gst_ffmpegdec_do_qos (GstFFMpegDec *ffmpegdec, GstClockTime timestamp, gboolean
|
|||
* speed up again when we were slow. */
|
||||
if (proportion < 0.4 && diff < 0) {
|
||||
goto normal_mode;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
/* if we're more than two seconds late, switch to the next keyframe */
|
||||
/* FIXME, let the demuxer decide what's the best since we might be dropping
|
||||
* a lot of frames when the keyframe is far away or we even might not get a new
|
||||
* keyframe at all.. */
|
||||
if (diff > ((GstClockTimeDiff) GST_SECOND * 2) && !ffmpegdec->waiting_for_key) {
|
||||
if (diff > ((GstClockTimeDiff) GST_SECOND * 2)
|
||||
&& !ffmpegdec->waiting_for_key) {
|
||||
goto skip_to_keyframe;
|
||||
}
|
||||
else if (diff >= 0) {
|
||||
} else if (diff >= 0) {
|
||||
/* we're too slow, try to speed up */
|
||||
if (ffmpegdec->waiting_for_key) {
|
||||
/* we were waiting for a keyframe, that's ok */
|
||||
|
@ -955,7 +961,8 @@ skip_to_keyframe:
|
|||
ffmpegdec->context->hurry_up = 1;
|
||||
ffmpegdec->waiting_for_key = TRUE;
|
||||
*mode_switch = TRUE;
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "QOS: keyframe, %"G_GINT64_FORMAT" > GST_SECOND/2", diff);
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"QOS: keyframe, %" G_GINT64_FORMAT " > GST_SECOND/2", diff);
|
||||
/* we can skip the current frame */
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -964,55 +971,66 @@ hurry_up:
|
|||
if (ffmpegdec->context->hurry_up != 1) {
|
||||
ffmpegdec->context->hurry_up = 1;
|
||||
*mode_switch = TRUE;
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "QOS: hurry up, diff %"G_GINT64_FORMAT" >= 0", diff);
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"QOS: hurry up, diff %" G_GINT64_FORMAT " >= 0", diff);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* gst_ffmpegdec_[video|audio]_frame:
|
||||
* ffmpegdec:
|
||||
* data: pointer to the data to decode
|
||||
* size: size of data in bytes
|
||||
* inbuf: incoming buffer. Must have valid timestamp.
|
||||
* The incoming buffer can be NULL, but only used for EOS draining.
|
||||
* outbuf: outgoing buffer. Different from NULL ONLY if it contains decoded data.
|
||||
* ret: Return flow.
|
||||
*
|
||||
* Returns: number of bytes used in decoding. The check for successful decode is
|
||||
* outbuf being non-NULL.
|
||||
*/
|
||||
|
||||
static gint
|
||||
gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
|
||||
guint8 * data, guint size, gint * got_data, guint64 * in_ts,
|
||||
GstBuffer * inbuf, GstFlowReturn * ret)
|
||||
{
|
||||
GstFFMpegDecClass *oclass;
|
||||
GstBuffer *outbuf = NULL;
|
||||
gint have_data = 0, len = 0;
|
||||
|
||||
if (ffmpegdec->context->codec == NULL)
|
||||
return -1;
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"data:%p, size:%d, *in_ts:%" GST_TIME_FORMAT " inbuf:%p inbuf.ts:%"
|
||||
GST_TIME_FORMAT, data, size, GST_TIME_ARGS (*in_ts), inbuf,
|
||||
GST_TIME_ARGS ((inbuf) ? GST_BUFFER_TIMESTAMP (inbuf) : 0));
|
||||
|
||||
ffmpegdec->context->frame_number++;
|
||||
|
||||
oclass = (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
|
||||
switch (oclass->in_plugin->type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
gst_ffmpegdec_video_frame (GstFFMpegDec * ffmpegdec,
|
||||
guint8 * data, guint size,
|
||||
GstBuffer * inbuf, GstBuffer ** outbuf, GstFlowReturn * ret)
|
||||
{
|
||||
GstFFMpegDecClass *oclass =
|
||||
(GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
gint len = -1;
|
||||
gint have_data;
|
||||
gboolean iskeyframe = FALSE;
|
||||
gboolean is_itype = FALSE;
|
||||
gboolean is_reference = FALSE;
|
||||
gboolean mode_switch = FALSE;
|
||||
guint64 dec_time = GST_CLOCK_TIME_NONE;
|
||||
|
||||
*ret = GST_FLOW_OK;
|
||||
*outbuf = NULL;
|
||||
|
||||
ffmpegdec->context->opaque = ffmpegdec;
|
||||
|
||||
/* run QoS code, returns FALSE if we can skip decoding this
|
||||
* frame entirely. */
|
||||
if (!gst_ffmpegdec_do_qos (ffmpegdec, *in_ts, &mode_switch)) {
|
||||
have_data = 0;
|
||||
break;
|
||||
}
|
||||
if (inbuf)
|
||||
if (!gst_ffmpegdec_do_qos (ffmpegdec, GST_BUFFER_TIMESTAMP (inbuf),
|
||||
&mode_switch))
|
||||
goto beach;
|
||||
|
||||
ffmpegdec->picture->pict_type = -1; /* in case we skip frames */
|
||||
|
||||
len = avcodec_decode_video (ffmpegdec->context,
|
||||
ffmpegdec->picture, &have_data, data, size);
|
||||
|
||||
/* Get timestamp from FFMPEG */
|
||||
dec_time = gst_ffmpeg_time_ff_to_gst ((guint64) ffmpegdec->picture->pts,
|
||||
ffmpegdec->context->time_base);
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Picture : key_frame:%d , pict_type:%d, pts:%lld => [%" GST_TIME_FORMAT
|
||||
"]", ffmpegdec->picture->key_frame, ffmpegdec->picture->pict_type,
|
||||
ffmpegdec->picture->pts, GST_TIME_ARGS (dec_time));
|
||||
|
||||
/* when we are in hurry_up mode, don't complain when ffmpeg returned
|
||||
* no data because we told it to skip stuff. */
|
||||
if (len < 0 && (mode_switch || ffmpegdec->context->hurry_up))
|
||||
|
@ -1036,60 +1054,23 @@ gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
|
|||
ffmpegdec->waiting_for_key = FALSE;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (ffmpegdec, "Dropping non-keyframe (seek/init)");
|
||||
have_data = 0;
|
||||
break;
|
||||
goto beach;
|
||||
}
|
||||
}
|
||||
|
||||
/* note that ffmpeg sometimes gets the FPS wrong.
|
||||
* For B-frame containing movies, we get all pictures delayed
|
||||
* except for the I frames, so we synchronize only on I frames
|
||||
* and keep an internal counter based on FPS for the others. */
|
||||
if (!(oclass->in_plugin->capabilities & CODEC_CAP_DELAY) ||
|
||||
((iskeyframe || !GST_CLOCK_TIME_IS_VALID (ffmpegdec->next_ts)) &&
|
||||
GST_CLOCK_TIME_IS_VALID (*in_ts))) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "setting next_ts to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (*in_ts));
|
||||
ffmpegdec->next_ts = *in_ts;
|
||||
*in_ts = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
/* precise seeking.... */
|
||||
if (GST_CLOCK_TIME_IS_VALID (ffmpegdec->synctime)) {
|
||||
if (ffmpegdec->next_ts >= ffmpegdec->synctime) {
|
||||
ffmpegdec->synctime = GST_CLOCK_TIME_NONE;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (ffmpegdec,
|
||||
"Dropping frame for synctime %" GST_TIME_FORMAT
|
||||
", expected(next_ts) %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (ffmpegdec->synctime),
|
||||
GST_TIME_ARGS (ffmpegdec->next_ts));
|
||||
|
||||
if (ffmpegdec->picture->opaque != NULL) {
|
||||
outbuf = (GstBuffer *) ffmpegdec->picture->opaque;
|
||||
gst_buffer_unref (outbuf);
|
||||
}
|
||||
|
||||
have_data = 0;
|
||||
/* don´t break here! Timestamps are updated below */
|
||||
}
|
||||
}
|
||||
|
||||
if (ffmpegdec->waiting_for_key && !iskeyframe) {
|
||||
have_data = 0;
|
||||
} else if (len >= 0 && have_data > 0) {
|
||||
if (len >= 0 && have_data > 0) {
|
||||
/* libavcodec constantly crashes on stupid buffer allocation
|
||||
* errors inside. This drives me crazy, so we let it allocate
|
||||
* its own buffers and copy to our own buffer afterwards... */
|
||||
|
||||
/* BUFFER CREATION */
|
||||
if (ffmpegdec->picture->opaque != NULL) {
|
||||
outbuf = (GstBuffer *) ffmpegdec->picture->opaque;
|
||||
if (outbuf == ffmpegdec->last_buffer)
|
||||
*outbuf = (GstBuffer *) ffmpegdec->picture->opaque;
|
||||
if (*outbuf == ffmpegdec->last_buffer)
|
||||
ffmpegdec->last_buffer = NULL;
|
||||
} else {
|
||||
AVPicture pic;
|
||||
gint fsize =
|
||||
gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
|
||||
gint fsize = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
|
||||
ffmpegdec->context->width, ffmpegdec->context->height);
|
||||
|
||||
if (!gst_ffmpegdec_negotiate (ffmpegdec))
|
||||
|
@ -1099,21 +1080,22 @@ gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
|
|||
if ((*ret =
|
||||
gst_pad_alloc_buffer_and_set_caps (ffmpegdec->srcpad,
|
||||
GST_BUFFER_OFFSET_NONE, fsize,
|
||||
GST_PAD_CAPS (ffmpegdec->srcpad),
|
||||
&outbuf)) != GST_FLOW_OK)
|
||||
return -1;
|
||||
GST_PAD_CAPS (ffmpegdec->srcpad), outbuf)) != GST_FLOW_OK) {
|
||||
len = -1;
|
||||
goto beach;
|
||||
}
|
||||
} else {
|
||||
/* for paletted data we can't use pad_alloc_buffer(), because
|
||||
* fsize contains the size of the palette, so the overall size
|
||||
* is bigger than ffmpegcolorspace's unit size, which will
|
||||
* prompt GstBaseTransform to complain endlessly ... */
|
||||
outbuf = gst_buffer_new_and_alloc (fsize);
|
||||
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegdec->srcpad));
|
||||
*outbuf = gst_buffer_new_and_alloc (fsize);
|
||||
gst_buffer_set_caps (*outbuf, GST_PAD_CAPS (ffmpegdec->srcpad));
|
||||
}
|
||||
|
||||
/* original ffmpeg code does not handle odd sizes correctly.
|
||||
* This patched up version does */
|
||||
gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
|
||||
gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (*outbuf),
|
||||
ffmpegdec->context->pix_fmt,
|
||||
ffmpegdec->context->width, ffmpegdec->context->height);
|
||||
|
||||
|
@ -1126,140 +1108,319 @@ gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
|
|||
ffmpegdec->context->width, ffmpegdec->context->height);
|
||||
}
|
||||
|
||||
ffmpegdec->waiting_for_key = FALSE;
|
||||
|
||||
ffmpegdec->waiting_for_key = FALSE;
|
||||
if (!iskeyframe) {
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Timestamp and duration
|
||||
*/
|
||||
|
||||
/* If we have used the framerate from the demuxer then
|
||||
* also use the demuxer's timestamp information (#317596) */
|
||||
if (ffmpegdec->format.video.fps_n != -1 && inbuf != NULL) {
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (inbuf))) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "using incoming buffer's timestamps");
|
||||
GST_LOG_OBJECT (ffmpegdec, "incoming timestamp %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)));
|
||||
gst_buffer_stamp (outbuf, inbuf);
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"incoming timestamp %" GST_TIME_FORMAT " duration:%"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)));
|
||||
gst_buffer_stamp (*outbuf, inbuf);
|
||||
} else {
|
||||
GST_LOG_OBJECT (ffmpegdec, "using timestamp returned by ffmpeg");
|
||||
GST_BUFFER_TIMESTAMP (*outbuf) = dec_time;
|
||||
}
|
||||
if (!(GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (*outbuf)))) {
|
||||
/* some codecs don't have normal time_base */
|
||||
if (ffmpegdec->context->time_base.den >= 1000)
|
||||
GST_BUFFER_DURATION (*outbuf) =
|
||||
gst_util_uint64_scale_int (GST_SECOND,
|
||||
ffmpegdec->format.video.fps_d, ffmpegdec->format.video.fps_n);
|
||||
else
|
||||
GST_BUFFER_DURATION (*outbuf) =
|
||||
gst_util_uint64_scale_int (GST_SECOND,
|
||||
ffmpegdec->context->time_base.num,
|
||||
ffmpegdec->context->time_base.den);
|
||||
/* Take repeat_pict into account */
|
||||
GST_BUFFER_DURATION (*outbuf) += GST_BUFFER_DURATION (*outbuf)
|
||||
* ffmpegdec->picture->repeat_pict / 2;
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Buffer duration is now %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (*outbuf)));
|
||||
}
|
||||
} else {
|
||||
GST_LOG_OBJECT (ffmpegdec, "using decoder's timestamps");
|
||||
GST_BUFFER_TIMESTAMP (outbuf) = ffmpegdec->next_ts;
|
||||
GST_BUFFER_TIMESTAMP (*outbuf) = dec_time;
|
||||
if (ffmpegdec->context->time_base.num != 0 &&
|
||||
ffmpegdec->context->time_base.den != 0) {
|
||||
GST_BUFFER_DURATION (outbuf) =
|
||||
GST_BUFFER_DURATION (*outbuf) =
|
||||
gst_util_uint64_scale_int (GST_SECOND,
|
||||
ffmpegdec->context->time_base.num,
|
||||
ffmpegdec->context->time_base.den);
|
||||
|
||||
/* Take repeat_pict into account */
|
||||
GST_BUFFER_DURATION (outbuf) += GST_BUFFER_DURATION (outbuf)
|
||||
GST_BUFFER_DURATION (*outbuf) += GST_BUFFER_DURATION (*outbuf)
|
||||
* ffmpegdec->picture->repeat_pict / 2;
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"advancing next_ts by duration of %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
|
||||
ffmpegdec->next_ts += GST_BUFFER_DURATION (outbuf);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "setting next_ts to NONE");
|
||||
ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
}
|
||||
GST_LOG_OBJECT (ffmpegdec, "outgoing timestamp %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
|
||||
} else if (ffmpegdec->picture->pict_type != -1 &&
|
||||
oclass->in_plugin->capabilities & CODEC_CAP_DELAY) {
|
||||
/* update time for skip-frame */
|
||||
if ((have_data == 0) ||
|
||||
((iskeyframe || !GST_CLOCK_TIME_IS_VALID (ffmpegdec->next_ts))
|
||||
&& GST_CLOCK_TIME_IS_VALID (*in_ts))) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "setting next_ts to *in_ts");
|
||||
ffmpegdec->next_ts = *in_ts;
|
||||
*in_ts = GST_CLOCK_TIME_NONE;
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*outbuf)));
|
||||
}
|
||||
|
||||
if (ffmpegdec->context->time_base.num != 0 &&
|
||||
ffmpegdec->context->time_base.den != 0) {
|
||||
guint64 dur = GST_SECOND *
|
||||
ffmpegdec->context->time_base.num /
|
||||
ffmpegdec->context->time_base.den;
|
||||
|
||||
/* Take repeat_pict into account */
|
||||
dur += dur * ffmpegdec->picture->repeat_pict / 2;
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Advancing next_ts by dur:%" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (dur));
|
||||
ffmpegdec->next_ts += dur;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "setting next_ts to NONE");
|
||||
ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
}
|
||||
/* palette is not part of raw video frame in gst and the size
|
||||
* of the outgoing buffer needs to be adjusted accordingly */
|
||||
if (ffmpegdec->context->palctrl != NULL && outbuf != NULL)
|
||||
GST_BUFFER_SIZE (outbuf) -= AVPALETTE_SIZE;
|
||||
break;
|
||||
if (ffmpegdec->context->palctrl != NULL && *outbuf != NULL)
|
||||
GST_BUFFER_SIZE (*outbuf) -= AVPALETTE_SIZE;
|
||||
|
||||
|
||||
if (*outbuf) {
|
||||
ffmpegdec->next_ts =
|
||||
GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf);
|
||||
|
||||
/* Clipping/dropping */
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"clipping/droping check for buffer %" GST_TIME_FORMAT " [duration:%"
|
||||
GST_TIME_FORMAT "]", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*outbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (*outbuf)));
|
||||
if ((ffmpegdec->segment.format == GST_FORMAT_TIME)
|
||||
&& (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (*outbuf)))) {
|
||||
gint64 start, stop, clip_start, clip_stop;
|
||||
|
||||
start = GST_BUFFER_TIMESTAMP (*outbuf);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (*outbuf))) {
|
||||
stop = start + GST_BUFFER_DURATION (*outbuf);
|
||||
|
||||
if (!(gst_segment_clip (&ffmpegdec->segment, GST_FORMAT_TIME,
|
||||
start, stop, &clip_start, &clip_stop))) {
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"dropping buffer since it's outside configured segment [%"
|
||||
GST_TIME_FORMAT "-%" GST_TIME_FORMAT "]",
|
||||
GST_TIME_ARGS (ffmpegdec->segment.start),
|
||||
GST_TIME_ARGS (ffmpegdec->segment.stop));
|
||||
gst_buffer_unref (*outbuf);
|
||||
*outbuf = NULL;
|
||||
} else {
|
||||
GST_BUFFER_TIMESTAMP (*outbuf) = clip_start;
|
||||
GST_BUFFER_DURATION (*outbuf) = clip_stop - clip_start;
|
||||
GST_LOG_OBJECT (ffmpegdec, "Buffer %" GST_TIME_FORMAT " [duration:%"
|
||||
GST_TIME_FORMAT "] is inside configured segment",
|
||||
GST_TIME_ARGS (clip_start),
|
||||
GST_TIME_ARGS (clip_stop - clip_start));
|
||||
}
|
||||
case CODEC_TYPE_AUDIO:
|
||||
} else {
|
||||
if ((start < ffmpegdec->segment.start)
|
||||
|| (start >= ffmpegdec->segment.stop)) {
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"dropping buffer since it's start time is outside configured segment");
|
||||
gst_buffer_unref (*outbuf);
|
||||
*outbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
} else
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"segment wasn't configured, or buffer has no timestamp. No clipping done");
|
||||
}
|
||||
|
||||
beach:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static gint
|
||||
gst_ffmpegdec_audio_frame (GstFFMpegDec * ffmpegdec,
|
||||
guint8 * data, guint size,
|
||||
GstBuffer * inbuf, GstBuffer ** outbuf, GstFlowReturn * ret)
|
||||
{
|
||||
gint len = -1;
|
||||
gint have_data;
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"size:%d, inbuf.ts %" GST_TIME_FORMAT " , ffmpegdec->next_ts:%"
|
||||
GST_TIME_FORMAT, size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)),
|
||||
GST_TIME_ARGS (ffmpegdec->next_ts));
|
||||
|
||||
/* outgoing buffer */
|
||||
if (!ffmpegdec->last_buffer)
|
||||
outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||
*outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||
else {
|
||||
outbuf = ffmpegdec->last_buffer;
|
||||
*outbuf = ffmpegdec->last_buffer;
|
||||
ffmpegdec->last_buffer = NULL;
|
||||
}
|
||||
|
||||
len = avcodec_decode_audio (ffmpegdec->context,
|
||||
(int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
|
||||
(int16_t *) GST_BUFFER_DATA (*outbuf), &have_data, data, size);
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Decode audio: len=%d, have_data=%d", len, have_data);
|
||||
|
||||
if (len >= 0 && have_data > 0) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Creating output buffer");
|
||||
if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
|
||||
gst_buffer_unref (outbuf);
|
||||
return -1;
|
||||
gst_buffer_unref (*outbuf);
|
||||
*outbuf = NULL;
|
||||
len = -1;
|
||||
goto beach;
|
||||
}
|
||||
|
||||
GST_BUFFER_SIZE (outbuf) = have_data;
|
||||
if (GST_CLOCK_TIME_IS_VALID (*in_ts)) {
|
||||
ffmpegdec->next_ts = *in_ts;
|
||||
}
|
||||
GST_BUFFER_TIMESTAMP (outbuf) = ffmpegdec->next_ts;
|
||||
GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
|
||||
/* Buffer size timestamp and duration */
|
||||
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);
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec,
|
||||
"Buffer created. Size:%d , timestamp:%" GST_TIME_FORMAT " , duration:%"
|
||||
GST_TIME_FORMAT, GST_BUFFER_SIZE (*outbuf),
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*outbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (*outbuf)));
|
||||
|
||||
ffmpegdec->next_ts =
|
||||
GST_BUFFER_TIMESTAMP (*outbuf) + GST_BUFFER_DURATION (*outbuf);
|
||||
|
||||
/* clipping/dropping of outgoing audio buffers */
|
||||
if (ffmpegdec->segment.format == GST_FORMAT_TIME) {
|
||||
gint64 start, stop, cstart, cstop, diff;
|
||||
|
||||
start = GST_BUFFER_TIMESTAMP (*outbuf);
|
||||
stop = ffmpegdec->next_ts;
|
||||
|
||||
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);
|
||||
ffmpegdec->next_ts += GST_BUFFER_DURATION (outbuf);
|
||||
if (GST_CLOCK_TIME_IS_VALID (*in_ts))
|
||||
*in_ts += GST_BUFFER_DURATION (outbuf);
|
||||
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) {
|
||||
/* cache output, because it may be used for caching (in-place) */
|
||||
ffmpegdec->last_buffer = outbuf;
|
||||
ffmpegdec->last_buffer = *outbuf;
|
||||
*outbuf = NULL;
|
||||
} else {
|
||||
gst_buffer_unref (outbuf);
|
||||
gst_buffer_unref (*outbuf);
|
||||
*outbuf = NULL;
|
||||
}
|
||||
|
||||
beach:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* gst_ffmpegdec_frame:
|
||||
* ffmpegdec:
|
||||
* data: pointer to the data to decode
|
||||
* size: size of data in bytes
|
||||
* got_data: 0 if no data was decoded, != 0 otherwise.
|
||||
* inbuf: incoming buffer. The incoming buffer should be properly timestamped.
|
||||
* The incoming buffer can be NULL, but only used for EOS draining.
|
||||
* ret: GstFlowReturn to return in the chain function
|
||||
*
|
||||
* Decode the given frame and pushes it downstream.
|
||||
*
|
||||
* Returns: Number of bytes used in decoding, -1 on error/failure.
|
||||
*/
|
||||
|
||||
static gint
|
||||
gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
|
||||
guint8 * data, guint size, gint * got_data,
|
||||
GstBuffer * inbuf, GstFlowReturn * ret)
|
||||
{
|
||||
GstFFMpegDecClass *oclass =
|
||||
(GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||
GstBuffer *outbuf = NULL;
|
||||
gint have_data = 0, len = 0;
|
||||
|
||||
if (ffmpegdec->context->codec == NULL)
|
||||
return -1;
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"data:%p, size:%d, inbuf:%p inbuf.ts:%"
|
||||
GST_TIME_FORMAT, data, size, inbuf,
|
||||
GST_TIME_ARGS ((inbuf) ? GST_BUFFER_TIMESTAMP (inbuf) : 0));
|
||||
|
||||
*ret = GST_FLOW_OK;
|
||||
ffmpegdec->context->frame_number++;
|
||||
|
||||
switch (oclass->in_plugin->type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
len =
|
||||
gst_ffmpegdec_video_frame (ffmpegdec, data, size, inbuf, &outbuf,
|
||||
ret);
|
||||
break;
|
||||
case CODEC_TYPE_AUDIO:
|
||||
len =
|
||||
gst_ffmpegdec_audio_frame (ffmpegdec, data, size, inbuf, &outbuf,
|
||||
ret);
|
||||
break;
|
||||
default:
|
||||
g_assert (0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (outbuf) {
|
||||
have_data = 1;
|
||||
}
|
||||
|
||||
if (len < 0 || have_data < 0) {
|
||||
GST_ERROR_OBJECT (ffmpegdec,
|
||||
GST_WARNING_OBJECT (ffmpegdec,
|
||||
"ffdec_%s: decoding error (len: %d, have_data: %d)",
|
||||
oclass->in_plugin->name, len, have_data);
|
||||
*got_data = 0;
|
||||
return len;
|
||||
goto beach;
|
||||
} else if (len == 0 && have_data == 0) {
|
||||
*got_data = 0;
|
||||
return 0;
|
||||
goto beach;
|
||||
} else {
|
||||
/* this is where I lost my last clue on ffmpeg... */
|
||||
*got_data = 1; //(ffmpegdec->pctx || have_data) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (have_data) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "Decoded data, now pushing with timestamp %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
|
||||
if (outbuf) {
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"Decoded data, now pushing buffer with timestamp %" GST_TIME_FORMAT
|
||||
" and duration %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
|
||||
|
||||
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegdec->srcpad));
|
||||
*ret = gst_pad_push (ffmpegdec->srcpad, outbuf);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "We didn't get a decoded buffer");
|
||||
}
|
||||
|
||||
beach:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -1298,16 +1459,18 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
|
|||
if (oclass->in_plugin->capabilities & CODEC_CAP_DELAY) {
|
||||
gint have_data, len, try = 0;
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"codec has delay capabilities, calling until ffmpeg has drained everything");
|
||||
|
||||
do {
|
||||
GstFlowReturn ret;
|
||||
|
||||
len = gst_ffmpegdec_frame (ffmpegdec, NULL, 0, &have_data,
|
||||
&ffmpegdec->next_ts, NULL, &ret);
|
||||
NULL, &ret);
|
||||
if (len < 0 || have_data == 0)
|
||||
break;
|
||||
} while (try++ < 10);
|
||||
}
|
||||
ret = gst_pad_push_event (ffmpegdec->srcpad, event);
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_FLUSH_STOP:{
|
||||
|
@ -1318,7 +1481,6 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
|
|||
gst_ffmpegdec_flush_pcache (ffmpegdec);
|
||||
ffmpegdec->waiting_for_key = TRUE;
|
||||
gst_segment_init (&ffmpegdec->segment, GST_FORMAT_TIME);
|
||||
ret = gst_pad_push_event (ffmpegdec->srcpad, event);
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_NEWSEGMENT:{
|
||||
|
@ -1351,14 +1513,11 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
|
|||
|
||||
/* convert values to TIME */
|
||||
if (start != -1)
|
||||
start = gst_util_uint64_scale_int (start, GST_SECOND,
|
||||
bit_rate);
|
||||
start = gst_util_uint64_scale_int (start, GST_SECOND, bit_rate);
|
||||
if (stop != -1)
|
||||
stop = gst_util_uint64_scale_int (stop, GST_SECOND,
|
||||
bit_rate);
|
||||
stop = gst_util_uint64_scale_int (stop, GST_SECOND, bit_rate);
|
||||
if (time != -1)
|
||||
time = gst_util_uint64_scale_int (time, GST_SECOND,
|
||||
bit_rate);
|
||||
time = gst_util_uint64_scale_int (time, GST_SECOND, bit_rate);
|
||||
|
||||
/* unref old event */
|
||||
gst_event_unref (event);
|
||||
|
@ -1387,7 +1546,6 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
|
|||
|
||||
/* FIXME, newsegment does not define the next timestamp */
|
||||
ffmpegdec->next_ts = start;
|
||||
ffmpegdec->synctime = start;
|
||||
|
||||
/* FIXME, newsegment does not mean a DISCONT */
|
||||
if (ffmpegdec->opened) {
|
||||
|
@ -1396,14 +1554,14 @@ gst_ffmpegdec_sink_event (GstPad * pad, GstEvent * event)
|
|||
ffmpegdec->waiting_for_key = TRUE;
|
||||
|
||||
/* and push segment downstream */
|
||||
ret = gst_pad_push_event (ffmpegdec->srcpad, event);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = gst_pad_push_event (ffmpegdec->srcpad, event);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = gst_pad_push_event (ffmpegdec->srcpad, event);
|
||||
|
||||
done:
|
||||
gst_object_unref (ffmpegdec);
|
||||
|
||||
|
@ -1438,6 +1596,7 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
guint8 *bdata, *data;
|
||||
gint bsize, size, len, have_data;
|
||||
guint64 in_ts;
|
||||
guint64 next_ts = GST_CLOCK_TIME_NONE;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
|
||||
ffmpegdec = (GstFFMpegDec *) (GST_PAD_PARENT (pad));
|
||||
|
@ -1447,6 +1606,8 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
if (G_UNLIKELY (!ffmpegdec->opened))
|
||||
goto not_negotiated;
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "chain");
|
||||
|
||||
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT))) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "received DISCONT");
|
||||
gst_ffmpegdec_flush_pcache (ffmpegdec);
|
||||
|
@ -1459,7 +1620,8 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DELTA_UNIT))
|
||||
goto skip_keyframe;
|
||||
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "got keyframe %"GST_TIME_FORMAT, GST_TIME_ARGS (in_ts));
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "got keyframe %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (in_ts));
|
||||
ffmpegdec->waiting_for_key = FALSE;
|
||||
}
|
||||
|
||||
|
@ -1471,16 +1633,9 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
|
||||
/* parse cache joining */
|
||||
if (ffmpegdec->pcache) {
|
||||
GstClockTime timestamp = GST_CLOCK_TIME_NONE;
|
||||
GstClockTime timestamp = GST_BUFFER_TIMESTAMP (ffmpegdec->pcache);
|
||||
GstClockTime duration = GST_CLOCK_TIME_NONE;
|
||||
|
||||
/* decide on resulting timestamp/duration before we give away our ref */
|
||||
/* since the cache is all data that did not result in an outgoing frame,
|
||||
* we should timestamp with the new incoming buffer. This is probably
|
||||
* not entirely correct though, but better than nothing. */
|
||||
if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf))
|
||||
timestamp = GST_BUFFER_TIMESTAMP (inbuf);
|
||||
|
||||
if (GST_BUFFER_DURATION_IS_VALID (ffmpegdec->pcache)
|
||||
&& GST_BUFFER_DURATION_IS_VALID (inbuf))
|
||||
duration = GST_BUFFER_DURATION (ffmpegdec->pcache) +
|
||||
|
@ -1491,12 +1646,17 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
/* update time info as appropriate */
|
||||
GST_BUFFER_TIMESTAMP (inbuf) = timestamp;
|
||||
GST_BUFFER_DURATION (inbuf) = duration;
|
||||
GST_LOG_OBJECT (ffmpegdec, "joined parse cache, inbuf now has ts %" GST_TIME_FORMAT
|
||||
" and duration %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
|
||||
GST_TIME_ARGS (duration));
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"joined parse cache, inbuf now has ts %" GST_TIME_FORMAT
|
||||
" and duration %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)), GST_TIME_ARGS (duration));
|
||||
ffmpegdec->pcache = NULL;
|
||||
bdata = GST_BUFFER_DATA (inbuf);
|
||||
bsize = GST_BUFFER_SIZE (inbuf);
|
||||
|
||||
/* If we're decoding something else, we'll use next_ts */
|
||||
next_ts = in_ts;
|
||||
in_ts = timestamp;
|
||||
}
|
||||
/* workarounds, functions write to buffers:
|
||||
* libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
|
||||
|
@ -1513,24 +1673,34 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
}
|
||||
|
||||
do {
|
||||
GST_LOG_OBJECT (ffmpegdec, "bdata:%p , bsize:%d", bdata, bsize);
|
||||
/* parse, if at all possible */
|
||||
if (ffmpegdec->pctx) {
|
||||
gint res;
|
||||
gint64 ffpts;
|
||||
|
||||
dump_parser_context (ffmpegdec->pctx, ffmpegdec->context);
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"Calling av_parser_parse with ts:%" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (in_ts));
|
||||
|
||||
ffpts = gst_ffmpeg_time_gst_to_ff (in_ts, ffmpegdec->context->time_base);
|
||||
res = av_parser_parse (ffmpegdec->pctx, ffmpegdec->context,
|
||||
&data, &size, bdata, bsize, ffpts, ffpts);
|
||||
|
||||
GST_LOG_OBJECT (ffmpegdec, "Parsed video frame, res=%d, size=%d",
|
||||
res, size);
|
||||
|
||||
in_ts = gst_ffmpeg_time_ff_to_gst (ffmpegdec->pctx->pts,
|
||||
ffmpegdec->context->time_base);
|
||||
|
||||
if (res == 0 || size == 0)
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"Parsed video frame, res=%d, size=%d, data=%p, in_ts:%"
|
||||
GST_TIME_FORMAT, res, size, data, GST_TIME_ARGS (in_ts));
|
||||
|
||||
dump_parser_context (ffmpegdec->pctx, ffmpegdec->context);
|
||||
|
||||
if (res == 0 || size == 0) {
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"parser returned null res or size, breaking");
|
||||
break;
|
||||
else {
|
||||
} else {
|
||||
bsize -= res;
|
||||
bdata += res;
|
||||
}
|
||||
|
@ -1540,7 +1710,7 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
}
|
||||
|
||||
if ((len = gst_ffmpegdec_frame (ffmpegdec, data, size,
|
||||
&have_data, &in_ts, inbuf, &ret)) < 0 || ret != GST_FLOW_OK)
|
||||
&have_data, inbuf, &ret)) < 0 || ret != GST_FLOW_OK)
|
||||
break;
|
||||
|
||||
if (!ffmpegdec->pctx) {
|
||||
|
@ -1549,18 +1719,28 @@ gst_ffmpegdec_chain (GstPad * pad, GstBuffer * inbuf)
|
|||
}
|
||||
|
||||
if (!have_data) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "Decoding didn't return any data, breaking");
|
||||
break;
|
||||
}
|
||||
in_ts = next_ts;
|
||||
GST_BUFFER_TIMESTAMP (inbuf) = in_ts;
|
||||
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",
|
||||
bsize, bdata);
|
||||
} while (bsize > 0);
|
||||
|
||||
/* keep left-over */
|
||||
if ((ffmpegdec->pctx || oclass->in_plugin->id == CODEC_ID_MP3) && bsize > 0) {
|
||||
GST_LOG_OBJECT (ffmpegdec, "Keeping %d bytes of data", bsize);
|
||||
GST_LOG_OBJECT (ffmpegdec,
|
||||
"Keeping %d bytes of data with timestamp %" GST_TIME_FORMAT, bsize,
|
||||
GST_TIME_ARGS (in_ts));
|
||||
|
||||
ffmpegdec->pcache = gst_buffer_create_sub (inbuf,
|
||||
GST_BUFFER_SIZE (inbuf) - bsize, bsize);
|
||||
/* we keep timestamp, even though all we really know is that the correct
|
||||
* timestamp is not below the one from inbuf */
|
||||
GST_BUFFER_TIMESTAMP (ffmpegdec->pcache) = GST_BUFFER_TIMESTAMP (inbuf);
|
||||
GST_BUFFER_TIMESTAMP (ffmpegdec->pcache) = in_ts;
|
||||
} else if (bsize > 0) {
|
||||
GST_DEBUG_OBJECT (ffmpegdec, "Dropping %d bytes of data", bsize);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue