diff options
author | Mark Nauwelaerts <manauw@skynet.be> | 2006-07-13 11:06:45 +0000 |
---|---|---|
committer | Tim-Philipp Müller <tim@centricular.net> | 2006-07-13 11:06:45 +0000 |
commit | 676eafc77c2799521925d54ba43b26df72c76b14 (patch) | |
tree | de46f5bf8f22a83cd7a55fa1cdb26507986e22fd /ext/mpeg2enc/gstmpeg2enc.cc | |
parent | 0e9af4401ec81f82b3b4d7f548a96b2585f1e0e1 (diff) | |
download | gst-plugins-bad-676eafc77c2799521925d54ba43b26df72c76b14.tar.gz gst-plugins-bad-676eafc77c2799521925d54ba43b26df72c76b14.tar.bz2 gst-plugins-bad-676eafc77c2799521925d54ba43b26df72c76b14.zip |
Port mpeg2enc to 0.10 (#343184).
Original commit message from CVS:
Patch by: Mark Nauwelaerts <manauw at skynet be>
* configure.ac:
* ext/Makefile.am:
* ext/mpeg2enc/Makefile.am:
* ext/mpeg2enc/gstmpeg2enc.cc:
* ext/mpeg2enc/gstmpeg2enc.hh:
* ext/mpeg2enc/gstmpeg2encoder.cc:
* ext/mpeg2enc/gstmpeg2encoder.hh:
* ext/mpeg2enc/gstmpeg2encoptions.cc:
* ext/mpeg2enc/gstmpeg2encpicturereader.cc:
* ext/mpeg2enc/gstmpeg2encpicturereader.hh:
* ext/mpeg2enc/gstmpeg2encstreamwriter.cc:
* ext/mpeg2enc/gstmpeg2encstreamwriter.hh:
Port mpeg2enc to 0.10 (#343184).
* tests/check/Makefile.am:
* tests/check/elements/.cvsignore:
* tests/check/elements/mpeg2enc.c:
Add unit test for mpeg2enc.
* tests/icles/.cvsignore:
Ignore pitch-test.
Diffstat (limited to 'ext/mpeg2enc/gstmpeg2enc.cc')
-rw-r--r-- | ext/mpeg2enc/gstmpeg2enc.cc | 738 |
1 files changed, 558 insertions, 180 deletions
diff --git a/ext/mpeg2enc/gstmpeg2enc.cc b/ext/mpeg2enc/gstmpeg2enc.cc index d53c28f4..b91d9426 100644 --- a/ext/mpeg2enc/gstmpeg2enc.cc +++ b/ext/mpeg2enc/gstmpeg2enc.cc @@ -1,7 +1,8 @@ /* GStreamer mpeg2enc (mjpegtools) wrapper * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> + * (c) 2006 Mark Nauwelaerts <manauw@skynet.be> * - * gstmpeg2enc.cc: gstreamer wrapping + * gstmpeg2enc.cc: gstreamer mpeg2enc wrapping * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -19,91 +20,92 @@ * Boston, MA 02111-1307, USA. */ +/** + * SECTION:element-mpeg2enc + * @see_also: mpeg2dec + * + * <refsect2> + * <para> + * This element encodes raw video into an MPEG ?? stream using the + * <ulink url="http://mjpeg.sourceforge.net/">mjpegtools</ulink> library. + * Documentation on MPEG encoding in general can be found in the + * <ulink url="https://sourceforge.net/docman/display_doc.php?docid=3456&group_id=5776#s7">MJPEG Howto</ulink> + * and on the various available parameters in the documentation + * of the mpeg2enc tool in particular, which shares options with this element. + * </para> + * <title>Example pipeline</title> + * <para> + * <programlisting> + * gst-launch-0.10 videotestsrc num-buffers=1000 ! mpeg2enc ! filesink location=videotestsrc.m1v + * </programlisting> + * This example pipeline will encode a test video source to a an + * MPEG1 elementary stream (with Generic MPEG1 profile). + * </para> + * <para> + * Likely, the <link linkend="GstMpeg2enc--format">format</link> property + * is most important, as it selects the type of MPEG stream that is produced. + * In particular, default property values are dependent on the format, + * and can even be forcibly restrained to certain pre-sets (and thereby ignored). + * Note that the (S)VCD profiles also restrict the image size, so some scaling + * may be needed to accomodate this. The so-called generic profiles (as used + * in the example above) allow most parameters to be adjusted. + * <programlisting> + * gst-launch-0.10 videotestsrc num-buffers=1000 ! videoscale \ + * ! mpeg2enc format=1 norm=p ! filesink location=videotestsrc.m1v + * </programlisting> + * (write everything in one line, without the backslash characters) + * This will produce an MPEG1 profile stream according to VCD2.0 specifications + * for PAL <link linkend="GstMpeg2enc--norm">norm</link> (as the image height + * is dependent on video norm). + * </para> + * </refsect2> + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstmpeg2enc.hh" -/* - * We can't use fractions in static pad templates, so - * we do something manual... - */ -static void -add_fps (GstCaps * caps) -{ - GstStructure *structure = gst_caps_get_structure (caps, 0); - GValue list = { 0 }, fps = { - 0}; - gdouble fpss[] = { 24.0 / 1.001, 24.0, 25.0, - 30.0 / 1.001, 30.0, 50.0, - 60.0 / 1.001, 60.0, 0 - }; - guint n; - - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&fps, G_TYPE_DOUBLE); - for (n = 0; fpss[n] != 0; n++) { - g_value_set_double (&fps, fpss[n]); - gst_value_list_append_value (&list, &fps); - } - gst_structure_set_value (structure, "framerate", &list); - g_value_unset (&list); - g_value_unset (&fps); -} - -static GstPadTemplate * -sink_templ (void) -{ - static GstPadTemplate *templ = NULL; - - if (!templ) { - GstCaps *caps; - - caps = gst_caps_new_simple ("video/x-raw-yuv", - "format", GST_TYPE_FOURCC, - GST_MAKE_FOURCC ('I', '4', '2', '0'), - "width", GST_TYPE_INT_RANGE, 16, 4096, - "height", GST_TYPE_INT_RANGE, 16, 4096, NULL); - add_fps (caps); - - templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); - } - - return templ; -} - -static GstPadTemplate * -src_templ (void) -{ - static GstPadTemplate *templ = NULL; - - if (!templ) { - GstCaps *caps; - - caps = gst_caps_new_simple ("video/mpeg", - "systemstream", G_TYPE_BOOLEAN, FALSE, - "mpegversion", GST_TYPE_INT_RANGE, 1, 2, - "width", GST_TYPE_INT_RANGE, 16, 4096, - "height", GST_TYPE_INT_RANGE, 16, 4096, NULL); - add_fps (caps); - - templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps); - } - - return templ; -} - -static void gst_mpeg2enc_base_init (GstMpeg2encClass * klass); -static void gst_mpeg2enc_class_init (GstMpeg2encClass * klass); -static void gst_mpeg2enc_init (GstMpeg2enc * enc); -static void gst_mpeg2enc_dispose (GObject * object); - -static void gst_mpeg2enc_loop (GstElement * element); - -static GstPadLinkReturn -gst_mpeg2enc_sink_link (GstPad * pad, const GstCaps * caps); +GST_DEBUG_CATEGORY (mpeg2enc_debug); +static GstElementDetails gst_mpeg2enc_details = +GST_ELEMENT_DETAILS ("mpeg2enc video encoder", + "Codec/Encoder/Video", + "High-quality MPEG-1/2 video encoder", + "Andrew Stevens <andrew.stevens@nexgo.de>\n" + "Ronald Bultje <rbultje@ronald.bitfreak.net>"); + +#define COMMON_VIDEO_CAPS \ + "width = (int) [ 16, 4096 ], " \ + "height = (int) [ 16, 4096 ], " \ + "framerate = " \ + " (fraction) { 24000/1001, 24/1, 25/1, 30000/1001, 30/1, 50/1, 60000/1001 }" + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv, " + "format = (fourcc) { I420 }, " COMMON_VIDEO_CAPS) + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg, " + "systemstream = (boolean) false, " + "mpegversion = (int) { 1, 2 }, " COMMON_VIDEO_CAPS) + ); + + +static void gst_mpeg2enc_finalize (GObject * object); +static void gst_mpeg2enc_reset (GstMpeg2enc * enc); +static gboolean gst_mpeg2enc_setcaps (GstPad * pad, GstCaps * caps); +static GstCaps *gst_mpeg2enc_getcaps (GstPad * pad); +static gboolean gst_mpeg2enc_sink_event (GstPad * pad, GstEvent * event); +static void gst_mpeg2enc_loop (GstMpeg2enc * enc); +static GstFlowReturn gst_mpeg2enc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_mpeg2enc_src_activate_push (GstPad * pad, gboolean active); static GstStateChangeReturn gst_mpeg2enc_change_state (GstElement * element, GstStateChange transition); @@ -112,49 +114,19 @@ static void gst_mpeg2enc_get_property (GObject * object, static void gst_mpeg2enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static GstElementClass *parent_class = NULL; - -GType -gst_mpeg2enc_get_type (void) -{ - static GType gst_mpeg2enc_type = 0; - - if (!gst_mpeg2enc_type) { - static const GTypeInfo gst_mpeg2enc_info = { - sizeof (GstMpeg2encClass), - (GBaseInitFunc) gst_mpeg2enc_base_init, - NULL, - (GClassInitFunc) gst_mpeg2enc_class_init, - NULL, - NULL, - sizeof (GstMpeg2enc), - 0, - (GInstanceInitFunc) gst_mpeg2enc_init, - }; - - gst_mpeg2enc_type = - g_type_register_static (GST_TYPE_ELEMENT, - "GstMpeg2enc", &gst_mpeg2enc_info, (GTypeFlags) 0); - } - - return gst_mpeg2enc_type; -} +GST_BOILERPLATE (GstMpeg2enc, gst_mpeg2enc, GstElement, GST_TYPE_ELEMENT); static void -gst_mpeg2enc_base_init (GstMpeg2encClass * klass) +gst_mpeg2enc_base_init (gpointer klass) { - static GstElementDetails gst_mpeg2enc_details = { - "mpeg2enc video encoder", - "Codec/Encoder/Video", - "High-quality MPEG-1/2 video encoder", - "Andrew Stevens <andrew.stevens@nexgo.de>\n" - "Ronald Bultje <rbultje@ronald.bitfreak.net>" - }; GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - gst_element_class_add_pad_template (element_class, src_templ ()); - gst_element_class_add_pad_template (element_class, sink_templ ()); gst_element_class_set_details (element_class, &gst_mpeg2enc_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); } static void @@ -163,22 +135,21 @@ gst_mpeg2enc_class_init (GstMpeg2encClass * klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - parent_class = GST_ELEMENT_CLASS (g_type_class_ref (GST_TYPE_ELEMENT)); - - /* register arguments */ - mjpeg_default_handler_verbosity (0); - GstMpeg2EncOptions::initProperties (object_class); + GST_DEBUG_CATEGORY_INIT (mpeg2enc_debug, "mpeg2enc", 0, "MPEG1/2 encoder"); object_class->set_property = gst_mpeg2enc_set_property; object_class->get_property = gst_mpeg2enc_get_property; - object_class->dispose = gst_mpeg2enc_dispose; + /* register properties */ + GstMpeg2EncOptions::initProperties (object_class); + + object_class->finalize = GST_DEBUG_FUNCPTR (gst_mpeg2enc_finalize); - element_class->change_state = gst_mpeg2enc_change_state; + element_class->change_state = GST_DEBUG_FUNCPTR (gst_mpeg2enc_change_state); } static void -gst_mpeg2enc_dispose (GObject * object) +gst_mpeg2enc_finalize (GObject * object) { GstMpeg2enc *enc = GST_MPEG2ENC (object); @@ -189,96 +160,431 @@ gst_mpeg2enc_dispose (GObject * object) } delete enc->options; - G_OBJECT_CLASS (parent_class)->dispose (object); + g_mutex_free (enc->tlock); + g_cond_free (enc->cond); + + G_OBJECT_CLASS (parent_class)->finalize (object); } static void -gst_mpeg2enc_init (GstMpeg2enc * enc) +gst_mpeg2enc_init (GstMpeg2enc * enc, GstMpeg2encClass * g_class) { GstElement *element = GST_ELEMENT (enc); - GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); - - GST_FLAG_SET (element, GST_ELEMENT_EVENT_AWARE); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); enc->sinkpad = - gst_pad_new_from_template (gst_element_class_get_pad_template (klass, - "sink"), "sink"); - gst_pad_set_link_function (enc->sinkpad, gst_mpeg2enc_sink_link); + gst_pad_new_from_template (gst_element_class_get_pad_template + (element_class, "sink"), "sink"); + gst_pad_set_setcaps_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_mpeg2enc_setcaps)); + gst_pad_set_getcaps_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_mpeg2enc_getcaps)); + gst_pad_set_event_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_mpeg2enc_sink_event)); + gst_pad_set_chain_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_mpeg2enc_chain)); gst_element_add_pad (element, enc->sinkpad); enc->srcpad = - gst_pad_new_from_template (gst_element_class_get_pad_template (klass, - "src"), "src"); - gst_pad_use_explicit_caps (enc->srcpad); + gst_pad_new_from_template (gst_element_class_get_pad_template + (element_class, "src"), "src"); + gst_pad_use_fixed_caps (enc->srcpad); + gst_pad_set_activatepush_function (enc->srcpad, + GST_DEBUG_FUNCPTR (gst_mpeg2enc_src_activate_push)); gst_element_add_pad (element, enc->srcpad); enc->options = new GstMpeg2EncOptions (); + enc->encoder = NULL; - gst_element_set_loop_function (element, gst_mpeg2enc_loop); + enc->buffer = NULL; + enc->tlock = g_mutex_new (); + enc->cond = g_cond_new (); - enc->encoder = NULL; + gst_mpeg2enc_reset (enc); } static void -gst_mpeg2enc_loop (GstElement * element) +gst_mpeg2enc_reset (GstMpeg2enc * enc) { - GstMpeg2enc *enc = GST_MPEG2ENC (element); + enc->eos = FALSE; + enc->srcresult = GST_FLOW_OK; - if (!enc->encoder) { - const GstCaps *caps; - GstCaps *othercaps; - GstData *data; - - /* make sure we've had data */ - data = gst_pad_pull (enc->sinkpad); - /* forward any events */ - if (GST_IS_EVENT (data)) { - gst_pad_event_default (enc->sinkpad, GST_EVENT (data)); - return; - } + /* in case of error'ed ending */ + if (enc->buffer) + gst_buffer_unref (enc->buffer); + enc->buffer = NULL; + + if (enc->encoder) { + delete enc->encoder; + + enc->encoder = NULL; + } +} - gst_pad_set_element_private (enc->sinkpad, data); +/* some (!) coding to get caps depending on the video norm and chosen format */ +static void +gst_mpeg2enc_add_fps (GstStructure * structure, gint fpss[]) +{ + GValue list = { 0, }, fps = { + 0,}; + guint n; - if (!(caps = GST_PAD_CAPS (enc->sinkpad))) { - GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL), - ("format wasn't negotiated before loop function")); - return; + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&fps, GST_TYPE_FRACTION); + for (n = 0; fpss[n] != 0; n++) { + gst_value_set_fraction (&fps, fpss[n], fpss[n + 1]); + gst_value_list_append_value (&list, &fps); + n++; + } + gst_structure_set_value (structure, "framerate", &list); + g_value_unset (&list); + g_value_unset (&fps); +} + +static inline gint * +gst_mpeg2enc_get_fps (GstMpeg2enc * enc) +{ + static gint fps_pal[] + = { 24, 1, 25, 1, 50, 1, 0 }; + static gint fps_ntsc[] + = { 24000, 1001, 24, 1, 30000, 1001, 30, 1, 60000, 1001, 0 }; + static gint fps_all[] + = { 24000, 1001, 24, 1, 30000, 1001, 30, 1, 60000, 1001, 25, 1, 50, 1, 0 }; + + if (enc->options->norm == 'n') + return fps_ntsc; + else if (enc->options->norm == 0) + return fps_all; + else + return fps_pal; +} + +static GstStructure * +gst_mpeg2enc_structure_from_norm (GstMpeg2enc * enc, gint horiz, + gint pal_v, gint ntsc_v) +{ + GstStructure *structure; + + structure = gst_structure_new ("video/x-raw-yuv", + "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL); + + switch (enc->options->norm) { + case 0: + { + GValue list = { 0, } + , val = { + 0,}; + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_INT); + g_value_set_int (&val, pal_v); + gst_value_list_append_value (&list, &val); + g_value_set_int (&val, ntsc_v); + gst_value_list_append_value (&list, &val); + gst_structure_set_value (structure, "height", &list); + g_value_unset (&list); + g_value_unset (&val); + break; } + case 'n': + gst_structure_set (structure, "height", G_TYPE_INT, ntsc_v, NULL); + break; + default: + gst_structure_set (structure, "height", G_TYPE_INT, pal_v, NULL); + break; + } + gst_structure_set (structure, "width", G_TYPE_INT, horiz, NULL); + gst_mpeg2enc_add_fps (structure, gst_mpeg2enc_get_fps (enc)); + + return structure; +} + +static GstCaps * +gst_mpeg2enc_getcaps (GstPad * pad) +{ + GstMpeg2enc *enc = GST_MPEG2ENC (GST_PAD_PARENT (pad)); + GstCaps *caps; + + caps = GST_PAD_CAPS (pad); + if (caps) { + gst_caps_ref (caps); + return caps; + } + + switch (enc->options->format) { + case 1: /* vcd */ + case 2: /* user vcd */ + caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc, + 352, 288, 240), NULL); + break; + case 4: /* svcd */ + case 5: /* user svcd */ + caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc, + 480, 576, 480), NULL); + break; + case 6: /* vcd stills */ + /* low resolution */ + caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc, + 352, 288, 240), NULL); + /* high resolution */ + gst_caps_append_structure (caps, + gst_mpeg2enc_structure_from_norm (enc, 704, 576, 480)); + break; + case 7: /* svcd stills */ + /* low resolution */ + caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc, + 480, 576, 480), NULL); + /* high resolution */ + gst_caps_append_structure (caps, + gst_mpeg2enc_structure_from_norm (enc, 704, 576, 480)); + break; + case 0: + case 3: + case 8: + case 9: + default: + caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + gst_mpeg2enc_add_fps (gst_caps_get_structure (caps, 0), + gst_mpeg2enc_get_fps (enc)); + break; + } - /* create new encoder with these settings */ - enc->encoder = new GstMpeg2Encoder (enc->options, enc->sinkpad, - caps, enc->srcpad); + GST_DEBUG_OBJECT (enc, "returned caps %" GST_PTR_FORMAT, caps); + return caps; +} - /* and set caps on other side */ - othercaps = enc->encoder->getFormat (); - if (gst_pad_set_explicit_caps (enc->srcpad, othercaps) <= 0) { - GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL), (NULL)); +static gboolean +gst_mpeg2enc_setcaps (GstPad * pad, GstCaps * caps) +{ + GstMpeg2enc *enc = GST_MPEG2ENC (GST_PAD_PARENT (pad)); + GstCaps *othercaps = NULL, *mycaps; + gboolean ret; + + /* does not go well to restart stream mid-way */ + if (enc->encoder) + goto refuse_renegotiation; + + /* since mpeg encoder does not really check, let's check caps */ + mycaps = gst_pad_get_caps (pad); + othercaps = gst_caps_intersect (caps, mycaps); + gst_caps_unref (mycaps); + if (!othercaps || gst_caps_is_empty (othercaps)) + goto refuse_caps; + gst_caps_unref (othercaps); + othercaps = NULL; + + /* create new encoder with these settings */ + enc->encoder = new GstMpeg2Encoder (enc->options, GST_ELEMENT (enc), caps); + + if (!enc->encoder->setup ()) + goto refuse_caps; + + /* and set caps on other side, which should accept anyway */ + othercaps = enc->encoder->getFormat (); + ret = gst_pad_set_caps (enc->srcpad, othercaps); + gst_caps_unref (othercaps); + othercaps = NULL; + if (!ret) + goto refuse_caps; + + /* now that we have all the setup and buffers are expected incoming; + * task can get going */ + gst_pad_start_task (enc->srcpad, (GstTaskFunction) gst_mpeg2enc_loop, enc); + + return TRUE; + +refuse_caps: + { + GST_WARNING_OBJECT (enc, "refused caps %" GST_PTR_FORMAT, caps); + + if (othercaps) + gst_caps_unref (othercaps); + + if (enc->encoder) { delete enc->encoder; enc->encoder = NULL; - return; } + + return FALSE; } +refuse_renegotiation: + { + GST_WARNING_OBJECT (enc, "refused renegotiation (to %" GST_PTR_FORMAT ")", + caps); - enc->encoder->encodePicture (); - gst_pad_event_default (enc->sinkpad, gst_event_new (GST_EVENT_EOS)); + return FALSE; + } } -static GstPadLinkReturn -gst_mpeg2enc_sink_link (GstPad * pad, const GstCaps * caps) +static gboolean +gst_mpeg2enc_sink_event (GstPad * pad, GstEvent * event) { - GstMpeg2enc *enc = GST_MPEG2ENC (gst_pad_get_parent (pad)); + GstMpeg2enc *enc; + gboolean result = TRUE; + + enc = GST_MPEG2ENC (GST_PAD_PARENT (pad)); - if (!gst_caps_is_fixed (caps)) - return GST_PAD_LINK_DELAYED; + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + /* forward event */ + result = gst_pad_push_event (enc->srcpad, event); + + /* no special action as there is not much to flush; + * neither is it possible to halt the mpeg encoding loop */ + goto done; + break; + case GST_EVENT_FLUSH_STOP: + /* forward event */ + result = gst_pad_push_event (enc->srcpad, event); + if (!result) + goto done; + + /* this clears the error state in case of a failure in encoding task; + * so chain function can carry on again */ + GST_MPEG2ENC_MUTEX_LOCK (enc); + enc->srcresult = GST_FLOW_OK; + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + goto done; + break; + case GST_EVENT_EOS: + /* inform the encoding task that it can stop now */ + GST_MPEG2ENC_MUTEX_LOCK (enc); + enc->eos = TRUE; + GST_MPEG2ENC_SIGNAL (enc); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + + /* eat this event for now, task will send eos when finished */ + gst_event_unref (event); + goto done; + break; + default: + /* for a serialized event, wait until an earlier buffer is gone, + * though this is no guarantee as to when the encoder is done with it */ + if (GST_EVENT_IS_SERIALIZED (event)) { + GST_MPEG2ENC_MUTEX_LOCK (enc); + while (enc->buffer) + GST_MPEG2ENC_WAIT (enc); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + } + break; + } + + result = gst_pad_push_event (enc->srcpad, event); + +done: + return result; +} + +static void +gst_mpeg2enc_loop (GstMpeg2enc * enc) +{ + /* do not try to resume or start when output problems; + * also ensures a proper (forced) state change */ + if (enc->srcresult != GST_FLOW_OK) + goto ignore; if (enc->encoder) { - delete enc->encoder; + /* note that init performs a pre-fill and therefore needs buffers */ + enc->encoder->init (); + /* task will stay in here during all of the encoding */ + enc->encoder->encode (); + + /* if not well and truly eos, something strange happened */ + if (!enc->eos) { + GST_ERROR_OBJECT (enc, "encoding task ended without being eos"); + /* notify the chain function that it's over */ + GST_MPEG2ENC_MUTEX_LOCK (enc); + enc->srcresult = GST_FLOW_ERROR; + GST_MPEG2ENC_SIGNAL (enc); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + } else { + /* send eos if this was not a forced stop or other problem */ + if (enc->srcresult == GST_FLOW_OK) + gst_pad_push_event (enc->srcpad, gst_event_new_eos ()); + goto eos; + } + } else { + GST_WARNING_OBJECT (enc, "task started without Mpeg2Encoder"); + } - enc->encoder = NULL; + /* fall-through */ +done: + { + /* no need to run wildly, stopped elsewhere, e.g. state change */ + GST_DEBUG_OBJECT (enc, "pausing encoding task"); + gst_pad_pause_task (enc->srcpad); + + return; + } +eos: + { + GST_DEBUG_OBJECT (enc, "encoding task reached eos"); + goto done; } +ignore: + { + GST_DEBUG_OBJECT (enc, "not looping because encoding task encountered %s", + gst_flow_get_name (enc->srcresult)); + goto done; + } +} - return GST_PAD_LINK_OK; +static GstFlowReturn +gst_mpeg2enc_chain (GstPad * pad, GstBuffer * buffer) +{ + GstMpeg2enc *enc; + + enc = GST_MPEG2ENC (GST_PAD_PARENT (pad)); + + if (G_UNLIKELY (!enc->encoder)) + goto not_negotiated; + + GST_MPEG2ENC_MUTEX_LOCK (enc); + + if (G_UNLIKELY (enc->eos)) + goto eos; + + if (G_UNLIKELY (enc->srcresult != GST_FLOW_OK)) + goto ignore; + + /* things look good, now inform the encoding task that a buffer is ready */ + while (enc->buffer) + GST_MPEG2ENC_WAIT (enc); + enc->buffer = buffer; + GST_MPEG2ENC_SIGNAL (enc); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + + /* buffer will be released by task */ + return GST_FLOW_OK; + + /* special cases */ +not_negotiated: + { + GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL), + ("format wasn't negotiated before chain function")); + + gst_buffer_unref (buffer); + return GST_FLOW_NOT_NEGOTIATED; + } +eos: + { + GST_DEBUG_OBJECT (enc, "ignoring buffer at end-of-stream"); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + + gst_buffer_unref (buffer); + return GST_FLOW_UNEXPECTED; + } +ignore: + { + GST_DEBUG_OBJECT (enc, + "ignoring buffer because encoding task encountered %s", + gst_flow_get_name (enc->srcresult)); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + + gst_buffer_unref (buffer); + return enc->srcresult; + } } static void @@ -295,29 +601,101 @@ gst_mpeg2enc_set_property (GObject * object, GST_MPEG2ENC (object)->options->setProperty (prop_id, value); } +static gboolean +gst_mpeg2enc_src_activate_push (GstPad * pad, gboolean active) +{ + gboolean result = TRUE; + GstMpeg2enc *enc; + + enc = GST_MPEG2ENC (GST_PAD_PARENT (pad)); + + if (active) { + /* setcaps will start task once encoder is setup */ + } else { + /* can only end the encoding loop by forcing eos */ + GST_MPEG2ENC_MUTEX_LOCK (enc); + enc->eos = TRUE; + enc->srcresult = GST_FLOW_WRONG_STATE; + GST_MPEG2ENC_SIGNAL (enc); + GST_MPEG2ENC_MUTEX_UNLOCK (enc); + + /* encoding loop should have ended now and can be joined */ + result = gst_pad_stop_task (pad); + } + + return result; +} + static GstStateChangeReturn gst_mpeg2enc_change_state (GstElement * element, GstStateChange transition) { GstMpeg2enc *enc = GST_MPEG2ENC (element); + GstStateChangeReturn ret; + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - delete enc->encoder; - enc->encoder = NULL; + gst_mpeg2enc_reset (enc); break; default: break; } - if (parent_class->change_state) - return parent_class->change_state (element, transition); +done: + return ret; +} + +#ifndef GST_DISABLE_GST_DEBUG + +static mjpeg_log_handler_t old_handler = NULL; - return GST_STATE_CHANGE_SUCCESS; +/* note that this will affect all mjpegtools elements/threads */ +static void +gst_mpeg2enc_log_callback (log_level_t level, const char *message) +{ + GstDebugLevel gst_level; + + switch (level) { + case LOG_NONE: + gst_level = GST_LEVEL_NONE; + break; + case LOG_ERROR: + gst_level = GST_LEVEL_ERROR; + break; + case LOG_INFO: + gst_level = GST_LEVEL_INFO; + break; + case LOG_DEBUG: + gst_level = GST_LEVEL_DEBUG; + break; + default: + gst_level = GST_LEVEL_INFO; + break; + } + + gst_debug_log (mpeg2enc_debug, gst_level, "", "", 0, NULL, message); + + /* chain up to the old handler; + * this could actually be a handler from another mjpegtools based + * plugin; in which case messages can come out double or from + * the wrong plugin (element)... */ + old_handler (level, message); } +#endif static gboolean plugin_init (GstPlugin * plugin) { +#ifndef GST_DISABLE_GST_DEBUG + old_handler = mjpeg_log_set_handler (gst_mpeg2enc_log_callback); + g_assert (old_handler != NULL); +#endif + /* in any case, we do not want default handler output */ + mjpeg_default_handler_verbosity (0); + return gst_element_register (plugin, "mpeg2enc", GST_RANK_NONE, GST_TYPE_MPEG2ENC); } @@ -326,4 +704,4 @@ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "mpeg2enc", "High-quality MPEG-1/2 video encoder", - plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN) + plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |