mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
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:
parent
0e9af4401e
commit
676eafc77c
17 changed files with 1175 additions and 309 deletions
26
ChangeLog
26
ChangeLog
|
@ -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):
|
||||
|
|
79
configure.ac
79
configure.ac
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -28,7 +28,8 @@ clean-local: clean-local-check
|
|||
|
||||
check_PROGRAMS = \
|
||||
elements/gdppay \
|
||||
elements/gdpdepay
|
||||
elements/gdpdepay \
|
||||
elements/mpeg2enc
|
||||
|
||||
TESTS = $(check_PROGRAMS)
|
||||
|
||||
|
|
1
tests/check/elements/.gitignore
vendored
1
tests/check/elements/.gitignore
vendored
|
@ -3,3 +3,4 @@ tagid3v2mux
|
|||
|
||||
gdpdepay
|
||||
gdppay
|
||||
mpeg2enc
|
||||
|
|
203
tests/check/elements/mpeg2enc.c
Normal file
203
tests/check/elements/mpeg2enc.c
Normal 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;
|
||||
}
|
2
tests/icles/.gitignore
vendored
2
tests/icles/.gitignore
vendored
|
@ -1,2 +1,2 @@
|
|||
pitch-test
|
||||
v4l2src-test
|
||||
ximagesrc-test
|
||||
|
|
Loading…
Reference in a new issue