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.
This commit is contained in:
Mark Nauwelaerts 2006-07-13 11:06:45 +00:00 committed by Tim-Philipp Müller
parent 0e9af4401e
commit 676eafc77c
17 changed files with 1175 additions and 309 deletions

View file

@ -1,3 +1,29 @@
2006-07-13 Tim-Philipp Müller <tim at centricular dot net>
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.
2006-07-12 Tim-Philipp Müller <tim at centricular dot net>
* gst/spectrum/gstspectrum.c: (gst_spectrum_class_init):

View file

@ -542,6 +542,84 @@ GST_CHECK_FEATURE(LIBMMS, [mms protocol library], libmms, [
])
AC_SUBST(LIBMMS_LIBS)
dnl *** mjpegtools version info ***
dnl some may prefer older version (given quirks above)
dnl hm, no version info seems available within mjpegtools headers
PKG_CHECK_EXISTS(mjpegtools >= 1.8.0 mjpegtools < 1.9.0, [
AC_DEFINE(GST_MJPEGTOOLS_18x, 1, [mjpegtools >= 1.8.0 is used])
have_mpjegtools_18x=yes
], [
have_mpjegtools_18x=no])
dnl *** mpeg2enc ***
translit(dnm, m, l) AM_CONDITIONAL(USE_MPEG2ENC, true)
GST_CHECK_FEATURE(MPEG2ENC, [mpeg2enc], mpeg2enc, [
HAVE_MPEG2ENC="no"
dnl we require a c++ compiler for this one
if [ test x$HAVE_CXX = xyes ]; then
dnl libmpeg2enc was first included in mjpegtools-1.6.2-rc3 (1.6.1.92)
dnl since many distros include mjpegtools specifically without mplex
dnl and mpeg2enc, we check for mpeg2enc on its own, too.
dnl HACK because mpeg2enc 1.8.0 header files have a spurious 'include config.h'
touch config.h
PKG_CHECK_MODULES(MPEG2ENC, mjpegtools >= 1.6.1.93, [
dnl switch over to c++ to test things
AC_LANG_CPLUSPLUS
OLD_CPPFLAGS="$CPPFLAGS"
dnl HACK as above
CPPFLAGS_GOOD="$CPPFLAGS $MPEG2ENC_CFLAGS"
CPPFLAGS="$CPPFLAGS_GOOD -I."
dnl check headers
mpeg2enc_headers_ok=no
AC_CHECK_HEADER([mpeg2encoder.hh], [
MPEG2ENC_LIBS="$MPEG2ENC_LIBS -lmpeg2encpp -lm -lpthread"
OLD_LIBS="$LIBS"
LIBS="$LIBS $MPEG2ENC_LIBS"
AC_MSG_CHECKING([for valid mpeg2enc objects])
AC_TRY_RUN([
#include <mpeg2encoder.hh>
#include <mpeg2encoptions.hh>
int
main (int argc,
char *argv[])
{
MPEG2EncOptions *options = new MPEG2EncOptions ();
MPEG2Encoder *encoder = new MPEG2Encoder (*options);
return 0;
}
],[
AC_MSG_RESULT(yes)
dnl so far so good, let's check more things:
dnl mjpegtools-1.8.0 does not install the required
dnl mpeg2syntaxcodes.h header by default, and a new release
dnl is not in sight, so check for this oversight in case
dnl distros or folks have fixed this themselves
if test "x$have_mpjegtools_18x" = "xyes"; then
AC_CHECK_HEADER([mpeg2syntaxcodes.h], [
mpeg2enc_headers_ok=yes
], [
mpeg2enc_headers_ok=no
])
fi
if test "x$mpeg2enc_headers_ok" = "xyes"; then
HAVE_MPEG2ENC="yes"
fi
CPP_FLAGS="$CPPFLAGS_GOOD"
AC_SUBST(MPEG2ENC_CFLAGS)
AC_SUBST(MPEG2ENC_LIBS)
], [
AC_MSG_RESULT(no)
])
LIBS="$OLD_LIBS"
])
CPPFLAGS="$OLD_CPPFLAGS"
AC_LANG_C
])
fi
])
dnl *** musepack ***
translit(dnm, m, l) AM_CONDITIONAL(USE_MUSEPACK, true)
GST_CHECK_FEATURE(MUSEPACK, [musepackdec], musepack, [
@ -767,6 +845,7 @@ ext/gsm/Makefile
ext/ivorbis/Makefile
ext/libmms/Makefile
ext/Makefile
ext/mpeg2enc/Makefile
ext/musepack/Makefile
ext/musicbrainz/Makefile
ext/neon/Makefile

View file

@ -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 \

View file

@ -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 \

View file

@ -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;
GST_DEBUG_CATEGORY (mpeg2enc_debug);
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 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>");
static GstPadTemplate *
sink_templ (void)
{
static GstPadTemplate *templ = NULL;
#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 }"
if (!templ) {
GstCaps *caps;
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)
);
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);
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)
);
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);
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);
element_class->change_state = gst_mpeg2enc_change_state;
object_class->finalize = GST_DEBUG_FUNCPTR (gst_mpeg2enc_finalize);
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 ();
gst_element_set_loop_function (element, gst_mpeg2enc_loop);
enc->encoder = NULL;
enc->buffer = NULL;
enc->tlock = g_mutex_new ();
enc->cond = g_cond_new ();
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;
}
gst_pad_set_element_private (enc->sinkpad, data);
if (!(caps = GST_PAD_CAPS (enc->sinkpad))) {
GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
("format wasn't negotiated before loop function"));
return;
}
/* create new encoder with these settings */
enc->encoder = new GstMpeg2Encoder (enc->options, enc->sinkpad,
caps, enc->srcpad);
/* 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));
delete enc->encoder;
enc->encoder = NULL;
return;
}
}
enc->encoder->encodePicture ();
gst_pad_event_default (enc->sinkpad, gst_event_new (GST_EVENT_EOS));
}
static GstPadLinkReturn
gst_mpeg2enc_sink_link (GstPad * pad, const GstCaps * caps)
{
GstMpeg2enc *enc = GST_MPEG2ENC (gst_pad_get_parent (pad));
if (!gst_caps_is_fixed (caps))
return GST_PAD_LINK_DELAYED;
/* 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;
}
}
return GST_PAD_LINK_OK;
/* 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;
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;
}
GST_DEBUG_OBJECT (enc, "returned caps %" GST_PTR_FORMAT, caps);
return caps;
}
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 FALSE;
}
refuse_renegotiation:
{
GST_WARNING_OBJECT (enc, "refused renegotiation (to %" GST_PTR_FORMAT ")",
caps);
return FALSE;
}
}
static gboolean
gst_mpeg2enc_sink_event (GstPad * pad, GstEvent * event)
{
GstMpeg2enc *enc;
gboolean result = TRUE;
enc = GST_MPEG2ENC (GST_PAD_PARENT (pad));
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) {
/* 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");
}
/* 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;
}
}
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);
return GST_STATE_CHANGE_SUCCESS;
done:
return ret;
}
#ifndef GST_DISABLE_GST_DEBUG
static mjpeg_log_handler_t old_handler = NULL;
/* 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)

View file

@ -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 {

View file

@ -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
parms.Init (*options);
reader->Init ();
quantizer->Init ();
return TRUE;
}
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);
}

View file

@ -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 ();
/* one image */
void encodePicture ();
gboolean setup ();
void init ();
/* process stream */
void encode ();
/* get current output format */
GstCaps *getFormat ();
private:
GstElement *element;
GstCaps *caps;
gboolean init_done;
};
#endif /* __GST_MPEG2ENCODER_H__ */

View file

@ -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;
}

View file

@ -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;
GstMpeg2enc *enc;
gint
i,
x,
y,
n;
guint8 *
frame;
enc = GST_MPEG2ENC (element);
GstFormat
fmt =
GST_FORMAT_DEFAULT;
gint64
pos =
0,
tot =
0;
GST_MPEG2ENC_MUTEX_LOCK (enc);
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;
/* 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;
}
GST_MPEG2ENC_WAIT (enc);
}
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);
}
} while (!buf);
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;
return false;
/* inform the element the buffer has been processed */
GST_MPEG2ENC_SIGNAL (enc);
GST_MPEG2ENC_MUTEX_UNLOCK (enc);
return FALSE;
}

View file

@ -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;
};

View file

@ -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 */

View file

@ -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__ */

View file

@ -28,7 +28,8 @@ clean-local: clean-local-check
check_PROGRAMS = \
elements/gdppay \
elements/gdpdepay
elements/gdpdepay \
elements/mpeg2enc
TESTS = $(check_PROGRAMS)

View file

@ -3,3 +3,4 @@ tagid3v2mux
gdpdepay
gdppay
mpeg2enc

View file

@ -0,0 +1,203 @@
/* GStreamer
*
* unit test for mpeg2enc
*
* Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
#include <gst/check/gstcheck.h>
/* For ease of programming we use globals to keep refs for our floating
* src and sink pads we create; otherwise we always have to do get_pad,
* get_peer, and then remove references in every test function */
static GstPad *mysrcpad, *mysinkpad;
#define VIDEO_CAPS_STRING "video/x-raw-yuv, " \
"width = (int) 384, " \
"height = (int) 288, " \
"framerate = (fraction) 25/1"
#define MPEG_CAPS_STRING "video/mpeg, " \
"mpegversion = (int) { 1, 2 }, " \
"systemstream = (bool) false, " \
"height = (int) 288, " \
"framerate = (fraction) 25/1"
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (MPEG_CAPS_STRING));
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (VIDEO_CAPS_STRING));
/* some global vars, makes it easy as for the ones above */
static GMutex *mpeg2enc_mutex;
static GCond *mpeg2enc_cond;
static gboolean arrived_eos;
gboolean
test_sink_event (GstPad * pad, GstEvent * event)
{
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
g_mutex_lock (mpeg2enc_mutex);
arrived_eos = TRUE;
g_cond_signal (mpeg2enc_cond);
g_mutex_unlock (mpeg2enc_mutex);
break;
default:
break;
}
return gst_pad_event_default (pad, event);
}
GstElement *
setup_mpeg2enc ()
{
GstElement *mpeg2enc;
GST_DEBUG ("setup_mpeg2enc");
mpeg2enc = gst_check_setup_element ("mpeg2enc");
mysrcpad = gst_check_setup_src_pad (mpeg2enc, &srctemplate, NULL);
mysinkpad = gst_check_setup_sink_pad (mpeg2enc, &sinktemplate, NULL);
/* need to know when we are eos */
gst_pad_set_event_function (mysinkpad, test_sink_event);
/* and notify the test run */
mpeg2enc_mutex = g_mutex_new ();
mpeg2enc_cond = g_cond_new ();
return mpeg2enc;
}
void
cleanup_mpeg2enc (GstElement * mpeg2enc)
{
GST_DEBUG ("cleanup_mpeg2enc");
gst_element_set_state (mpeg2enc, GST_STATE_NULL);
gst_check_teardown_src_pad (mpeg2enc);
gst_check_teardown_sink_pad (mpeg2enc);
gst_check_teardown_element (mpeg2enc);
g_mutex_free (mpeg2enc_mutex);
g_cond_free (mpeg2enc_cond);
}
GST_START_TEST (test_video_pad)
{
GstElement *mpeg2enc;
GstBuffer *inbuffer, *outbuffer;
GstCaps *caps;
int i, num_buffers;
guint8 data0[] = { 0x00, 0x00, 0x01, 0xb3 };
mpeg2enc = setup_mpeg2enc ();
fail_unless (gst_element_set_state (mpeg2enc,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
/* corresponds to I420 buffer for the size mentioned in the caps */
inbuffer = gst_buffer_new_and_alloc (384 * 288 * 3 / 2);
/* makes valgrind's memcheck happier */
memset (GST_BUFFER_DATA (inbuffer), 0, GST_BUFFER_SIZE (inbuffer));
caps = gst_caps_from_string (VIDEO_CAPS_STRING);
gst_buffer_set_caps (inbuffer, caps);
gst_caps_unref (caps);
GST_BUFFER_TIMESTAMP (inbuffer) = 0;
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
/* need to force eos and state change to make sure the encoding task ends */
fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
/* need to wait a bit to make sure mpeg2enc task digested all this */
g_mutex_lock (mpeg2enc_mutex);
while (!arrived_eos)
g_cond_wait (mpeg2enc_cond, mpeg2enc_mutex);
g_mutex_unlock (mpeg2enc_mutex);
num_buffers = g_list_length (buffers);
/* well, we do not really know much with mpeg, but at least something ... */
fail_unless (num_buffers >= 1);
/* clean up buffers */
for (i = 0; i < num_buffers; ++i) {
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
switch (i) {
case 0:
fail_unless (GST_BUFFER_SIZE (outbuffer) >= sizeof (data0));
fail_unless (memcmp (data0, GST_BUFFER_DATA (outbuffer),
sizeof (data0)) == 0);
break;
default:
break;
}
buffers = g_list_remove (buffers, outbuffer);
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_buffer_unref (outbuffer);
outbuffer = NULL;
}
cleanup_mpeg2enc (mpeg2enc);
g_list_free (buffers);
buffers = NULL;
}
GST_END_TEST;
Suite *
mpeg2enc_suite (void)
{
Suite *s = suite_create ("mpeg2enc");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_video_pad);
return s;
}
int
main (int argc, char **argv)
{
int nf;
Suite *s = mpeg2enc_suite ();
SRunner *sr = srunner_create (s);
gst_check_init (&argc, &argv);
srunner_run_all (sr, CK_NORMAL);
nf = srunner_ntests_failed (sr);
srunner_free (sr);
return nf;
}

View file

@ -1,2 +1,2 @@
pitch-test
v4l2src-test
ximagesrc-test