summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>2003-11-12 10:42:36 +0000
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>2003-11-12 10:42:36 +0000
commit199e7f14f23335d02362ded89ba9b8829591e810 (patch)
treedf03298f5fa5f8719026d0e23cc9bb3c89d12571
parentd26aa5ad0b6e5443b9253b386e42bbd7d94b5c87 (diff)
downloadgst-libav-199e7f14f23335d02362ded89ba9b8829591e810.tar.gz
Several encoding changes: for video, double check the pix_fmt after opening the AVCodec. The pix_fmt will be changed ...
Original commit message from CVS: Several encoding changes: * for video, double check the pix_fmt after opening the AVCodec. The pix_fmt will be changed to the preferred pix_fmt instead of that opening the codec fails. We fail when it has changed (then, ffmpeg doesn't like it). * for video, set the correct timestamp. * for audio, do *not* set the frame_size: most codecs don't like it. Instead, keep a cache of buffers and always give data of size "frame_size". Maybe I should have used bytestream here, I'm not sure. This works, though. I can now create mpeg1 video, mp2 and ac3 audio. I didn't test any others yet. I also didn't start working on integration with any of the muxers yet, that's all one big TODO. One thing at a time, please. :).
-rw-r--r--ext/ffmpeg/gstffmpegcodecmap.c4
-rw-r--r--ext/ffmpeg/gstffmpegenc.c179
2 files changed, 130 insertions, 53 deletions
diff --git a/ext/ffmpeg/gstffmpegcodecmap.c b/ext/ffmpeg/gstffmpegcodecmap.c
index e3c88d0..c7ff98b 100644
--- a/ext/ffmpeg/gstffmpegcodecmap.c
+++ b/ext/ffmpeg/gstffmpegcodecmap.c
@@ -831,8 +831,8 @@ gst_ffmpeg_caps_to_pixfmt (GstCaps *caps,
GST_PROPS_FLOAT_TYPE)) {
gfloat fps;
gst_caps_get_float (caps, "framerate", &fps);
- context->frame_rate = fps * 1000;
- context->frame_rate_base = 1000;
+ context->frame_rate = fps * DEFAULT_FRAME_RATE_BASE;
+ context->frame_rate_base = DEFAULT_FRAME_RATE_BASE;
}
if (gst_caps_has_property_typed (caps, "format",
diff --git a/ext/ffmpeg/gstffmpegenc.c b/ext/ffmpeg/gstffmpegenc.c
index 1d0631e..c63e461 100644
--- a/ext/ffmpeg/gstffmpegenc.c
+++ b/ext/ffmpeg/gstffmpegenc.c
@@ -44,6 +44,7 @@ struct _GstFFMpegEnc {
AVCodecContext *context;
AVFrame *picture;
gboolean opened;
+ GstBuffer *cache;
/* cache */
gulong bitrate;
@@ -123,7 +124,8 @@ static void gst_ffmpegenc_dispose (GObject *object);
static GstPadLinkReturn
gst_ffmpegenc_connect (GstPad *pad, GstCaps *caps);
-static void gst_ffmpegenc_chain (GstPad *pad, GstData *_data);
+static void gst_ffmpegenc_chain_video (GstPad *pad, GstData *_data);
+static void gst_ffmpegenc_chain_audio (GstPad *pad, GstData *_data);
static void gst_ffmpegenc_set_property (GObject *object,
guint prop_id,
@@ -238,7 +240,6 @@ gst_ffmpegenc_init(GstFFMpegEnc *ffmpegenc)
/* setup pads */
ffmpegenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
gst_pad_set_link_function (ffmpegenc->sinkpad, gst_ffmpegenc_connect);
- gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain);
ffmpegenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->sinkpad);
@@ -248,12 +249,17 @@ gst_ffmpegenc_init(GstFFMpegEnc *ffmpegenc)
ffmpegenc->context = avcodec_alloc_context();
ffmpegenc->picture = avcodec_alloc_frame();
ffmpegenc->opened = FALSE;
+ ffmpegenc->cache = NULL;
if (oclass->in_plugin->type == CODEC_TYPE_VIDEO) {
+ gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_video);
+
ffmpegenc->bitrate = 300000;
ffmpegenc->buffer_size = 512 * 1024;
ffmpegenc->gop_size = 15;
} else if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) {
+ gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_audio);
+
ffmpegenc->bitrate = 128000;
}
}
@@ -309,25 +315,30 @@ gst_ffmpegenc_connect (GstPad *pad,
/* no edges */
ffmpegenc->context->flags |= CODEC_FLAG_EMU_EDGE;
- /* FIXME: we actually need to request the framerate
- * from the previous element - we currently use the
- * default (25.0), which is just plain wrong */
- ffmpegenc->context->frame_rate = 25 * DEFAULT_FRAME_RATE_BASE;
- ffmpegenc->context->frame_rate_base = DEFAULT_FRAME_RATE_BASE;
-
for (ret_caps = caps; ret_caps != NULL; ret_caps = ret_caps->next) {
+ enum PixelFormat pix_fmt;
+
/* fetch pix_fmt and so on */
gst_ffmpeg_caps_to_codectype (oclass->in_plugin->type,
caps, ffmpegenc->context);
+ pix_fmt = ffmpegenc->context->pix_fmt;
+
/* open codec */
if (avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) {
- GST_DEBUG (
- "ffenc_%s: Failed to open FFMPEG codec",
+ GST_DEBUG ("ffenc_%s: Failed to open FFMPEG codec",
oclass->in_plugin->name);
continue;
}
+ /* is the colourspace correct? */
+ if (pix_fmt != ffmpegenc->context->pix_fmt) {
+ avcodec_close (ffmpegenc->context);
+ GST_DEBUG ("ffenc_%s: AV wants different colourspace (%d given, %d wanted)",
+ oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt);
+ continue;
+ }
+
break;
}
@@ -340,15 +351,13 @@ gst_ffmpegenc_connect (GstPad *pad,
ffmpegenc->context);
if (!ret_caps) {
avcodec_close (ffmpegenc->context);
- GST_DEBUG (
- "Unsupported codec - no caps found");
+ GST_DEBUG ("Unsupported codec - no caps found");
return GST_PAD_LINK_REFUSED;
}
if ((ret = gst_pad_try_set_caps (ffmpegenc->srcpad, ret_caps)) <= 0) {
avcodec_close (ffmpegenc->context);
- GST_DEBUG (
- "Failed to set caps on next element for ffmpeg encoder (%s)",
+ GST_DEBUG ("Failed to set caps on next element for ffmpeg encoder (%s)",
oclass->in_plugin->name);
return ret;
}
@@ -360,50 +369,28 @@ gst_ffmpegenc_connect (GstPad *pad,
}
static void
-gst_ffmpegenc_chain (GstPad *pad,
- GstData *_data)
+gst_ffmpegenc_chain_video (GstPad *pad,
+ GstData *_data)
{
GstBuffer *inbuf = GST_BUFFER (_data);
GstBuffer *outbuf = NULL;
GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *)(gst_pad_get_parent (pad));
GstFFMpegEncClass *oclass = (GstFFMpegEncClass*)(G_OBJECT_GET_CLASS(ffmpegenc));
- gpointer data;
- gint size, ret_size = 0;
-
- data = GST_BUFFER_DATA (inbuf);
- size = GST_BUFFER_SIZE (inbuf);
+ gint ret_size = 0;
/* FIXME: events (discont (flush!) and eos (close down) etc.) */
- switch (oclass->in_plugin->type) {
- case CODEC_TYPE_VIDEO:
- outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
- avpicture_fill ((AVPicture *) ffmpegenc->picture,
- GST_BUFFER_DATA (inbuf),
- ffmpegenc->context->pix_fmt,
- ffmpegenc->context->width,
- ffmpegenc->context->height);
- ret_size = avcodec_encode_video (ffmpegenc->context,
- GST_BUFFER_DATA (outbuf),
- GST_BUFFER_MAXSIZE (outbuf),
- ffmpegenc->picture);
- break;
-
- case CODEC_TYPE_AUDIO:
- ffmpegenc->context->frame_size = GST_BUFFER_SIZE (inbuf) /
- (2 * ffmpegenc->context->channels);
- outbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (inbuf));
- ret_size = avcodec_encode_audio (ffmpegenc->context,
- GST_BUFFER_DATA (outbuf),
- GST_BUFFER_MAXSIZE (outbuf),
- (const short int *)
- GST_BUFFER_DATA (inbuf));
- break;
-
- default:
- g_assert(0);
- break;
- }
+ outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
+ avpicture_fill ((AVPicture *) ffmpegenc->picture,
+ GST_BUFFER_DATA (inbuf),
+ ffmpegenc->context->pix_fmt,
+ ffmpegenc->context->width,
+ ffmpegenc->context->height);
+ ffmpegenc->picture->pts = GST_BUFFER_TIMESTAMP (inbuf) / 1000;
+ ret_size = avcodec_encode_video (ffmpegenc->context,
+ GST_BUFFER_DATA (outbuf),
+ GST_BUFFER_MAXSIZE (outbuf),
+ ffmpegenc->picture);
if (ret_size < 0) {
g_warning("ffenc_%s: failed to encode buffer",
@@ -412,7 +399,6 @@ gst_ffmpegenc_chain (GstPad *pad,
return;
}
- /* bla */
GST_BUFFER_SIZE (outbuf) = ret_size;
GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
@@ -422,6 +408,93 @@ gst_ffmpegenc_chain (GstPad *pad,
}
static void
+gst_ffmpegenc_chain_audio (GstPad *pad,
+ GstData *_data)
+{
+ GstBuffer *inbuf = GST_BUFFER (_data);
+ GstBuffer *outbuf = NULL, *subbuf;
+ GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *)(gst_pad_get_parent (pad));
+ GstFFMpegEncClass *oclass = (GstFFMpegEncClass*)(G_OBJECT_GET_CLASS(ffmpegenc));
+ gint size, ret_size = 0, in_size, frame_size;
+
+ size = GST_BUFFER_SIZE (inbuf);
+
+ /* FIXME: events (discont (flush!) and eos (close down) etc.) */
+
+ frame_size = ffmpegenc->context->frame_size * 2 *
+ ffmpegenc->context->channels;
+ in_size = size;
+ if (ffmpegenc->cache)
+ in_size += GST_BUFFER_SIZE (ffmpegenc->cache);
+
+ while (1) {
+ /* do we have enough data for one frame? */
+ if (in_size / (2 * ffmpegenc->context->channels) <
+ ffmpegenc->context->frame_size) {
+ if (in_size > size) {
+ /* this is panic! we got a buffer, but still don't have enough
+ * data. Merge them and retry in the next cycle... */
+ ffmpegenc->cache = gst_buffer_merge (ffmpegenc->cache, inbuf);
+ } else if (in_size == size) {
+ /* exactly the same! how wonderful */
+ ffmpegenc->cache = inbuf;
+ } else if (in_size > 0) {
+ ffmpegenc->cache = gst_buffer_create_sub (inbuf, size - in_size,
+ in_size);
+ GST_BUFFER_DURATION (ffmpegenc->cache) =
+ GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (ffmpegenc->cache) / size;
+ GST_BUFFER_TIMESTAMP (ffmpegenc->cache) =
+ GST_BUFFER_TIMESTAMP (inbuf) + (GST_BUFFER_DURATION (inbuf) *
+ (size - in_size) / size);
+ gst_buffer_unref (inbuf);
+ } else {
+ gst_buffer_unref (inbuf);
+ }
+
+ return;
+ }
+
+ /* create the frame */
+ if (in_size > size) {
+ /* merge */
+ subbuf = gst_buffer_create_sub (inbuf, 0, frame_size - (in_size - size));
+ GST_BUFFER_DURATION (subbuf) =
+ GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
+ subbuf = gst_buffer_merge (ffmpegenc->cache, subbuf);
+ ffmpegenc->cache = NULL;
+ } else {
+ subbuf = gst_buffer_create_sub (inbuf, size - in_size, frame_size);
+ GST_BUFFER_DURATION (subbuf) =
+ GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
+ GST_BUFFER_TIMESTAMP (subbuf) =
+ GST_BUFFER_TIMESTAMP (inbuf) + (GST_BUFFER_DURATION (inbuf) *
+ (size - in_size) / size);
+ }
+
+ outbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (inbuf));
+ ret_size = avcodec_encode_audio (ffmpegenc->context,
+ GST_BUFFER_DATA (outbuf),
+ GST_BUFFER_MAXSIZE (outbuf),
+ (const short int *)
+ GST_BUFFER_DATA (subbuf));
+
+ if (ret_size < 0) {
+ g_warning("ffenc_%s: failed to encode buffer",
+ oclass->in_plugin->name);
+ gst_buffer_unref (inbuf);
+ return;
+ }
+
+ GST_BUFFER_SIZE (outbuf) = ret_size;
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (subbuf);
+ GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (subbuf);
+ gst_pad_push (ffmpegenc->srcpad, GST_DATA (outbuf));
+
+ in_size -= frame_size;
+ }
+}
+
+static void
gst_ffmpegenc_set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -495,6 +568,10 @@ gst_ffmpegenc_change_state (GstElement *element)
avcodec_close (ffmpegenc->context);
ffmpegenc->opened = FALSE;
}
+ if (ffmpegenc->cache) {
+ gst_buffer_unref (ffmpegenc->cache);
+ ffmpegenc->cache = NULL;
+ }
break;
}