summaryrefslogtreecommitdiffstats
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/Makefile.am9
-rw-r--r--ext/mpeg2enc/Makefile.am6
-rw-r--r--ext/mpeg2enc/gstmpeg2enc.cc738
-rw-r--r--ext/mpeg2enc/gstmpeg2enc.hh39
-rw-r--r--ext/mpeg2enc/gstmpeg2encoder.cc78
-rw-r--r--ext/mpeg2enc/gstmpeg2encoder.hh21
-rw-r--r--ext/mpeg2enc/gstmpeg2encoptions.cc58
-rw-r--r--ext/mpeg2enc/gstmpeg2encpicturereader.cc105
-rw-r--r--ext/mpeg2enc/gstmpeg2encpicturereader.hh8
-rw-r--r--ext/mpeg2enc/gstmpeg2encstreamwriter.cc67
-rw-r--r--ext/mpeg2enc/gstmpeg2encstreamwriter.hh25
11 files changed, 855 insertions, 299 deletions
diff --git a/ext/Makefile.am b/ext/Makefile.am
index 43b0990e..42765f11 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -130,11 +130,11 @@ else
LIBMMS_DIR=
endif
-# if USE_MPEG2ENC
-# MPEG2ENC_DIR=mpeg2enc
-# else
+if USE_MPEG2ENC
+MPEG2ENC_DIR=mpeg2enc
+else
MPEG2ENC_DIR=
-# endif
+endif
# if USE_MPLEX
# MPLEX_DIR=mplex
@@ -286,6 +286,7 @@ DIST_SUBDIRS= \
libmms \
dts \
divx \
+ mpeg2enc \
musepack \
musicbrainz \
neon \
diff --git a/ext/mpeg2enc/Makefile.am b/ext/mpeg2enc/Makefile.am
index 15174006..7d5d3468 100644
--- a/ext/mpeg2enc/Makefile.am
+++ b/ext/mpeg2enc/Makefile.am
@@ -6,8 +6,10 @@ libgstmpeg2enc_la_SOURCES = \
gstmpeg2encoder.cc \
gstmpeg2encstreamwriter.cc \
gstmpeg2encpicturereader.cc
-libgstmpeg2enc_la_CXXFLAGS = $(MPEG2ENC_CFLAGS) $(GST_CFLAGS)
-libgstmpeg2enc_la_LIBADD = $(MPEG2ENC_LIBS) $(GST_PLUGIN_LDFLAGS)
+
+libgstmpeg2enc_la_CXXFLAGS = $(MPEG2ENC_CFLAGS) $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -Wno-non-virtual-dtor
+libgstmpeg2enc_la_LIBADD = $(MPEG2ENC_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS)
+libgstmpeg2enc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = \
gstmpeg2enc.hh \
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)
diff --git a/ext/mpeg2enc/gstmpeg2enc.hh b/ext/mpeg2enc/gstmpeg2enc.hh
index 25c61e61..66e799a0 100644
--- a/ext/mpeg2enc/gstmpeg2enc.hh
+++ b/ext/mpeg2enc/gstmpeg2enc.hh
@@ -1,5 +1,6 @@
/* GStreamer mpeg2enc (mjpegtools) wrapper
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 Mark Nauwelaerts <manauw@skynet.be>
*
* gstmpeg2enc.hh: object definition
*
@@ -39,6 +40,30 @@ G_BEGIN_DECLS
#define GST_IS_MPEG2ENC_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MPEG2ENC))
+GST_DEBUG_CATEGORY_EXTERN (mpeg2enc_debug);
+#define GST_CAT_DEFAULT mpeg2enc_debug
+
+#define GST_MPEG2ENC_MUTEX_LOCK(m) G_STMT_START { \
+ GST_LOG_OBJECT (m, "locking tlock from thread %p", g_thread_self ()); \
+ g_mutex_lock (m->tlock); \
+ GST_LOG_OBJECT (m, "locked tlock from thread %p", g_thread_self ()); \
+} G_STMT_END
+
+#define GST_MPEG2ENC_MUTEX_UNLOCK(m) G_STMT_START { \
+ GST_LOG_OBJECT (m, "unlocking tlock from thread %p", g_thread_self ()); \
+ g_mutex_unlock (m->tlock); \
+} G_STMT_END
+
+#define GST_MPEG2ENC_WAIT(m) G_STMT_START { \
+ GST_LOG_OBJECT (m, "thread %p waiting", g_thread_self ()); \
+ g_cond_wait (m->cond, m->tlock); \
+} G_STMT_END
+
+#define GST_MPEG2ENC_SIGNAL(m) G_STMT_START { \
+ GST_LOG_OBJECT (m, "signalling from thread %p", g_thread_self ()); \
+ g_cond_signal (m->cond); \
+} G_STMT_END
+
typedef struct _GstMpeg2enc {
GstElement parent;
@@ -50,6 +75,20 @@ typedef struct _GstMpeg2enc {
/* general encoding object (contains rest) */
GstMpeg2Encoder *encoder;
+
+ /* lock for syncing with encoding task */
+ GMutex *tlock;
+ /* with TLOCK */
+ /* signals counterpart thread that something changed;
+ * buffer ready for task or buffer has been processed */
+ GCond *cond;
+ /* seen eos */
+ gboolean eos;
+ /* flowreturn obtained by encoding task */
+ GstFlowReturn srcresult;
+ /* buffer for encoding task */
+ GstBuffer *buffer;
+
} GstMpeg2enc;
typedef struct _GstMpeg2encClass {
diff --git a/ext/mpeg2enc/gstmpeg2encoder.cc b/ext/mpeg2enc/gstmpeg2encoder.cc
index 5a77b58b..866f63d8 100644
--- a/ext/mpeg2enc/gstmpeg2encoder.cc
+++ b/ext/mpeg2enc/gstmpeg2encoder.cc
@@ -1,5 +1,6 @@
/* GStreamer mpeg2enc (mjpegtools) wrapper
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 Mark Nauwelaerts <manauw@skynet.be>
*
* gstmpeg2encoder.cc: gstreamer/mpeg2enc encoder class
*
@@ -29,49 +30,94 @@
#include <seqencoder.hh>
#include <mpeg2coder.hh>
+#include "gstmpeg2enc.hh"
#include "gstmpeg2encoder.hh"
/*
* Class init stuff.
*/
-GstMpeg2Encoder::GstMpeg2Encoder (GstMpeg2EncOptions * options,
- GstPad * sinkpad, const GstCaps * caps, GstPad * srcpad):
+GstMpeg2Encoder::GstMpeg2Encoder (GstMpeg2EncOptions * options, GstElement * in_element, GstCaps * in_caps):
MPEG2Encoder (*options)
{
- MPEG2EncInVidParams strm;
+ element = in_element;
+ gst_object_ref (element);
+ caps = in_caps;
+ gst_caps_ref (in_caps);
+ init_done = FALSE;
+}
+
+GstMpeg2Encoder::~GstMpeg2Encoder ()
+{
+ gst_caps_unref (caps);
+ gst_object_unref (element);
+}
+
+gboolean GstMpeg2Encoder::setup ()
+{
+ MPEG2EncInVidParams
+ strm;
+ GstMpeg2enc *
+ enc;
+
+ enc = GST_MPEG2ENC (element);
/* I/O */
- reader = new GstMpeg2EncPictureReader (sinkpad, caps, &parms);
+ reader = new GstMpeg2EncPictureReader (element, caps, &parms);
reader->StreamPictureParams (strm);
- if (options->SetFormatPresets (strm)) {
- g_warning ("Eek! Format presets failed. This is really bad!");
+#ifdef GST_MJPEGTOOLS_18x
+ /* chain thread caters for reading, do not need another thread for this */
+ options.allow_parallel_read = FALSE;
+#endif
+ if (options.SetFormatPresets (strm)) {
+ return FALSE;
}
- writer = new GstMpeg2EncStreamWriter (srcpad, &parms);
+ writer = new GstMpeg2EncStreamWriter (enc->srcpad, &parms);
/* encoding internals */
quantizer = new Quantizer (parms);
- coder = new MPEG2Coder (parms, *writer);
bitrate_controller = new OnTheFlyRateCtl (parms);
-
+#ifdef GST_MJPEGTOOLS_18x
+ /* sequencer */
+ seqencoder = new SeqEncoder (parms, *reader, *quantizer,
+ *writer, *bitrate_controller);
+#else
+ coder = new MPEG2Coder (parms, *writer);
/* sequencer */
seqencoder = new SeqEncoder (parms, *reader, *quantizer,
*writer, *coder, *bitrate_controller);
+#endif
+
+ return TRUE;
+}
- parms.Init (*options);
- reader->Init ();
- quantizer->Init ();
+void
+GstMpeg2Encoder::init ()
+{
+ if (!init_done) {
+ parms.Init (options);
+ reader->Init ();
+ quantizer->Init ();
+#ifdef GST_MJPEGTOOLS_18x
+ seqencoder->Init ();
+#endif
+ init_done = TRUE;
+ }
}
/*
- * One image.
+ * Process all input provided by the reader until it signals eos.
*/
void
-GstMpeg2Encoder::encodePicture ()
+GstMpeg2Encoder::encode ()
{
/* hm, this is all... eek! */
+#ifdef GST_MJPEGTOOLS_18x
+ seqencoder->EncodeStream ();
+#else
seqencoder->Encode ();
+#endif
}
/*
@@ -81,12 +127,12 @@ GstMpeg2Encoder::encodePicture ()
GstCaps *
GstMpeg2Encoder::getFormat ()
{
- gdouble fps = Y4M_RATIO_DBL (mpeg_framerate (options.frame_rate));
+ y4m_ratio_t fps = mpeg_framerate (options.frame_rate);
return gst_caps_new_simple ("video/mpeg",
"systemstream", G_TYPE_BOOLEAN, FALSE,
"mpegversion", G_TYPE_INT, options.mpeg,
"width", G_TYPE_INT, options.in_img_width,
"height", G_TYPE_INT, options.in_img_height,
- "framerate", G_TYPE_DOUBLE, fps, NULL);
+ "framerate", GST_TYPE_FRACTION, fps.n, fps.d, NULL);
}
diff --git a/ext/mpeg2enc/gstmpeg2encoder.hh b/ext/mpeg2enc/gstmpeg2encoder.hh
index df034e6b..94cf5764 100644
--- a/ext/mpeg2enc/gstmpeg2encoder.hh
+++ b/ext/mpeg2enc/gstmpeg2encoder.hh
@@ -1,5 +1,6 @@
/* GStreamer mpeg2enc (mjpegtools) wrapper
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 Mark Nauwelaerts <manauw@skynet.be>
*
* gstmpeg2encoder.hh: gstreamer/mpeg2enc encoder class
*
@@ -22,6 +23,7 @@
#ifndef __GST_MPEG2ENCODER_H__
#define __GST_MPEG2ENCODER_H__
+
#include <mpeg2encoder.hh>
#include "gstmpeg2encoptions.hh"
#include "gstmpeg2encpicturereader.hh"
@@ -29,16 +31,23 @@
class GstMpeg2Encoder : public MPEG2Encoder {
public:
- GstMpeg2Encoder (GstMpeg2EncOptions *options,
- GstPad *sinkpad,
- const GstCaps *caps,
- GstPad *srcpad);
+ GstMpeg2Encoder (GstMpeg2EncOptions *options,
+ GstElement *element, GstCaps *caps);
+ ~GstMpeg2Encoder ();
+
+ gboolean setup ();
+ void init ();
- /* one image */
- void encodePicture ();
+ /* process stream */
+ void encode ();
/* get current output format */
GstCaps *getFormat ();
+
+private:
+ GstElement *element;
+ GstCaps *caps;
+ gboolean init_done;
};
#endif /* __GST_MPEG2ENCODER_H__ */
diff --git a/ext/mpeg2enc/gstmpeg2encoptions.cc b/ext/mpeg2enc/gstmpeg2encoptions.cc
index 0d48ed29..4635b81e 100644
--- a/ext/mpeg2enc/gstmpeg2encoptions.cc
+++ b/ext/mpeg2enc/gstmpeg2encoptions.cc
@@ -64,14 +64,20 @@ enum
ARG_DUMMY_SVCD_SOF,
ARG_CORRECT_SVCD_HDS,
ARG_ALTSCAN_MPEG2,
- ARG_CONSTRAINTS
+ ARG_CONSTRAINTS,
+ ARG_DUALPRIME_MPEG2
/* FILL ME */
};
+/* MPEG1 VCD bitrate is used as default (also by mpeg2enc) */
+#define DEFAULT_BITRATE 1125
+
/*
* Property enumeration types.
*/
+/* FIXME: nick/name perhaps to be reversed (?) */
+
#define GST_TYPE_MPEG2ENC_FORMAT \
(gst_mpeg2enc_format_get_type ())
@@ -276,6 +282,9 @@ MPEG2EncOptions ()
num_cpus = 1;
if (num_cpus > 32)
num_cpus = 32;
+
+ /* set some default(s) not set in base class */
+ bitrate = DEFAULT_BITRATE * 1024;
}
/*
@@ -306,7 +315,7 @@ GstMpeg2EncOptions::initProperties (GObjectClass * klass)
/* general encoding stream options */
g_object_class_install_property (klass, ARG_BITRATE,
g_param_spec_int ("bitrate", "Bitrate", "Compressed video bitrate (kbps)",
- 0, 10 * 1024, 1125, (GParamFlags) G_PARAM_READWRITE));
+ 0, 10 * 1024, DEFAULT_BITRATE, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (klass, ARG_NONVIDEO_BITRATE,
g_param_spec_int ("non-video-bitrate", "Non-video bitrate",
"Assumed bitrate of non-video for sequence splitting (kbps)",
@@ -346,11 +355,11 @@ GstMpeg2EncOptions::initProperties (GObjectClass * klass)
g_object_class_install_property (klass, ARG_MIN_GOP_SIZE,
g_param_spec_int ("min-gop-size", "Min. GOP size",
"Minimal size per Group-of-Pictures (-1=default)",
- -1, 250, 0, (GParamFlags) G_PARAM_READWRITE));
+ -1, 250, -1, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (klass, ARG_MAX_GOP_SIZE,
g_param_spec_int ("max-gop-size", "Max. GOP size",
"Maximal size per Group-of-Pictures (-1=default)",
- -1, 250, 0, (GParamFlags) G_PARAM_READWRITE));
+ -1, 250, -1, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (klass, ARG_CLOSED_GOP,
g_param_spec_boolean ("closed-gop", "Closed GOP",
"All Group-of-Pictures are closed (for multi-angle DVDs)",
@@ -362,7 +371,7 @@ GstMpeg2EncOptions::initProperties (GObjectClass * klass)
g_object_class_install_property (klass, ARG_B_PER_REFFRAME,
g_param_spec_int ("b-per-refframe", "B per ref. frame",
"Number of B frames between each I/P frame",
- 0, 2, 2, (GParamFlags) G_PARAM_READWRITE));
+ 0, 2, 0, (GParamFlags) G_PARAM_READWRITE));
/* quantisation options */
g_object_class_install_property (klass, ARG_QUANTISATION_REDUCTION,
@@ -372,12 +381,12 @@ GstMpeg2EncOptions::initProperties (GObjectClass * klass)
g_object_class_install_property (klass, ARG_QUANT_REDUCTION_MAX_VAR,
g_param_spec_float ("quant-reduction-max-var",
"Max. quant. reduction variance",
- "Maximal luma variance below which quantisation boost is used", 0.,
- 2500., 0., (GParamFlags) G_PARAM_READWRITE));
+ "Maximal luma variance below which quantisation boost is used",
+ 0., 2500., 100., (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (klass, ARG_INTRA_DC_PRECISION,
g_param_spec_int ("intra-dc-prec", "Intra. DC precision",
- "Number of bits precision for DC (base colour) in MPEG-2 blocks", 8,
- 11, 9, (GParamFlags) G_PARAM_READWRITE));
+ "Number of bits precision for DC (base colour) in MPEG-2 blocks",
+ 8, 11, 9, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (klass, ARG_REDUCE_HF,
g_param_spec_float ("reduce-hf", "Reduce HF",
"How much to reduce high-frequency resolution (by increasing quantisation)",
@@ -395,7 +404,7 @@ GstMpeg2EncOptions::initProperties (GObjectClass * klass)
/* general options */
g_object_class_install_property (klass, ARG_BUFSIZE,
g_param_spec_int ("bufsize", "Decoder buf. size",
- "Target decoders video buffer size (kB)",
+ "Target decoders video buffer size (kB) (default depends on format)",
20, 4000, 46, (GParamFlags) G_PARAM_READWRITE));
/* header flag settings */
@@ -434,14 +443,19 @@ GstMpeg2EncOptions::initProperties (GObjectClass * klass)
"Alternate MPEG-2 block scanning. Disabling this might "
"make buggy players play SVCD streams",
TRUE, (GParamFlags) G_PARAM_READWRITE));
-#if 0
- "--dxr2-hack"
-#endif
- /* dangerous/experimental stuff */
- g_object_class_install_property (klass, ARG_CONSTRAINTS,
+
+ /* dangerous/experimental stuff */
+ g_object_class_install_property (klass, ARG_CONSTRAINTS,
g_param_spec_boolean ("constraints", "Constraints",
"Use strict video resolution and bitrate checks",
TRUE, (GParamFlags) G_PARAM_READWRITE));
+#ifdef GST_MJPEGTOOLS_18x
+ g_object_class_install_property (klass, ARG_DUALPRIME_MPEG2,
+ g_param_spec_boolean ("dualprime", "Dual Prime Motion Estimation",
+ "Dual Prime Motion Estimation Mode for MPEG-2 I/P-frame only "
+ "streams. Quite some players do not support this.",
+ FALSE, (GParamFlags) G_PARAM_READWRITE));
+#endif
}
/*
@@ -510,7 +524,7 @@ GstMpeg2EncOptions::getProperty (guint prop_id, GValue * value)
g_value_set_float (value, boost_var_ceil);
break;
case ARG_INTRA_DC_PRECISION:
- g_value_set_int (value, mpeg2_dc_prec - 8);
+ g_value_set_int (value, mpeg2_dc_prec + 8);
break;
case ARG_REDUCE_HF:
g_value_set_float (value, hf_q_boost);
@@ -564,6 +578,11 @@ GstMpeg2EncOptions::getProperty (guint prop_id, GValue * value)
case ARG_CONSTRAINTS:
g_value_set_boolean (value, !ignore_constraints);
break;
+#ifdef GST_MJPEGTOOLS_18x
+ case ARG_DUALPRIME_MPEG2:
+ g_value_set_boolean (value, hack_dualprime);
+ break;
+#endif
default:
break;
}
@@ -631,7 +650,7 @@ GstMpeg2EncOptions::setProperty (guint prop_id, const GValue * value)
boost_var_ceil = g_value_get_float (value);
break;
case ARG_INTRA_DC_PRECISION:
- mpeg2_dc_prec = g_value_get_int (value) + 8;
+ mpeg2_dc_prec = g_value_get_int (value) - 8;
break;
case ARG_REDUCE_HF:
hf_q_boost = g_value_get_float (value);
@@ -688,6 +707,11 @@ GstMpeg2EncOptions::setProperty (guint prop_id, const GValue * value)
case ARG_CONSTRAINTS:
ignore_constraints = !g_value_get_boolean (value);
break;
+#ifdef GST_MJPEGTOOLS_18x
+ case ARG_DUALPRIME_MPEG2:
+ hack_dualprime = g_value_get_boolean (value);
+ break;
+#endif
default:
break;
}
diff --git a/ext/mpeg2enc/gstmpeg2encpicturereader.cc b/ext/mpeg2enc/gstmpeg2encpicturereader.cc
index 9ed8afc0..9bc59edf 100644
--- a/ext/mpeg2enc/gstmpeg2encpicturereader.cc
+++ b/ext/mpeg2enc/gstmpeg2encpicturereader.cc
@@ -25,23 +25,26 @@
#include <encoderparams.hh>
+#include "gstmpeg2enc.hh"
#include "gstmpeg2encpicturereader.hh"
/*
* Class init stuff.
*/
-GstMpeg2EncPictureReader::GstMpeg2EncPictureReader (GstPad * in_pad,
- const GstCaps * in_caps, EncoderParams * params):
+GstMpeg2EncPictureReader::GstMpeg2EncPictureReader (GstElement * in_element, GstCaps * in_caps, EncoderParams * params):
PictureReader (*params)
{
- pad = in_pad;
- caps = gst_caps_copy (in_caps);
+ element = in_element;
+ gst_object_ref (element);
+ caps = in_caps;
+ gst_caps_ref (caps);
}
GstMpeg2EncPictureReader::~GstMpeg2EncPictureReader ()
{
- gst_caps_free (caps);
+ gst_caps_unref (caps);
+ gst_object_unref (element);
}
/*
@@ -53,83 +56,50 @@ GstMpeg2EncPictureReader::StreamPictureParams (MPEG2EncInVidParams & strm)
{
GstStructure *structure = gst_caps_get_structure (caps, 0);
gint width, height;
- gdouble fps;
+ const GValue *fps_val;
+ y4m_ratio_t fps;
gst_structure_get_int (structure, "width", &width);
gst_structure_get_int (structure, "height", &height);
- gst_structure_get_double (structure, "framerate", &fps);
+ fps_val = gst_structure_get_value (structure, "framerate");
+ fps.n = gst_value_get_fraction_numerator (fps_val);
+ fps.d = gst_value_get_fraction_denominator (fps_val);
strm.horizontal_size = width;
strm.vertical_size = height;
- strm.frame_rate_code = mpeg_framerate_code (mpeg_conform_framerate (fps));
+ strm.frame_rate_code = mpeg_framerate_code (fps);
strm.interlacing_code = Y4M_ILACE_NONE;
+ /* FIXME perhaps involve pixel-aspect-ratio for 'better' sar */
strm.aspect_ratio_code = mpeg_guess_mpeg_aspect_code (2, y4m_sar_SQUARE,
strm.horizontal_size, strm.vertical_size);
-
- /* FIXME:
- * strm.interlacing_code = y4m_si_get_interlace(&si);
- * sar = y4m_si_get_sampleaspect(&si);
- * strm.aspect_ratio_code =
- * mpeg_guess_mpeg_aspect_code(2, sar,
- * strm.horizontal_size,
- * strm.vertical_size);
- */
}
/*
* Read a frame. Return true means EOS or error.
*/
-bool GstMpeg2EncPictureReader::LoadFrame ()
+bool
+GstMpeg2EncPictureReader::LoadFrame ()
{
- GstData *
- data;
- GstBuffer *
- buf =
- NULL;
-
- gint
- i,
- x,
- y,
- n;
- guint8 *
- frame;
-
- GstFormat
- fmt =
- GST_FORMAT_DEFAULT;
- gint64
- pos =
- 0,
- tot =
- 0;
-
- gst_pad_query (GST_PAD_PEER (pad), GST_QUERY_POSITION, &fmt, &pos);
- gst_pad_query (GST_PAD_PEER (pad), GST_QUERY_TOTAL, &fmt, &tot);
-
- do {
- if ((data = (GstData *) gst_pad_get_element_private (pad))) {
- gst_pad_set_element_private (pad, NULL);
- } else if (!(data = gst_pad_pull (pad))) {
- GST_ELEMENT_ERROR (gst_pad_get_parent (pad), RESOURCE, READ,
- (NULL), (NULL));
- return true;
- }
+ gint i, x, y, n;
+ guint8 *frame;
+ GstMpeg2enc *enc;
+
+ enc = GST_MPEG2ENC (element);
- if (GST_IS_EVENT (data)) {
- if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) {
- gst_event_unref (GST_EVENT (data));
- return true;
- } else {
- gst_pad_event_default (pad, GST_EVENT (data));
- }
- } else {
- buf = GST_BUFFER (data);
+ GST_MPEG2ENC_MUTEX_LOCK (enc);
+
+ /* hang around until the element provides us with a buffer */
+ while (!enc->buffer) {
+ if (enc->eos) {
+ GST_MPEG2ENC_MUTEX_UNLOCK (enc);
+ /* inform the mpeg encoding loop that it can give up */
+ return TRUE;
}
- } while (!buf);
+ GST_MPEG2ENC_WAIT (enc);
+ }
- frame = GST_BUFFER_DATA (buf);
+ frame = GST_BUFFER_DATA (enc->buffer);
n = frames_read % input_imgs_buf_size;
x = encparams.horizontal_size;
y = encparams.vertical_size;
@@ -149,7 +119,12 @@ bool GstMpeg2EncPictureReader::LoadFrame ()
memcpy (input_imgs_buf[n][2] + i * encparams.phy_chrom_width, frame, x);
frame += x;
}
- gst_buffer_unref (buf);
+ gst_buffer_unref (enc->buffer);
+ enc->buffer = NULL;
+
+ /* inform the element the buffer has been processed */
+ GST_MPEG2ENC_SIGNAL (enc);
+ GST_MPEG2ENC_MUTEX_UNLOCK (enc);
- return false;
+ return FALSE;
}
diff --git a/ext/mpeg2enc/gstmpeg2encpicturereader.hh b/ext/mpeg2enc/gstmpeg2encpicturereader.hh
index 92d01f6d..7f6c9c3c 100644
--- a/ext/mpeg2enc/gstmpeg2encpicturereader.hh
+++ b/ext/mpeg2enc/gstmpeg2encpicturereader.hh
@@ -25,13 +25,11 @@
#include <gst/gst.h>
#include <picturereader.hh>
-#include "gstmpeg2encoptions.hh"
class GstMpeg2EncPictureReader : public PictureReader {
public:
- GstMpeg2EncPictureReader (GstPad *pad,
- const GstCaps *caps,
- EncoderParams *params);
+ GstMpeg2EncPictureReader (GstElement *element, GstCaps *caps,
+ EncoderParams *params);
~GstMpeg2EncPictureReader ();
/* get input picture parameters (width/height etc.) */
@@ -42,7 +40,7 @@ protected:
bool LoadFrame ();
private:
- GstPad *pad;
+ GstElement *element;
GstCaps *caps;
};
diff --git a/ext/mpeg2enc/gstmpeg2encstreamwriter.cc b/ext/mpeg2enc/gstmpeg2encstreamwriter.cc
index 39c705ac..ee3fefd6 100644
--- a/ext/mpeg2enc/gstmpeg2encstreamwriter.cc
+++ b/ext/mpeg2enc/gstmpeg2encstreamwriter.cc
@@ -1,5 +1,6 @@
/* GStreamer mpeg2enc (mjpegtools) wrapper
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 Mark Nauwelaerts <manauw@skynet.be>
*
* gstmpeg2encstreamwriter.cc: GStreamer/mpeg2enc output wrapper
*
@@ -23,7 +24,55 @@
#include "config.h"
#endif
+#include "gstmpeg2enc.hh"
#include "gstmpeg2encstreamwriter.hh"
+#include <string.h>
+
+#ifdef GST_MJPEGTOOLS_18x
+
+/*
+ * Class init stuff.
+ */
+
+GstMpeg2EncStreamWriter::GstMpeg2EncStreamWriter (GstPad * in_pad,
+ EncoderParams * params)
+{
+ pad = in_pad;
+ gst_object_ref (pad);
+ buf = NULL;
+}
+
+GstMpeg2EncStreamWriter::~GstMpeg2EncStreamWriter ()
+{
+ gst_object_unref (pad);
+}
+
+void
+GstMpeg2EncStreamWriter::WriteOutBufferUpto (const guint8 * buffer,
+ const guint32 flush_upto)
+{
+ GstBuffer *buf;
+ GstMpeg2enc *enc = GST_MPEG2ENC (GST_PAD_PARENT (pad));
+
+ buf = gst_buffer_new_and_alloc (flush_upto);
+
+ memcpy (GST_BUFFER_DATA (buf), buffer, flush_upto);
+ flushed += flush_upto;
+
+ /* this should not block anything else (e.g. chain), but if it does,
+ * it's ok as mpeg2enc is not really a loop-based element, but push-based */
+ GST_MPEG2ENC_MUTEX_LOCK (enc);
+ gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
+ enc->srcresult = gst_pad_push (pad, buf);
+ GST_MPEG2ENC_MUTEX_UNLOCK (enc);
+}
+
+guint64 GstMpeg2EncStreamWriter::BitCount ()
+{
+ return flushed * 8ll;
+}
+
+#else
#define BUFSIZE (128*1024)
@@ -35,9 +84,15 @@ GstMpeg2EncStreamWriter::GstMpeg2EncStreamWriter (GstPad * in_pad, EncoderParams
ElemStrmWriter (*params)
{
pad = in_pad;
+ gst_object_ref (pad);
buf = NULL;
}
+GstMpeg2EncStreamWriter::~GstMpeg2EncStreamWriter ()
+{
+ gst_object_unref (pad);
+}
+
/*
* Output functions.
*/
@@ -66,7 +121,7 @@ GstMpeg2EncStreamWriter::PutBits (guint32 val, gint n)
outcnt = 8;
bytecnt++;
- if (GST_BUFFER_SIZE (buf) >= GST_BUFFER_MAXSIZE (buf))
+ if (GST_BUFFER_SIZE (buf) >= BUFSIZE)
FrameFlush ();
}
@@ -85,8 +140,15 @@ GstMpeg2EncStreamWriter::FrameBegin ()
void
GstMpeg2EncStreamWriter::FrameFlush ()
{
+ GstMpeg2enc *enc = GST_MPEG2ENC (GST_PAD_PARENT (pad));
+
if (buf) {
- gst_pad_push (pad, GST_DATA (buf));
+ /* this should not block anything else (e.g. chain), but if it does,
+ * it's ok as mpeg2enc is not really a loop-based element, but push-based */
+ GST_MPEG2ENC_MUTEX_LOCK (enc);
+ gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
+ enc->srcresult = gst_pad_push (pad, buf);
+ GST_MPEG2ENC_MUTEX_UNLOCK (enc);
buf = NULL;
}
}
@@ -95,3 +157,4 @@ void
GstMpeg2EncStreamWriter::FrameDiscard ()
{
}
+#endif /* GST_MJPEGTOOLS_18x */
diff --git a/ext/mpeg2enc/gstmpeg2encstreamwriter.hh b/ext/mpeg2enc/gstmpeg2encstreamwriter.hh
index 708b942e..766d7bd1 100644
--- a/ext/mpeg2enc/gstmpeg2encstreamwriter.hh
+++ b/ext/mpeg2enc/gstmpeg2encstreamwriter.hh
@@ -1,5 +1,6 @@
/* GStreamer mpeg2enc (mjpegtools) wrapper
* (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2006 Mark Nauwelaerts <manauw@skynet.be>
*
* gstmpeg2encstreamwriter.hh: GStreamer/mpeg2enc output wrapper
*
@@ -26,10 +27,29 @@
#include <elemstrmwriter.hh>
+#ifdef GST_MJPEGTOOLS_18x
+
+class GstMpeg2EncStreamWriter : public ElemStrmWriter {
+ public:
+ GstMpeg2EncStreamWriter (GstPad *pad, EncoderParams *params);
+ ~GstMpeg2EncStreamWriter ();
+
+ /* output functions */
+ void WriteOutBufferUpto (const guint8 * buffer,
+ const guint32 flush_upto);
+ guint64 BitCount ();
+
+ private:
+ GstPad *pad;
+ GstBuffer *buf;
+};
+
+#else
+
class GstMpeg2EncStreamWriter : public ElemStrmWriter {
public:
- GstMpeg2EncStreamWriter (GstPad *pad,
- EncoderParams *params);
+ GstMpeg2EncStreamWriter (GstPad *pad, EncoderParams *params);
+ ~GstMpeg2EncStreamWriter ();
/* output functions */
void PutBits (guint32 val, gint n);
@@ -41,5 +61,6 @@ private:
GstPad *pad;
GstBuffer *buf;
};
+#endif /* GST_MJPEGTOOLS_18x */
#endif /* __GST_MPEG2ENCSTREAMWRITER_H__ */