summaryrefslogtreecommitdiffstats
path: root/ext/mpeg2enc/gstmpeg2enc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mpeg2enc/gstmpeg2enc.cc')
-rw-r--r--ext/mpeg2enc/gstmpeg2enc.cc738
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)