mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
ext/ffmpeg/gstffmpegdec.c: Separate buffer handling from actual decoding, handle pending frames (for B-frames and so ...
Original commit message from CVS: * ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_frame), (gst_ffmpegdec_chain): Separate buffer handling from actual decoding, handle pending frames (for B-frames and so on) so we catch the last frames before EOS, prepare for some timestamp changes to make playback of movies with B-frames smoother (not done yet).
This commit is contained in:
parent
1b1044122b
commit
f29f652236
2 changed files with 133 additions and 106 deletions
12
ChangeLog
12
ChangeLog
|
@ -1,7 +1,15 @@
|
||||||
|
2005-01-19 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
|
||||||
|
* ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_frame),
|
||||||
|
(gst_ffmpegdec_chain):
|
||||||
|
Separate buffer handling from actual decoding, handle pending
|
||||||
|
frames (for B-frames and so on) so we catch the last frames
|
||||||
|
before EOS, prepare for some timestamp changes to make playback
|
||||||
|
of movies with B-frames smoother (not done yet).
|
||||||
|
|
||||||
2005-01-18 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
2005-01-18 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
|
||||||
* ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_init),
|
|
||||||
(gst_ffmpegdec_query), (gst_ffmpegdec_event), (gst_ffmpegdec_open),
|
|
||||||
(gst_ffmpegdec_negotiate), (gst_ffmpegdec_chain):
|
(gst_ffmpegdec_negotiate), (gst_ffmpegdec_chain):
|
||||||
* ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_init),
|
* ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_init),
|
||||||
(gst_ffmpegdemux_close), (gst_ffmpegdemux_src_query),
|
(gst_ffmpegdemux_close), (gst_ffmpegdemux_src_query),
|
||||||
|
|
|
@ -536,17 +536,125 @@ gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
|
||||||
|
guint8 * data, guint size, gint * got_data, guint64 * expected_ts)
|
||||||
|
{
|
||||||
|
GstFFMpegDecClass *oclass =
|
||||||
|
(GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||||
|
GstBuffer *outbuf = NULL;
|
||||||
|
gint have_data, len = 0;
|
||||||
|
|
||||||
|
ffmpegdec->context->frame_number++;
|
||||||
|
|
||||||
|
switch (oclass->in_plugin->type) {
|
||||||
|
case CODEC_TYPE_VIDEO:
|
||||||
|
len = avcodec_decode_video (ffmpegdec->context,
|
||||||
|
ffmpegdec->picture, &have_data, data, size);
|
||||||
|
GST_DEBUG ("Decode video: len=%d, have_data=%d", len, have_data);
|
||||||
|
|
||||||
|
if (len >= 0 && have_data > 0) {
|
||||||
|
/* libavcodec constantly crashes on stupid buffer allocation
|
||||||
|
* errors inside. This drives me crazy, so we let it allocate
|
||||||
|
* it's own buffers and copy to our own buffer afterwards... */
|
||||||
|
AVPicture pic;
|
||||||
|
gint fsize = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
|
||||||
|
ffmpegdec->context->width, ffmpegdec->context->height);
|
||||||
|
|
||||||
|
outbuf = gst_buffer_new_and_alloc (fsize);
|
||||||
|
|
||||||
|
/* original ffmpeg code does not handle odd sizes correctly.
|
||||||
|
* This patched up version does */
|
||||||
|
gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
|
||||||
|
ffmpegdec->context->pix_fmt,
|
||||||
|
ffmpegdec->context->width, ffmpegdec->context->height);
|
||||||
|
|
||||||
|
/* the original convert function did not do the right thing, this
|
||||||
|
* is a patched up version that adjust widht/height so that the
|
||||||
|
* ffmpeg one works correctly. */
|
||||||
|
gst_ffmpeg_img_convert (&pic, ffmpegdec->context->pix_fmt,
|
||||||
|
(AVPicture *) ffmpegdec->picture,
|
||||||
|
ffmpegdec->context->pix_fmt,
|
||||||
|
ffmpegdec->context->width,
|
||||||
|
ffmpegdec->context->height);
|
||||||
|
|
||||||
|
/* note that ffmpeg sometimes gets the FPS wrong */
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (*expected_ts) &&
|
||||||
|
ffmpegdec->context->frame_rate > 0) {
|
||||||
|
GST_BUFFER_TIMESTAMP (outbuf) = *expected_ts;
|
||||||
|
GST_BUFFER_DURATION (outbuf) = GST_SECOND *
|
||||||
|
ffmpegdec->context->frame_rate_base /
|
||||||
|
ffmpegdec->context->frame_rate;
|
||||||
|
*expected_ts += GST_BUFFER_DURATION (outbuf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gst_buffer_unref (outbuf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CODEC_TYPE_AUDIO:
|
||||||
|
outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||||
|
len = avcodec_decode_audio (ffmpegdec->context,
|
||||||
|
(int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
|
||||||
|
GST_DEBUG ("Decode audio: len=%d, have_data=%d", len, have_data);
|
||||||
|
|
||||||
|
if (len >= 0 && have_data > 0) {
|
||||||
|
GST_BUFFER_SIZE (outbuf) = have_data;
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (*expected_ts)) {
|
||||||
|
GST_BUFFER_TIMESTAMP (outbuf) = *expected_ts;
|
||||||
|
GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
|
||||||
|
(2 * ffmpegdec->context->channels *
|
||||||
|
ffmpegdec->context->sample_rate);
|
||||||
|
*expected_ts += GST_BUFFER_DURATION (outbuf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gst_buffer_unref (outbuf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert (0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < 0 || have_data < 0) {
|
||||||
|
GST_ERROR_OBJECT (ffmpegdec,
|
||||||
|
"ffdec_%s: decoding error (len: %d, have_data: %d)",
|
||||||
|
oclass->in_plugin->name, len, have_data);
|
||||||
|
*got_data = 0;
|
||||||
|
return len;
|
||||||
|
} else if (len == 0 && have_data == 0) {
|
||||||
|
*got_data = 0;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
*got_data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_data) {
|
||||||
|
GST_DEBUG ("Decoded data, now pushing");
|
||||||
|
|
||||||
|
if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
|
||||||
|
gst_buffer_unref (outbuf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GST_PAD_IS_USABLE (ffmpegdec->srcpad))
|
||||||
|
gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
|
||||||
|
else
|
||||||
|
gst_buffer_unref (outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
|
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
|
||||||
{
|
{
|
||||||
GstBuffer *inbuf = GST_BUFFER (_data);
|
GstBuffer *inbuf = GST_BUFFER (_data);
|
||||||
GstBuffer *outbuf = NULL;
|
|
||||||
GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
|
GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
|
||||||
GstFFMpegDecClass *oclass =
|
GstFFMpegDecClass *oclass =
|
||||||
(GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
(GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
|
||||||
guint8 *bdata, *data;
|
guint8 *bdata, *data;
|
||||||
gint bsize, size, len = 0;
|
gint bsize, size, len, have_data;
|
||||||
gint have_data;
|
|
||||||
guint64 expected_ts = GST_BUFFER_TIMESTAMP (inbuf);
|
guint64 expected_ts = GST_BUFFER_TIMESTAMP (inbuf);
|
||||||
|
|
||||||
if (!ffmpegdec->opened) {
|
if (!ffmpegdec->opened) {
|
||||||
|
@ -586,7 +694,10 @@ gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* parse, if at all possible */
|
/* parse, if at all possible */
|
||||||
if (ffmpegdec->pctx) {
|
if (bsize == 0) {
|
||||||
|
data = NULL;
|
||||||
|
size = 0;
|
||||||
|
} else if (ffmpegdec->pctx) {
|
||||||
gint res;
|
gint res;
|
||||||
|
|
||||||
res = av_parser_parse (ffmpegdec->pctx, ffmpegdec->context,
|
res = av_parser_parse (ffmpegdec->pctx, ffmpegdec->context,
|
||||||
|
@ -605,111 +716,19 @@ gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
|
||||||
size = bsize;
|
size = bsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
ffmpegdec->context->frame_number++;
|
if ((len = gst_ffmpegdec_frame (ffmpegdec, data, size,
|
||||||
|
&have_data, &expected_ts)) < 0)
|
||||||
switch (oclass->in_plugin->type) {
|
|
||||||
case CODEC_TYPE_VIDEO:
|
|
||||||
len = avcodec_decode_video (ffmpegdec->context,
|
|
||||||
ffmpegdec->picture, &have_data, data, size);
|
|
||||||
GST_DEBUG ("Decode video: len=%d, have_data=%d", len, have_data);
|
|
||||||
|
|
||||||
if (len >= 0 && have_data) {
|
|
||||||
/* libavcodec constantly crashes on stupid buffer allocation
|
|
||||||
* errors inside. This drives me crazy, so we let it allocate
|
|
||||||
* it's own buffers and copy to our own buffer afterwards... */
|
|
||||||
AVPicture pic;
|
|
||||||
gint fsize = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
|
|
||||||
ffmpegdec->context->width, ffmpegdec->context->height);
|
|
||||||
|
|
||||||
outbuf = gst_buffer_new_and_alloc (fsize);
|
|
||||||
/* original ffmpeg code does not handle odd sizes correctly. This patched
|
|
||||||
* up version does */
|
|
||||||
gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
|
|
||||||
ffmpegdec->context->pix_fmt,
|
|
||||||
ffmpegdec->context->width, ffmpegdec->context->height);
|
|
||||||
|
|
||||||
/* the original convert function did not do the right thing, this
|
|
||||||
* is a patched up version that adjust widht/height so that the
|
|
||||||
* ffmpeg one works correctly. */
|
|
||||||
gst_ffmpeg_img_convert (&pic, ffmpegdec->context->pix_fmt,
|
|
||||||
(AVPicture *) ffmpegdec->picture,
|
|
||||||
ffmpegdec->context->pix_fmt,
|
|
||||||
ffmpegdec->context->width,
|
|
||||||
ffmpegdec->context->height);
|
|
||||||
|
|
||||||
/* note that ffmpeg sometimes gets the FPS wrong */
|
|
||||||
if (GST_CLOCK_TIME_IS_VALID (expected_ts) &&
|
|
||||||
ffmpegdec->context->frame_rate > 0) {
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = expected_ts;
|
|
||||||
GST_BUFFER_DURATION (outbuf) = GST_SECOND *
|
|
||||||
ffmpegdec->context->frame_rate_base /
|
|
||||||
ffmpegdec->context->frame_rate;
|
|
||||||
} else {
|
|
||||||
GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CODEC_TYPE_AUDIO:
|
|
||||||
outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
|
||||||
len = avcodec_decode_audio (ffmpegdec->context,
|
|
||||||
(int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
|
|
||||||
GST_DEBUG ("Decode audio: len=%d, have_data=%d", len, have_data);
|
|
||||||
|
|
||||||
if (have_data < 0) {
|
|
||||||
GST_WARNING_OBJECT (ffmpegdec,
|
|
||||||
"FFmpeg error: len %d, have_data: %d < 0 !",
|
|
||||||
len, have_data);
|
|
||||||
gst_buffer_unref (inbuf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len >= 0 && have_data) {
|
|
||||||
GST_BUFFER_SIZE (outbuf) = have_data;
|
|
||||||
if (GST_CLOCK_TIME_IS_VALID (expected_ts)) {
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = expected_ts;
|
|
||||||
GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
|
|
||||||
(2 * ffmpegdec->context->channels *
|
|
||||||
ffmpegdec->context->sample_rate);
|
|
||||||
expected_ts += GST_BUFFER_DURATION (outbuf);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gst_buffer_unref (outbuf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
g_assert (0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 0) {
|
|
||||||
GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
|
|
||||||
oclass->in_plugin->name);
|
|
||||||
break;
|
break;
|
||||||
} else if (len == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (have_data) {
|
if (!ffmpegdec->pctx) {
|
||||||
GST_DEBUG ("Decoded data, now pushing");
|
|
||||||
|
|
||||||
if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
|
|
||||||
gst_buffer_unref (outbuf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GST_PAD_IS_USABLE (ffmpegdec->srcpad))
|
|
||||||
gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
|
|
||||||
else
|
|
||||||
gst_buffer_unref (outbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ffmpegdec->pctx || ffmpegdec->context->codec_id == CODEC_ID_MP3 ||
|
|
||||||
ffmpegdec->context->codec_id == CODEC_ID_MJPEG) {
|
|
||||||
bsize -= len;
|
bsize -= len;
|
||||||
bdata += len;
|
bdata += len;
|
||||||
}
|
}
|
||||||
} while (bsize > 0);
|
|
||||||
|
if (!have_data) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
|
||||||
if ((ffmpegdec->pctx || oclass->in_plugin->id == CODEC_ID_MP3) &&
|
if ((ffmpegdec->pctx || oclass->in_plugin->id == CODEC_ID_MP3) &&
|
||||||
bsize > 0) {
|
bsize > 0) {
|
||||||
|
|
Loading…
Reference in a new issue