gst: New encoding plugin

https://bugzilla.gnome.org/show_bug.cgi?id=627476
This commit is contained in:
Edward Hervey 2010-08-13 17:36:38 +02:00
parent 82b4f9bfef
commit 8a3b45aa1f
24 changed files with 4266 additions and 169 deletions

View file

@ -417,6 +417,7 @@ AG_GST_CHECK_PLUGIN(app)
AG_GST_CHECK_PLUGIN(audioconvert)
AG_GST_CHECK_PLUGIN(audiorate)
AG_GST_CHECK_PLUGIN(audiotestsrc)
AG_GST_CHECK_PLUGIN(encoding)
AG_GST_CHECK_PLUGIN(ffmpegcolorspace)
AG_GST_CHECK_PLUGIN(gdp)
AG_GST_CHECK_PLUGIN(playback)
@ -914,6 +915,7 @@ gst/app/Makefile
gst/audioconvert/Makefile
gst/audiorate/Makefile
gst/audiotestsrc/Makefile
gst/encoding/Makefile
gst/ffmpegcolorspace/Makefile
gst/gdp/Makefile
gst/playback/Makefile

View file

@ -50,7 +50,7 @@ MKDB_OPTIONS=--sgml-mode
# Extra options to supply to gtkdoc-fixref.
FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
--extra-dir=$(GST_PREFIX)/share/gtk-doc/html \
--extra-dir=$(datadir)/gtk-doc/html
--extra-dir=$(datadir)/gtk-doc/html
# Used for dependencies.
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
@ -102,6 +102,7 @@ EXTRA_HFILES = \
$(top_srcdir)/gst/audioconvert/audioconvert.h \
$(top_srcdir)/gst/audioconvert/gstaudioconvert.h \
$(top_srcdir)/gst/audiotestsrc/gstaudiotestsrc.h \
$(top_srcdir)/gst/encoding/gstencodebin.h \
$(top_srcdir)/gst/ffmpegcolorspace/gstffmpegcolorspace.h \
$(top_srcdir)/gst/gdp/gstgdpdepay.h \
$(top_srcdir)/gst/gdp/gstgdppay.h \

View file

@ -31,6 +31,7 @@
<xi:include href="xml/element-clockoverlay.xml" />
<xi:include href="xml/element-decodebin.xml" />
<xi:include href="xml/element-decodebin2.xml" />
<xi:include href="xml/element-encodebin.xml" />
<xi:include href="xml/element-ffmpegcolorspace.xml" />
<xi:include href="xml/element-gdpdepay.xml" />
<xi:include href="xml/element-gdppay.xml" />
@ -80,6 +81,7 @@
<xi:include href="xml/plugin-audiotestsrc.xml" />
<xi:include href="xml/plugin-cdparanoia.xml" />
<xi:include href="xml/plugin-decodebin.xml" />
<xi:include href="xml/plugin-encoding.xml" />
<xi:include href="xml/plugin-ffmpegcolorspace.xml" />
<xi:include href="xml/plugin-gdp.xml" />
<xi:include href="xml/plugin-gio.xml" />

View file

@ -247,6 +247,22 @@ GstDecodeBin2
<SUBSECTION Standard>
</SECTION>
<SECTION>
<FILE>element-encodebin</FILE>
<TITLE>encodebin</TITLE>
GstEncodeBin
<SUBSECTION Standard>
GST_ENCODE_BIN
GST_ENCODE_BIN_CLASS
GST_IS_ENCODE_BIN
GST_IS_ENCODE_BIN_CLASS
GST_TYPE_ENCODE_BIN
GstEncodeBinClass
gst_encode_bin_get_type
</SECTION>
<SECTION>
<FILE>element-ffmpegcolorspace</FILE>
<TITLE>ffmpegcolorspace</TITLE>

View file

@ -158,6 +158,26 @@
<DEFAULT>TRUE</DEFAULT>
</ARG>
<ARG>
<NAME>GstXvImageSink::window-height</NAME>
<TYPE>guint64</TYPE>
<RANGE></RANGE>
<FLAGS>r</FLAGS>
<NICK>window-height</NICK>
<BLURB>Height of the window.</BLURB>
<DEFAULT>0</DEFAULT>
</ARG>
<ARG>
<NAME>GstXvImageSink::window-width</NAME>
<TYPE>guint64</TYPE>
<RANGE></RANGE>
<FLAGS>r</FLAGS>
<NICK>window-width</NICK>
<BLURB>Width of the window.</BLURB>
<DEFAULT>0</DEFAULT>
</ARG>
<ARG>
<NAME>GstXImageSink::display</NAME>
<TYPE>gchar*</TYPE>
@ -218,6 +238,26 @@
<DEFAULT>TRUE</DEFAULT>
</ARG>
<ARG>
<NAME>GstXImageSink::window-height</NAME>
<TYPE>guint64</TYPE>
<RANGE></RANGE>
<FLAGS>r</FLAGS>
<NICK>window-height</NICK>
<BLURB>Height of the window.</BLURB>
<DEFAULT>0</DEFAULT>
</ARG>
<ARG>
<NAME>GstXImageSink::window-width</NAME>
<TYPE>guint64</TYPE>
<RANGE></RANGE>
<FLAGS>r</FLAGS>
<NICK>window-width</NICK>
<BLURB>Width of the window.</BLURB>
<DEFAULT>0</DEFAULT>
</ARG>
<ARG>
<NAME>GstV4lSrc::autoprobe</NAME>
<TYPE>gboolean</TYPE>
@ -3378,3 +3418,63 @@
<DEFAULT>NULL</DEFAULT>
</ARG>
<ARG>
<NAME>GstEncodeBin::audio-jitter-tolerance</NAME>
<TYPE>guint64</TYPE>
<RANGE></RANGE>
<FLAGS>rw</FLAGS>
<NICK>Audio jitter tolerance</NICK>
<BLURB>Amount of timestamp jitter/imperfection to allow on audio streams before inserting/dropping samples (ns).</BLURB>
<DEFAULT>20000000</DEFAULT>
</ARG>
<ARG>
<NAME>GstEncodeBin::avoid-reencoding</NAME>
<TYPE>gboolean</TYPE>
<RANGE></RANGE>
<FLAGS>rw</FLAGS>
<NICK>Avoid re-encoding</NICK>
<BLURB>Whether to re-encode portions of compatible video streams that lay on segment boundaries.</BLURB>
<DEFAULT>FALSE</DEFAULT>
</ARG>
<ARG>
<NAME>GstEncodeBin::profile</NAME>
<TYPE>GstEncodingProfile*</TYPE>
<RANGE></RANGE>
<FLAGS>rw</FLAGS>
<NICK>Profile</NICK>
<BLURB>The GstEncodingProfile to use.</BLURB>
<DEFAULT></DEFAULT>
</ARG>
<ARG>
<NAME>GstEncodeBin::queue-buffers-max</NAME>
<TYPE>guint</TYPE>
<RANGE></RANGE>
<FLAGS>rw</FLAGS>
<NICK>Max. size (buffers)</NICK>
<BLURB>Max. number of buffers in the queue (0=disable).</BLURB>
<DEFAULT>200</DEFAULT>
</ARG>
<ARG>
<NAME>GstEncodeBin::queue-bytes-max</NAME>
<TYPE>guint</TYPE>
<RANGE></RANGE>
<FLAGS>rw</FLAGS>
<NICK>Max. size (kB)</NICK>
<BLURB>Max. amount of data in the queue (bytes, 0=disable).</BLURB>
<DEFAULT>10485760</DEFAULT>
</ARG>
<ARG>
<NAME>GstEncodeBin::queue-time-max</NAME>
<TYPE>guint64</TYPE>
<RANGE></RANGE>
<FLAGS>rw</FLAGS>
<NICK>Max. size (ns)</NICK>
<BLURB>Max. amount of data in the queue (in ns, 0=disable).</BLURB>
<DEFAULT>1000000000</DEFAULT>
</ARG>

View file

@ -56,6 +56,7 @@ GObject
GstBin
GstDecodeBin
GstDecodeBin2
GstEncodeBin
GstPipeline
GstPlayBaseBin
GstPlayBin

View file

@ -8,6 +8,7 @@ GstPlaySink GstChildProxy
GstSubtitleOverlay GstChildProxy
GstDecodeBin2 GstChildProxy
GstURIDecodeBin GstChildProxy
GstEncodeBin GstChildProxy
GstCddaBaseSrc GstURIHandler
GstCdParanoiaSrc GstURIHandler
GstAlsaSrc GstImplementsInterface GstMixer GstPropertyProbe

View file

@ -471,3 +471,11 @@ GstPlaySink *gstplaysink
GstCaps *arg1
</SIGNAL>
<SIGNAL>
<NAME>GstEncodeBin::request-pad</NAME>
<RETURNS>GstPad*</RETURNS>
<FLAGS>la</FLAGS>
GstEncodeBin *gstencodebin
GstCaps *arg1
</SIGNAL>

View file

@ -0,0 +1,46 @@
<plugin>
<name>encoding</name>
<description>various encoding-related elements</description>
<filename>../../gst/encoding/.libs/libgstencodebin.so</filename>
<basename>libgstencodebin.so</basename>
<version>0.10.31.1</version>
<license>LGPL</license>
<source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin>
<elements>
<element>
<name>encodebin</name>
<longname>Encoder Bin</longname>
<class>Generic/Bin/Encoder</class>
<description>Convenience encoding/muxing element</description>
<author>Edward Hervey &lt;edward.hervey@collabora.co.uk&gt;</author>
<pads>
<caps>
<name>audio_%d</name>
<direction>sink</direction>
<presence>request</presence>
<details>ANY</details>
</caps>
<caps>
<name>private_%d</name>
<direction>sink</direction>
<presence>request</presence>
<details>ANY</details>
</caps>
<caps>
<name>video_%d</name>
<direction>sink</direction>
<presence>request</presence>
<details>ANY</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>ANY</details>
</caps>
</pads>
</element>
</elements>
</plugin>

View file

@ -9,173 +9,5 @@
<package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin>
<elements>
<element>
<name>libvisual_bumpscope</name>
<longname>libvisual Bumpscope plugin plugin v.0.0.1</longname>
<class>Visualization</class>
<description>Bumpscope visual plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_corona</name>
<longname>libvisual libvisual corona plugin plugin v.0.1</longname>
<class>Visualization</class>
<description>Libvisual corona plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_infinite</name>
<longname>libvisual infinite plugin plugin v.0.1</longname>
<class>Visualization</class>
<description>Infinite visual plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_jakdaw</name>
<longname>libvisual Jakdaw plugin plugin v.0.0.1</longname>
<class>Visualization</class>
<description>jakdaw visual plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_jess</name>
<longname>libvisual jess plugin plugin v.0.1</longname>
<class>Visualization</class>
<description>Jess visual plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_lv_analyzer</name>
<longname>libvisual libvisual analyzer plugin v.1.0</longname>
<class>Visualization</class>
<description>Libvisual analyzer plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_lv_scope</name>
<longname>libvisual libvisual scope plugin v.0.1</longname>
<class>Visualization</class>
<description>Libvisual scope plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
<element>
<name>libvisual_oinksie</name>
<longname>libvisual oinksie plugin plugin v.0.1</longname>
<class>Visualization</class>
<description>Libvisual Oinksie visual plugin</description>
<author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
</elements>
</plugin>

1
gst/encoding/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
gstencode-marshal.[ch]

41
gst/encoding/Makefile.am Normal file
View file

@ -0,0 +1,41 @@
# variables used for enum/marshal generation
glib_enum_define = GST_ENCODE
glib_gen_prefix = gst_encode
glib_gen_basename = gstencode
built_sources = gstencode-marshal.c
built_headers = gstencode-marshal.h
plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@
plugin_LTLIBRARIES = libgstencodebin.la
libgstencodebin_la_SOURCES = \
gstencodebin.c \
gstsmartencoder.c \
gststreamcombiner.c \
gststreamsplitter.c
nodist_libgstencodebin_la_SOURCES = $(built_sources)
libgstencodebin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
libgstencodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstencodebin_la_LIBADD = \
$(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
$(GST_LIBS)
libgstencodebin_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = \
gstencodebin.h \
gststreamcombiner.h \
gststreamsplitter.h \
gstsmartencoder.h
BUILT_SOURCES = $(built_headers) $(built_sources)
EXTRA_DIST = gstencode-marshal.list
CLEANFILES = $(BUILT_SOURCES)
include $(top_srcdir)/common/gst-glib-gen.mak

View file

@ -0,0 +1 @@
OBJECT:BOXED

1658
gst/encoding/gstencodebin.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
/* GStreamer encoding bin
* Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
* (C) 2009 Nokia Corporation
*
* 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.
*/
#ifndef __GST_ENCODEBIN_H__
#define __GST_ENCODEBIN_H__
#include <gst/gst.h>
#include <gst/pbutils/encoding-profile.h>
#include "gstencode-marshal.h"
#define GST_TYPE_ENCODE_BIN (gst_encode_bin_get_type())
#define GST_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODE_BIN,GstEncodeBin))
#define GST_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ENCODE_BIN,GstEncodeBinClass))
#define GST_IS_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODE_BIN))
#define GST_IS_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ENCODE_BIN))
typedef struct _GstEncodeBin GstEncodeBin;
typedef struct _GstEncodeBinClass GstEncodeBinClass;
GType gst_encode_bin_get_type(void);
#endif /* __GST_ENCODEBIN_H__ */

View file

@ -0,0 +1,701 @@
/* GStreamer Smart Video Encoder element
* Copyright (C) <2010> Edward Hervey <bilboed@gmail.com>
*
* 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.
*/
/* TODO:
* * Implement get_caps/set_caps (store/forward caps)
* * Adjust template caps to the formats we can support
**/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstsmartencoder.h"
GST_DEBUG_CATEGORY_STATIC (smart_encoder_debug);
#define GST_CAT_DEFAULT smart_encoder_debug
/* FIXME : Update this with new caps */
/* WARNING : We can only allow formats with closed-GOP */
#define ALLOWED_CAPS "video/x-h263;video/x-intel-h263;"\
"video/mpeg,mpegversion=(int)1,systemstream=(boolean)false;"\
"video/mpeg,mpegversion=(int)2,systemstream=(boolean)false;"
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (ALLOWED_CAPS)
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (ALLOWED_CAPS)
);
static GQuark INTERNAL_ELEMENT;
/* GstSmartEncoder signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0
/* FILL ME */
};
static void
_do_init (void)
{
INTERNAL_ELEMENT = g_quark_from_string ("internal-element");
};
G_DEFINE_TYPE_EXTENDED (GstSmartEncoder, gst_smart_encoder, GST_TYPE_ELEMENT, 0,
_do_init ());
static void gst_smart_encoder_dispose (GObject * object);
static gboolean setup_recoder_pipeline (GstSmartEncoder * smart_encoder);
static GstFlowReturn gst_smart_encoder_chain (GstPad * pad, GstBuffer * buf);
static gboolean smart_encoder_sink_event (GstPad * pad, GstEvent * event);
static GstCaps *smart_encoder_sink_getcaps (GstPad * pad);
static GstStateChangeReturn
gst_smart_encoder_change_state (GstElement * element,
GstStateChange transition);
static void
gst_smart_encoder_class_init (GstSmartEncoderClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
element_class = (GstElementClass *) klass;
gobject_class = G_OBJECT_CLASS (klass);
gst_smart_encoder_parent_class = g_type_class_peek_parent (klass);
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));
gst_element_class_set_details_simple (element_class, "Smart Video Encoder",
"Codec/Recoder/Video",
"Re-encodes portions of Video that lay on segment boundaries",
"Edward Hervey <bilboed@gmail.com>");
gobject_class->dispose = (GObjectFinalizeFunc) (gst_smart_encoder_dispose);
element_class->change_state = gst_smart_encoder_change_state;
GST_DEBUG_CATEGORY_INIT (smart_encoder_debug, "smartencoder", 0,
"Smart Encoder");
}
static void
smart_encoder_reset (GstSmartEncoder * smart_encoder)
{
gst_segment_init (smart_encoder->segment, GST_FORMAT_UNDEFINED);
if (smart_encoder->encoder) {
/* Clean up/remove elements */
gst_element_set_state (smart_encoder->encoder, GST_STATE_NULL);
gst_element_set_state (smart_encoder->decoder, GST_STATE_NULL);
gst_element_set_bus (smart_encoder->encoder, NULL);
gst_element_set_bus (smart_encoder->decoder, NULL);
gst_pad_set_active (smart_encoder->internal_srcpad, FALSE);
gst_pad_set_active (smart_encoder->internal_sinkpad, FALSE);
gst_object_unref (smart_encoder->encoder);
gst_object_unref (smart_encoder->decoder);
gst_object_unref (smart_encoder->internal_srcpad);
gst_object_unref (smart_encoder->internal_sinkpad);
smart_encoder->encoder = NULL;
smart_encoder->decoder = NULL;
smart_encoder->internal_sinkpad = NULL;
smart_encoder->internal_srcpad = NULL;
}
if (smart_encoder->newsegment) {
gst_event_unref (smart_encoder->newsegment);
smart_encoder->newsegment = NULL;
}
}
static void
gst_smart_encoder_init (GstSmartEncoder * smart_encoder)
{
smart_encoder->sinkpad =
gst_pad_new_from_static_template (&sink_template, "sink");
gst_pad_set_chain_function (smart_encoder->sinkpad, gst_smart_encoder_chain);
gst_pad_set_event_function (smart_encoder->sinkpad, smart_encoder_sink_event);
gst_pad_set_getcaps_function (smart_encoder->sinkpad,
smart_encoder_sink_getcaps);
gst_element_add_pad (GST_ELEMENT (smart_encoder), smart_encoder->sinkpad);
smart_encoder->srcpad =
gst_pad_new_from_static_template (&src_template, "src");
gst_pad_use_fixed_caps (smart_encoder->srcpad);
gst_element_add_pad (GST_ELEMENT (smart_encoder), smart_encoder->srcpad);
smart_encoder->segment = gst_segment_new ();
smart_encoder_reset (smart_encoder);
}
void
gst_smart_encoder_dispose (GObject * object)
{
GstSmartEncoder *smart_encoder = (GstSmartEncoder *) object;
if (smart_encoder->segment)
gst_segment_free (smart_encoder->segment);
smart_encoder->segment = NULL;
if (smart_encoder->available_caps)
gst_caps_unref (smart_encoder->available_caps);
smart_encoder->available_caps = NULL;
G_OBJECT_CLASS (gst_smart_encoder_parent_class)->dispose (object);
}
static GstFlowReturn
gst_smart_encoder_reencode_gop (GstSmartEncoder * smart_encoder)
{
GstFlowReturn res = GST_FLOW_OK;
GList *tmp;
if (smart_encoder->encoder == NULL) {
if (!setup_recoder_pipeline (smart_encoder))
return GST_FLOW_ERROR;
}
/* Activate elements */
/* Set elements to PAUSED */
gst_element_set_state (smart_encoder->encoder, GST_STATE_PAUSED);
gst_element_set_state (smart_encoder->decoder, GST_STATE_PAUSED);
GST_INFO ("Pushing Flush start/stop to clean decoder/encoder");
gst_pad_push_event (smart_encoder->internal_srcpad,
gst_event_new_flush_start ());
gst_pad_push_event (smart_encoder->internal_srcpad,
gst_event_new_flush_stop ());
/* push newsegment */
GST_INFO ("Pushing newsegment %" GST_PTR_FORMAT, smart_encoder->newsegment);
gst_pad_push_event (smart_encoder->internal_srcpad,
gst_event_ref (smart_encoder->newsegment));
/* Push buffers through our pads */
GST_DEBUG ("Pushing pending buffers");
for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) {
GstBuffer *buf = (GstBuffer *) tmp->data;
res = gst_pad_push (smart_encoder->internal_srcpad, buf);
if (G_UNLIKELY (res != GST_FLOW_OK))
break;
}
if (G_UNLIKELY (res != GST_FLOW_OK)) {
GST_WARNING ("Error pushing pending buffers : %s", gst_flow_get_name (res));
/* Remove pending bfufers */
for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) {
gst_buffer_unref ((GstBuffer *) tmp->data);
}
} else {
GST_INFO ("Pushing out EOS to flush out decoder/encoder");
gst_pad_push_event (smart_encoder->internal_srcpad, gst_event_new_eos ());
}
/* Activate elements */
/* Set elements to PAUSED */
gst_element_set_state (smart_encoder->encoder, GST_STATE_NULL);
gst_element_set_state (smart_encoder->decoder, GST_STATE_NULL);
g_list_free (smart_encoder->pending_gop);
smart_encoder->pending_gop = NULL;
return res;
}
static GstFlowReturn
gst_smart_encoder_push_pending_gop (GstSmartEncoder * smart_encoder)
{
gint64 cstart, cstop;
GList *tmp;
GstFlowReturn res = GST_FLOW_OK;
GST_DEBUG ("Pushing pending GOP (%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
")", GST_TIME_ARGS (smart_encoder->gop_start),
GST_TIME_ARGS (smart_encoder->gop_stop));
/* If GOP is entirely within segment, just push downstream */
if (gst_segment_clip (smart_encoder->segment, GST_FORMAT_TIME,
smart_encoder->gop_start, smart_encoder->gop_stop, &cstart, &cstop)) {
if ((cstart != smart_encoder->gop_start)
|| (cstop != smart_encoder->gop_stop)) {
GST_DEBUG ("GOP needs to be re-encoded from %" GST_TIME_FORMAT " to %"
GST_TIME_FORMAT, GST_TIME_ARGS (cstart), GST_TIME_ARGS (cstop));
res = gst_smart_encoder_reencode_gop (smart_encoder);
} else {
/* The whole GOP is within the segment, push all pending buffers downstream */
GST_DEBUG ("GOP doesn't need to be modified, pushing downstream");
for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) {
GstBuffer *buf = (GstBuffer *) tmp->data;
res = gst_pad_push (smart_encoder->srcpad, buf);
if (G_UNLIKELY (res != GST_FLOW_OK))
break;
}
}
} else {
/* The whole GOP is outside the segment, there's most likely
* a bug somewhere. */
GST_WARNING
("GOP is entirely outside of the segment, upstream gave us too much data");
for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) {
gst_buffer_unref ((GstBuffer *) tmp->data);
}
}
if (smart_encoder->pending_gop) {
g_list_free (smart_encoder->pending_gop);
smart_encoder->pending_gop = NULL;
}
smart_encoder->gop_start = GST_CLOCK_TIME_NONE;
smart_encoder->gop_stop = GST_CLOCK_TIME_NONE;
return res;
}
static GstFlowReturn
gst_smart_encoder_chain (GstPad * pad, GstBuffer * buf)
{
GstSmartEncoder *smart_encoder;
GstFlowReturn res = GST_FLOW_OK;
gboolean discont, keyframe;
smart_encoder = GST_SMART_ENCODER (gst_object_get_parent (GST_OBJECT (pad)));
discont = GST_BUFFER_IS_DISCONT (buf);
keyframe = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
GST_DEBUG ("New buffer %s %s %" GST_TIME_FORMAT,
discont ? "discont" : "",
keyframe ? "keyframe" : "", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
if (keyframe) {
GST_DEBUG ("Got a keyframe");
/* If there's a pending GOP, flush it out */
if (smart_encoder->pending_gop) {
/* Mark gop_stop */
smart_encoder->gop_stop = GST_BUFFER_TIMESTAMP (buf);
/* flush pending */
res = gst_smart_encoder_push_pending_gop (smart_encoder);
if (G_UNLIKELY (res != GST_FLOW_OK))
goto beach;
}
/* Mark gop_start for new gop */
smart_encoder->gop_start = GST_BUFFER_TIMESTAMP (buf);
}
/* Store buffer */
smart_encoder->pending_gop = g_list_append (smart_encoder->pending_gop, buf);
/* Update GOP stop position */
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
smart_encoder->gop_stop = GST_BUFFER_TIMESTAMP (buf);
if (GST_BUFFER_DURATION_IS_VALID (buf))
smart_encoder->gop_stop += GST_BUFFER_DURATION (buf);
}
GST_DEBUG ("Buffer stored , Current GOP : %" GST_TIME_FORMAT " -- %"
GST_TIME_FORMAT, GST_TIME_ARGS (smart_encoder->gop_start),
GST_TIME_ARGS (smart_encoder->gop_stop));
beach:
gst_object_unref (smart_encoder);
return res;
}
static gboolean
smart_encoder_sink_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
GstSmartEncoder *smart_encoder = GST_SMART_ENCODER (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
smart_encoder_reset (smart_encoder);
break;
case GST_EVENT_NEWSEGMENT:
{
GstFormat format;
gdouble rate, arate;
gint64 start, stop, time;
gboolean update;
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
GST_DEBUG_OBJECT (smart_encoder,
"newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
GST_TIME_ARGS (time));
if (format != GST_FORMAT_TIME)
GST_ERROR
("smart_encoder can not handle streams not specified in GST_FORMAT_TIME");
/* now configure the values */
gst_segment_set_newsegment_full (smart_encoder->segment, update,
rate, arate, format, start, stop, time);
/* And keep a copy for further usage */
if (smart_encoder->newsegment)
gst_event_unref (smart_encoder->newsegment);
smart_encoder->newsegment = gst_event_ref (event);
}
break;
case GST_EVENT_EOS:
GST_DEBUG ("Eos, flushing remaining data");
gst_smart_encoder_push_pending_gop (smart_encoder);
break;
default:
break;
}
res = gst_pad_push_event (smart_encoder->srcpad, event);
gst_object_unref (smart_encoder);
return res;
}
static GstCaps *
smart_encoder_sink_getcaps (GstPad * pad)
{
GstCaps *peer, *tmpl, *res;
GstSmartEncoder *smart_encoder = GST_SMART_ENCODER (gst_pad_get_parent (pad));
/* Try getting it from downstream */
peer = gst_pad_peer_get_caps_reffed (smart_encoder->srcpad);
/* Use computed caps */
if (smart_encoder->available_caps)
tmpl = gst_caps_ref (smart_encoder->available_caps);
else
tmpl = gst_static_pad_template_get_caps (&src_template);
if (peer == NULL) {
res = tmpl;
} else {
res = gst_caps_intersect (peer, tmpl);
gst_caps_unref (peer);
gst_caps_unref (tmpl);
}
gst_object_unref (smart_encoder);
return res;
}
/*****************************************
* Internal encoder/decoder pipeline *
******************************************/
static GstElementFactory *
get_decoder_factory (GstCaps * caps)
{
GstElementFactory *fact = NULL;
GList *decoders, *tmp;
tmp =
gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER,
GST_RANK_MARGINAL);
decoders = gst_element_factory_list_filter (tmp, caps, GST_PAD_SINK, FALSE);
gst_plugin_feature_list_free (tmp);
for (tmp = decoders; tmp; tmp = tmp->next) {
/* We just pick the first one */
fact = (GstElementFactory *) tmp->data;
gst_object_ref (fact);
break;
}
gst_plugin_feature_list_free (decoders);
return fact;
}
static GstElementFactory *
get_encoder_factory (GstCaps * caps)
{
GstElementFactory *fact = NULL;
GList *encoders, *tmp;
tmp =
gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER,
GST_RANK_MARGINAL);
encoders = gst_element_factory_list_filter (tmp, caps, GST_PAD_SRC, FALSE);
gst_plugin_feature_list_free (tmp);
for (tmp = encoders; tmp; tmp = tmp->next) {
/* We just pick the first one */
fact = (GstElementFactory *) tmp->data;
gst_object_ref (fact);
break;
}
gst_plugin_feature_list_free (encoders);
return fact;
}
static GstElement *
get_decoder (GstCaps * caps)
{
GstElementFactory *fact = get_decoder_factory (caps);
GstElement *res = NULL;
if (fact) {
res = gst_element_factory_create (fact, "internal-decoder");
gst_object_unref (fact);
}
return res;
}
static GstElement *
get_encoder (GstCaps * caps)
{
GstElementFactory *fact = get_encoder_factory (caps);
GstElement *res = NULL;
if (fact) {
res = gst_element_factory_create (fact, "internal-encoder");
gst_object_unref (fact);
}
return res;
}
static GstFlowReturn
internal_chain (GstPad * pad, GstBuffer * buf)
{
GstSmartEncoder *smart_encoder =
g_object_get_qdata ((GObject *) pad, INTERNAL_ELEMENT);
return gst_pad_push (smart_encoder->srcpad, buf);
}
static gboolean
setup_recoder_pipeline (GstSmartEncoder * smart_encoder)
{
GstPad *tmppad;
/* Fast path */
if (G_UNLIKELY (smart_encoder->encoder))
return TRUE;
GST_DEBUG ("Creating internal decoder and encoder");
/* Create decoder/encoder */
smart_encoder->decoder = get_decoder (GST_PAD_CAPS (smart_encoder->sinkpad));
if (G_UNLIKELY (smart_encoder->decoder == NULL))
goto no_decoder;
gst_element_set_bus (smart_encoder->decoder, GST_ELEMENT_BUS (smart_encoder));
smart_encoder->encoder = get_encoder (GST_PAD_CAPS (smart_encoder->sinkpad));
if (G_UNLIKELY (smart_encoder->encoder == NULL))
goto no_encoder;
gst_element_set_bus (smart_encoder->encoder, GST_ELEMENT_BUS (smart_encoder));
GST_DEBUG ("Creating internal pads");
/* Create internal pads */
/* Source pad which we'll use to feed data to decoders */
smart_encoder->internal_srcpad = gst_pad_new ("internal_src", GST_PAD_SRC);
g_object_set_qdata ((GObject *) smart_encoder->internal_srcpad,
INTERNAL_ELEMENT, smart_encoder);
gst_pad_set_caps (smart_encoder->internal_srcpad,
GST_PAD_CAPS (smart_encoder->sinkpad));
gst_pad_set_active (smart_encoder->internal_srcpad, TRUE);
/* Sink pad which will get the buffers from the encoder.
* Note: We don't need an event function since we'll be discarding all
* of them. */
smart_encoder->internal_sinkpad = gst_pad_new ("internal_sink", GST_PAD_SINK);
g_object_set_qdata ((GObject *) smart_encoder->internal_sinkpad,
INTERNAL_ELEMENT, smart_encoder);
gst_pad_set_caps (smart_encoder->internal_sinkpad,
GST_PAD_CAPS (smart_encoder->sinkpad));
gst_pad_set_chain_function (smart_encoder->internal_sinkpad, internal_chain);
gst_pad_set_active (smart_encoder->internal_sinkpad, TRUE);
GST_DEBUG ("Linking pads to elements");
/* Link everything */
tmppad = gst_element_get_static_pad (smart_encoder->encoder, "src");
if (GST_PAD_LINK_FAILED (gst_pad_link (tmppad,
smart_encoder->internal_sinkpad)))
goto sinkpad_link_fail;
gst_object_unref (tmppad);
if (!gst_element_link (smart_encoder->decoder, smart_encoder->encoder))
goto encoder_decoder_link_fail;
tmppad = gst_element_get_static_pad (smart_encoder->decoder, "sink");
if (GST_PAD_LINK_FAILED (gst_pad_link (smart_encoder->internal_srcpad,
tmppad)))
goto srcpad_link_fail;
gst_object_unref (tmppad);
GST_DEBUG ("Done creating internal elements/pads");
return TRUE;
no_decoder:
{
GST_WARNING ("Couldn't find a decoder for %" GST_PTR_FORMAT,
GST_PAD_CAPS (smart_encoder->sinkpad));
return FALSE;
}
no_encoder:
{
GST_WARNING ("Couldn't find an encoder for %" GST_PTR_FORMAT,
GST_PAD_CAPS (smart_encoder->sinkpad));
return FALSE;
}
srcpad_link_fail:
{
gst_object_unref (tmppad);
GST_WARNING ("Couldn't link internal srcpad to decoder");
return FALSE;
}
sinkpad_link_fail:
{
gst_object_unref (tmppad);
GST_WARNING ("Couldn't link encoder to internal sinkpad");
return FALSE;
}
encoder_decoder_link_fail:
{
GST_WARNING ("Couldn't link decoder to encoder");
return FALSE;
}
}
static GstStateChangeReturn
gst_smart_encoder_find_elements (GstSmartEncoder * smart_encoder)
{
guint i, n;
GstCaps *tmpl, *st, *res;
GstElementFactory *dec, *enc;
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
if (G_UNLIKELY (smart_encoder->available_caps))
goto beach;
/* Iterate over all pad template caps and see if we have both an
* encoder and a decoder for those media types */
tmpl = gst_static_pad_template_get_caps (&src_template);
res = gst_caps_new_empty ();
n = gst_caps_get_size (tmpl);
for (i = 0; i < n; i++) {
st = gst_caps_copy_nth (tmpl, i);
GST_DEBUG_OBJECT (smart_encoder,
"Checking for available decoder and encoder for %" GST_PTR_FORMAT, st);
if (!(dec = get_decoder_factory (st))) {
gst_caps_unref (st);
continue;
}
gst_object_unref (dec);
if (!(enc = get_encoder_factory (st))) {
gst_caps_unref (st);
continue;
}
gst_object_unref (enc);
GST_DEBUG_OBJECT (smart_encoder, "OK");
gst_caps_append (res, st);
}
gst_caps_unref (tmpl);
if (gst_caps_is_empty (res))
ret = GST_STATE_CHANGE_FAILURE;
else
smart_encoder->available_caps = res;
GST_DEBUG_OBJECT (smart_encoder, "Done, available_caps:%" GST_PTR_FORMAT,
smart_encoder->available_caps);
beach:
return ret;
}
/******************************************
* GstElement vmethod implementations *
******************************************/
static GstStateChangeReturn
gst_smart_encoder_change_state (GstElement * element, GstStateChange transition)
{
GstSmartEncoder *smart_encoder;
GstStateChangeReturn ret;
g_return_val_if_fail (GST_IS_SMART_ENCODER (element),
GST_STATE_CHANGE_FAILURE);
smart_encoder = GST_SMART_ENCODER (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
/* Figure out which elements are available */
if ((ret =
gst_smart_encoder_find_elements (smart_encoder)) ==
GST_STATE_CHANGE_FAILURE)
goto beach;
break;
default:
break;
}
ret =
GST_ELEMENT_CLASS (gst_smart_encoder_parent_class)->change_state (element,
transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
smart_encoder_reset (smart_encoder);
break;
default:
break;
}
beach:
return ret;
}

View file

@ -0,0 +1,71 @@
/* GStreamer video re-encoder element
* Copyright (C) <2010> Edward Hervey <bilboed@bilboed.com>
*
* 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.
*/
#ifndef __SMART_ENCODER_H__
#define __SMART_ENCODER_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_SMART_ENCODER \
(gst_smart_encoder_get_type())
#define GST_SMART_ENCODER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SMART_ENCODER,GstSmartEncoder))
#define GST_SMART_ENCODER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SMART_ENCODER,GstSmartEncoderClass))
#define GST_IS_SMART_ENCODER(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SMART_ENCODER))
#define GST_IS_SMART_ENCODER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SMART_ENCODER))
typedef struct _GstSmartEncoder GstSmartEncoder;
typedef struct _GstSmartEncoderClass GstSmartEncoderClass;
struct _GstSmartEncoder {
GstElement element;
GstPad *sinkpad, *srcpad;
GstSegment *segment;
GstEvent *newsegment;
/* Pending GOP to be checked */
GList *pending_gop;
guint64 gop_start; /* GOP start in running time */
guint64 gop_stop; /* GOP end in running time */
/* Internal recoding elements */
GstPad *internal_sinkpad;
GstPad *internal_srcpad;
GstElement *decoder;
GstElement *encoder;
/* Available caps at runtime */
GstCaps *available_caps;
};
struct _GstSmartEncoderClass {
GstElementClass parent_class;
};
GType gst_smart_encoder_get_type(void);
G_END_DECLS
#endif /* __SMART_ENCODER_H__ */

View file

@ -0,0 +1,276 @@
/* GStreamer Stream Combiner
* Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
* (C) 2009 Nokia Corporation
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gststreamcombiner.h"
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%d",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC (gst_stream_combiner_debug);
#define GST_CAT_DEFAULT gst_stream_combiner_debug
G_DEFINE_TYPE (GstStreamCombiner, gst_stream_combiner, GST_TYPE_ELEMENT);
#define STREAMS_LOCK(obj) (g_mutex_lock(obj->lock))
#define STREAMS_UNLOCK(obj) (g_mutex_unlock(obj->lock))
static void gst_stream_combiner_dispose (GObject * object);
static GstPad *gst_stream_combiner_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name);
static void gst_stream_combiner_release_pad (GstElement * element,
GstPad * pad);
static void
gst_stream_combiner_class_init (GstStreamCombinerClass * klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
gobject_klass = (GObjectClass *) klass;
gstelement_klass = (GstElementClass *) klass;
gobject_klass->dispose = gst_stream_combiner_dispose;
GST_DEBUG_CATEGORY_INIT (gst_stream_combiner_debug, "streamcombiner", 0,
"Stream Combiner");
gst_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&src_template));
gst_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&sink_template));
gstelement_klass->request_new_pad =
GST_DEBUG_FUNCPTR (gst_stream_combiner_request_new_pad);
gstelement_klass->release_pad =
GST_DEBUG_FUNCPTR (gst_stream_combiner_release_pad);
gst_element_class_set_details_simple (gstelement_klass,
"streamcombiner", "Generic",
"Recombines streams splitted by the streamsplitter element",
"Edward Hervey <edward.hervey@collabora.co.uk>");
}
static void
gst_stream_combiner_dispose (GObject * object)
{
GstStreamCombiner *stream_combiner = (GstStreamCombiner *) object;
if (stream_combiner->lock) {
g_mutex_free (stream_combiner->lock);
stream_combiner->lock = NULL;
}
G_OBJECT_CLASS (gst_stream_combiner_parent_class)->dispose (object);
}
static GstFlowReturn
gst_stream_combiner_chain (GstPad * pad, GstBuffer * buf)
{
GstStreamCombiner *stream_combiner =
(GstStreamCombiner *) GST_PAD_PARENT (pad);
/* FIXME : IMPLEMENT */
/* with lock taken, check if we're the active stream, if not drop */
return gst_pad_push (stream_combiner->srcpad, buf);
}
static gboolean
gst_stream_combiner_sink_event (GstPad * pad, GstEvent * event)
{
GstStreamCombiner *stream_combiner =
(GstStreamCombiner *) GST_PAD_PARENT (pad);
/* FIXME : IMPLEMENT */
GST_DEBUG_OBJECT (pad, "Got event %s", GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CUSTOM_DOWNSTREAM:
if (gst_event_has_name (event, "stream-switching-eos")) {
gst_event_unref (event);
event = gst_event_new_eos ();
}
break;
default:
break;
}
/* NEW_SEGMENT : lock, wait for other stream to EOS, select stream, unlock, push */
/* EOS : lock, mark pad as unused, unlock , drop event */
/* CUSTOM_REAL_EOS : push EOS downstream */
/* FLUSH_START : lock, mark as flushing, unlock. if wasn't flushing forward */
/* FLUSH_STOP : lock, unmark as flushing, unlock, if was flushing forward */
/* OTHER : if selected pad forward */
return gst_pad_push_event (stream_combiner->srcpad, event);
}
static GstCaps *
gst_stream_combiner_sink_getcaps (GstPad * pad)
{
GstStreamCombiner *stream_combiner =
(GstStreamCombiner *) GST_PAD_PARENT (pad);
return gst_pad_peer_get_caps_reffed (stream_combiner->srcpad);
}
static gboolean
gst_stream_combiner_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstStreamCombiner *stream_combiner =
(GstStreamCombiner *) GST_PAD_PARENT (pad);
GstPad *peer;
gboolean res = FALSE;
GST_DEBUG_OBJECT (pad, "caps:%" GST_PTR_FORMAT, caps);
peer = gst_pad_get_peer (stream_combiner->srcpad);
if (peer) {
GST_DEBUG_OBJECT (peer, "Setting caps");
res = gst_pad_set_caps (peer, caps);
gst_object_unref (peer);
} else
GST_WARNING_OBJECT (stream_combiner, "sourcepad has no peer !");
return res;
}
static gboolean
gst_stream_combiner_src_event (GstPad * pad, GstEvent * event)
{
GstStreamCombiner *stream_combiner =
(GstStreamCombiner *) GST_PAD_PARENT (pad);
GstPad *sinkpad = NULL;
STREAMS_LOCK (stream_combiner);
if (stream_combiner->current)
sinkpad = stream_combiner->current;
else if (stream_combiner->sinkpads)
sinkpad = (GstPad *) stream_combiner->sinkpads->data;
STREAMS_UNLOCK (stream_combiner);
if (sinkpad)
/* Forward upstream as is */
return gst_pad_push_event (sinkpad, event);
return FALSE;
}
static gboolean
gst_stream_combiner_src_query (GstPad * pad, GstQuery * query)
{
GstStreamCombiner *stream_combiner =
(GstStreamCombiner *) GST_PAD_PARENT (pad);
GstPad *sinkpad = NULL;
STREAMS_LOCK (stream_combiner);
if (stream_combiner->current)
sinkpad = stream_combiner->current;
else if (stream_combiner->sinkpads)
sinkpad = (GstPad *) stream_combiner->sinkpads->data;
STREAMS_UNLOCK (stream_combiner);
if (sinkpad)
/* Forward upstream as is */
return gst_pad_peer_query (sinkpad, query);
return FALSE;
}
static void
gst_stream_combiner_init (GstStreamCombiner * stream_combiner)
{
stream_combiner->srcpad =
gst_pad_new_from_static_template (&src_template, "src");
gst_pad_set_event_function (stream_combiner->srcpad,
gst_stream_combiner_src_event);
gst_pad_set_query_function (stream_combiner->srcpad,
gst_stream_combiner_src_query);
gst_element_add_pad (GST_ELEMENT (stream_combiner), stream_combiner->srcpad);
stream_combiner->lock = g_mutex_new ();
}
static GstPad *
gst_stream_combiner_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name)
{
GstStreamCombiner *stream_combiner = (GstStreamCombiner *) element;
GstPad *sinkpad;
GST_DEBUG_OBJECT (element, "templ:%p, name:%s", templ, name);
sinkpad = gst_pad_new_from_static_template (&sink_template, name);
/* FIXME : No buffer alloc for the time being, it will resort to the fallback */
/* gst_pad_set_bufferalloc_function (sinkpad, gst_stream_combiner_buffer_alloc); */
gst_pad_set_chain_function (sinkpad, gst_stream_combiner_chain);
gst_pad_set_event_function (sinkpad, gst_stream_combiner_sink_event);
gst_pad_set_getcaps_function (sinkpad, gst_stream_combiner_sink_getcaps);
gst_pad_set_setcaps_function (sinkpad, gst_stream_combiner_sink_setcaps);
STREAMS_LOCK (stream_combiner);
stream_combiner->sinkpads =
g_list_append (stream_combiner->sinkpads, sinkpad);
gst_pad_set_active (sinkpad, TRUE);
gst_element_add_pad (element, sinkpad);
stream_combiner->cookie++;
STREAMS_UNLOCK (stream_combiner);
GST_DEBUG_OBJECT (element, "Returning pad %p", sinkpad);
return sinkpad;
}
static void
gst_stream_combiner_release_pad (GstElement * element, GstPad * pad)
{
GstStreamCombiner *stream_combiner = (GstStreamCombiner *) element;
GList *tmp;
GST_DEBUG_OBJECT (element, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
STREAMS_LOCK (stream_combiner);
tmp = g_list_find (stream_combiner->sinkpads, pad);
if (tmp) {
GstPad *pad = (GstPad *) tmp->data;
stream_combiner->sinkpads =
g_list_delete_link (stream_combiner->sinkpads, tmp);
stream_combiner->cookie++;
if (pad == stream_combiner->current) {
/* Deactivate current flow */
GST_DEBUG_OBJECT (element, "Removed pad was the current one");
stream_combiner->current = NULL;
}
GST_DEBUG_OBJECT (element, "Removing pad from ourself");
gst_element_remove_pad (element, pad);
}
STREAMS_UNLOCK (stream_combiner);
return;
}

View file

@ -0,0 +1,60 @@
/* GStreamer Stream Combiner
* Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
* (C) 2009 Nokia Corporation
*
* 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.
*/
#ifndef __GST_STREAMCOMBINER_H__
#define __GST_STREAMCOMBINER_H__
#include <gst/gst.h>
#define GST_TYPE_STREAM_COMBINER (gst_stream_combiner_get_type())
#define GST_STREAM_COMBINER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STREAM_COMBINER,GstStreamCombiner))
#define GST_STREAM_COMBINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STREAM_COMBINER,GstStreamCombinerClass))
#define GST_IS_STREAM_COMBINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STREAM_COMBINER))
#define GST_IS_STREAM_COMBINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STREAM_COMBINER))
typedef struct _GstStreamCombiner GstStreamCombiner;
typedef struct _GstStreamCombinerClass GstStreamCombinerClass;
struct _GstStreamCombiner {
GstElement parent;
GstPad *srcpad;
/* lock protects:
* * the current pad
* * the list of srcpads
*/
GMutex *lock;
/* Currently activated srcpad */
GstPad *current;
GList *sinkpads;
guint32 cookie;
};
struct _GstStreamCombinerClass {
GstElementClass parent;
};
GType gst_stream_combiner_get_type(void);
GstElement *gst_stream_combiner_new (gchar *name);
#endif /* __GST_STREAMCOMBINER_H__ */

View file

@ -0,0 +1,431 @@
/* GStreamer Stream Splitter
* Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
* (C) 2009 Nokia Corporation
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gststreamsplitter.h"
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE ("src_%d", GST_PAD_SRC, GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC (gst_stream_splitter_debug);
#define GST_CAT_DEFAULT gst_stream_splitter_debug
G_DEFINE_TYPE (GstStreamSplitter, gst_stream_splitter, GST_TYPE_ELEMENT);
#define STREAMS_LOCK(obj) (g_mutex_lock(obj->lock))
#define STREAMS_UNLOCK(obj) (g_mutex_unlock(obj->lock))
static void gst_stream_splitter_dispose (GObject * object);
static GstPad *gst_stream_splitter_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name);
static void gst_stream_splitter_release_pad (GstElement * element,
GstPad * pad);
static void
gst_stream_splitter_class_init (GstStreamSplitterClass * klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
gobject_klass = (GObjectClass *) klass;
gstelement_klass = (GstElementClass *) klass;
gobject_klass->dispose = gst_stream_splitter_dispose;
GST_DEBUG_CATEGORY_INIT (gst_stream_splitter_debug, "streamsplitter", 0,
"Stream Splitter");
gst_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&src_template));
gst_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&sink_template));
gstelement_klass->request_new_pad =
GST_DEBUG_FUNCPTR (gst_stream_splitter_request_new_pad);
gstelement_klass->release_pad =
GST_DEBUG_FUNCPTR (gst_stream_splitter_release_pad);
gst_element_class_set_details_simple (gstelement_klass,
"streamsplitter", "Generic",
"Splits streams based on their media type",
"Edward Hervey <edward.hervey@collabora.co.uk>");
}
static void
gst_stream_splitter_dispose (GObject * object)
{
GstStreamSplitter *stream_splitter = (GstStreamSplitter *) object;
if (stream_splitter->lock) {
g_mutex_free (stream_splitter->lock);
stream_splitter->lock = NULL;
}
G_OBJECT_CLASS (gst_stream_splitter_parent_class)->dispose (object);
}
static GstFlowReturn
gst_stream_splitter_chain (GstPad * pad, GstBuffer * buf)
{
GstStreamSplitter *stream_splitter =
(GstStreamSplitter *) GST_PAD_PARENT (pad);
GstFlowReturn res;
GstPad *srcpad = NULL;
STREAMS_LOCK (stream_splitter);
if (stream_splitter->current)
srcpad = gst_object_ref (stream_splitter->current);
STREAMS_UNLOCK (stream_splitter);
if (G_UNLIKELY (srcpad == NULL))
goto nopad;
if (G_UNLIKELY (stream_splitter->pending_events)) {
GList *tmp;
GST_DEBUG_OBJECT (srcpad, "Pushing out pending events");
for (tmp = stream_splitter->pending_events; tmp; tmp = tmp->next) {
GstEvent *event = (GstEvent *) tmp->data;
gst_pad_push_event (srcpad, event);
}
g_list_free (stream_splitter->pending_events);
stream_splitter->pending_events = NULL;
}
/* Forward to currently activated stream */
res = gst_pad_push (srcpad, buf);
gst_object_unref (srcpad);
return res;
nopad:
GST_WARNING_OBJECT (stream_splitter, "No output pad was configured");
return GST_FLOW_ERROR;
}
static gboolean
gst_stream_splitter_sink_event (GstPad * pad, GstEvent * event)
{
GstStreamSplitter *stream_splitter =
(GstStreamSplitter *) GST_PAD_PARENT (pad);
gboolean res = TRUE;
gboolean toall = FALSE;
gboolean store = FALSE;
gboolean eos = FALSE;
gboolean flushpending = FALSE;
/* FLUSH_START/STOP : forward to all
* EOS : transform to CUSTOM_REAL_EOS and forward to all
* INBAND events : store to send in chain function to selected chain
* OUT_OF_BAND events : send to all
*/
GST_DEBUG_OBJECT (stream_splitter, "Got event %s",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
flushpending = TRUE;
toall = TRUE;
break;
case GST_EVENT_FLUSH_START:
toall = TRUE;
break;
case GST_EVENT_EOS:
/* Replace with our custom eos event */
gst_event_unref (event);
event =
gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
gst_structure_empty_new ("stream-switching-eos"));
toall = TRUE;
eos = TRUE;
break;
default:
if (GST_EVENT_TYPE (event) & GST_EVENT_TYPE_SERIALIZED)
store = TRUE;
}
if (flushpending) {
GList *tmp;
for (tmp = stream_splitter->pending_events; tmp; tmp = tmp->next)
gst_event_unref ((GstEvent *) tmp->data);
g_list_free (stream_splitter->pending_events);
stream_splitter->pending_events = NULL;
}
if (store) {
stream_splitter->pending_events =
g_list_append (stream_splitter->pending_events, event);
} else if (toall || eos) {
GList *tmp;
guint32 cookie;
/* Send to all pads */
STREAMS_LOCK (stream_splitter);
resync:
if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
STREAMS_UNLOCK (stream_splitter);
/* No source pads */
gst_event_unref (event);
res = FALSE;
goto beach;
}
tmp = stream_splitter->srcpads;
cookie = stream_splitter->cookie;
while (tmp) {
GstPad *srcpad = (GstPad *) tmp->data;
STREAMS_UNLOCK (stream_splitter);
/* In case of EOS, we first push out the real one to flush out
* each streams (but which will be discarded in the streamcombiner)
* before our custom one (which will be converted back to and EOS
* in the streamcombiner) */
if (eos)
gst_pad_push_event (srcpad, gst_event_new_eos ());
gst_event_ref (event);
res = gst_pad_push_event (srcpad, event);
STREAMS_LOCK (stream_splitter);
if (G_UNLIKELY (cookie != stream_splitter->cookie))
goto resync;
tmp = tmp->next;
}
STREAMS_UNLOCK (stream_splitter);
gst_event_unref (event);
} else {
GstPad *pad;
/* Only send to current pad */
STREAMS_LOCK (stream_splitter);
pad = stream_splitter->current;
STREAMS_UNLOCK (stream_splitter);
if (pad)
res = gst_pad_push_event (pad, event);
else {
gst_event_unref (event);
res = FALSE;
}
}
beach:
return res;
}
static GstCaps *
gst_stream_splitter_sink_getcaps (GstPad * pad)
{
GstStreamSplitter *stream_splitter =
(GstStreamSplitter *) GST_PAD_PARENT (pad);
guint32 cookie;
GList *tmp;
GstCaps *res = NULL;
/* Return the combination of all downstream caps */
STREAMS_LOCK (stream_splitter);
resync:
if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
res = gst_caps_new_any ();
goto beach;
}
res = NULL;
cookie = stream_splitter->cookie;
tmp = stream_splitter->srcpads;
while (tmp) {
GstPad *srcpad = (GstPad *) tmp->data;
STREAMS_UNLOCK (stream_splitter);
if (res)
gst_caps_merge (res, gst_pad_peer_get_caps_reffed (srcpad));
else
res = gst_pad_peer_get_caps (srcpad);
STREAMS_LOCK (stream_splitter);
if (G_UNLIKELY (cookie != stream_splitter->cookie)) {
if (res)
gst_caps_unref (res);
goto resync;
}
tmp = tmp->next;
}
beach:
STREAMS_UNLOCK (stream_splitter);
return res;
}
static gboolean
gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstStreamSplitter *stream_splitter =
(GstStreamSplitter *) GST_PAD_PARENT (pad);
guint32 cookie;
GList *tmp;
gboolean res;
GST_DEBUG_OBJECT (stream_splitter, "caps %" GST_PTR_FORMAT, caps);
/* Try on all pads, choose the one that succeeds as the current stream */
STREAMS_LOCK (stream_splitter);
resync:
if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
res = FALSE;
goto beach;
}
res = FALSE;
tmp = stream_splitter->srcpads;
cookie = stream_splitter->cookie;
while (tmp) {
GstPad *srcpad = (GstPad *) tmp->data;
GstCaps *peercaps;
STREAMS_UNLOCK (stream_splitter);
peercaps = gst_pad_peer_get_caps_reffed (srcpad);
if (peercaps) {
res = gst_caps_can_intersect (caps, peercaps);
gst_caps_unref (peercaps);
}
STREAMS_LOCK (stream_splitter);
if (G_UNLIKELY (cookie != stream_splitter->cookie))
goto resync;
if (res) {
/* FIXME : we need to switch properly */
GST_DEBUG_OBJECT (srcpad, "Setting caps on this pad was succesfull");
stream_splitter->current = srcpad;
goto beach;
}
tmp = tmp->next;
}
beach:
STREAMS_UNLOCK (stream_splitter);
return res;
}
static gboolean
gst_stream_splitter_src_event (GstPad * pad, GstEvent * event)
{
GstStreamSplitter *stream_splitter =
(GstStreamSplitter *) GST_PAD_PARENT (pad);
GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
/* Forward upstream as is */
return gst_pad_push_event (stream_splitter->sinkpad, event);
}
static gboolean
gst_stream_splitter_src_query (GstPad * pad, GstQuery * query)
{
GstStreamSplitter *stream_splitter =
(GstStreamSplitter *) GST_PAD_PARENT (pad);
GST_DEBUG_OBJECT (pad, "%s", GST_QUERY_TYPE_NAME (query));
/* Forward upstream as is */
return gst_pad_peer_query (stream_splitter->sinkpad, query);
}
static void
gst_stream_splitter_init (GstStreamSplitter * stream_splitter)
{
stream_splitter->sinkpad =
gst_pad_new_from_static_template (&sink_template, "sink");
/* FIXME : No buffer alloc for the time being, it will resort to the fallback */
/* gst_pad_set_bufferalloc_function (stream_splitter->sinkpad, */
/* gst_stream_splitter_buffer_alloc); */
gst_pad_set_chain_function (stream_splitter->sinkpad,
gst_stream_splitter_chain);
gst_pad_set_event_function (stream_splitter->sinkpad,
gst_stream_splitter_sink_event);
gst_pad_set_getcaps_function (stream_splitter->sinkpad,
gst_stream_splitter_sink_getcaps);
gst_pad_set_setcaps_function (stream_splitter->sinkpad,
gst_stream_splitter_sink_setcaps);
gst_element_add_pad (GST_ELEMENT (stream_splitter), stream_splitter->sinkpad);
stream_splitter->lock = g_mutex_new ();
}
static GstPad *
gst_stream_splitter_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name)
{
GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
GstPad *srcpad;
srcpad = gst_pad_new_from_static_template (&src_template, name);
gst_pad_set_event_function (srcpad, gst_stream_splitter_src_event);
gst_pad_set_query_function (srcpad, gst_stream_splitter_src_query);
STREAMS_LOCK (stream_splitter);
stream_splitter->srcpads = g_list_append (stream_splitter->srcpads, srcpad);
gst_pad_set_active (srcpad, TRUE);
gst_element_add_pad (element, srcpad);
stream_splitter->cookie++;
STREAMS_UNLOCK (stream_splitter);
return srcpad;
}
static void
gst_stream_splitter_release_pad (GstElement * element, GstPad * pad)
{
GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
GList *tmp;
STREAMS_LOCK (stream_splitter);
tmp = g_list_find (stream_splitter->srcpads, pad);
if (tmp) {
GstPad *pad = (GstPad *) tmp->data;
stream_splitter->srcpads =
g_list_delete_link (stream_splitter->srcpads, tmp);
stream_splitter->cookie++;
if (pad == stream_splitter->current) {
/* Deactivate current flow */
GST_DEBUG_OBJECT (element, "Removed pad was the current one");
stream_splitter->current = NULL;
}
gst_element_remove_pad (element, pad);
}
STREAMS_UNLOCK (stream_splitter);
return;
}

View file

@ -0,0 +1,62 @@
/* GStreamer Stream Splitter
* Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
* (C) 2009 Nokia Corporation
*
* 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.
*/
#ifndef __GST_STREAMSPLITTER_H__
#define __GST_STREAMSPLITTER_H__
#include <gst/gst.h>
#define GST_TYPE_STREAM_SPLITTER (gst_stream_splitter_get_type())
#define GST_STREAM_SPLITTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STREAM_SPLITTER,GstStreamSplitter))
#define GST_STREAM_SPLITTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STREAM_SPLITTER,GstStreamSplitterClass))
#define GST_IS_STREAM_SPLITTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STREAM_SPLITTER))
#define GST_IS_STREAM_SPLITTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STREAM_SPLITTER))
typedef struct _GstStreamSplitter GstStreamSplitter;
typedef struct _GstStreamSplitterClass GstStreamSplitterClass;
struct _GstStreamSplitter {
GstElement parent;
GstPad *sinkpad;
/* lock protects:
* * the current pad
* * the list of srcpads
*/
GMutex *lock;
/* Currently activated srcpad */
GstPad *current;
GList *srcpads;
guint32 cookie;
/* List of pending in-band events */
GList *pending_events;
};
struct _GstStreamSplitterClass {
GstElementClass parent;
};
GType gst_stream_splitter_get_type(void);
GstElement *gst_stream_splitter_new (gchar *name);
#endif /* __GST_STREAMSPLITTER_H__ */

View file

@ -106,6 +106,7 @@ check_PROGRAMS = \
elements/audiotestsrc \
elements/decodebin \
elements/decodebin2 \
elements/encodebin \
elements/ffmpegcolorspace \
elements/gdpdepay \
elements/gdppay \
@ -298,6 +299,9 @@ elements_playbin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD)
elements_decodebin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_encodebin_LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la $(GST_BASE_LIBS) $(LDADD)
elements_encodebin_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_decodebin2_LDADD = $(GST_BASE_LIBS) $(LDADD)
elements_decodebin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)

View file

@ -9,6 +9,7 @@ audioresample
audiotestsrc
decodebin
decodebin2
encodebin
gdpdepay
gdppay
gnomevfssink

View file

@ -0,0 +1,742 @@
/* GStreamer unit test for gstprofile
*
* Copyright (C) <2009> Edward Hervey <edward.hervey@collabora.co.uk>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/pbutils/encoding-profile.h>
#include <gst/check/gstcheck.h>
/* Helper functions to create profiles */
static GstEncodingProfile *
create_ogg_vorbis_profile (guint presence, gchar * preset)
{
GstEncodingContainerProfile *cprof;
GstCaps *ogg, *vorbis;
ogg = gst_caps_new_simple ("application/ogg", NULL);
cprof =
gst_encoding_container_profile_new ((gchar *) "myprofile", NULL, ogg,
NULL);
gst_caps_unref (ogg);
vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL);
fail_unless (gst_encoding_container_profile_add_profile (cprof,
(GstEncodingProfile *) gst_encoding_audio_profile_new (vorbis, preset,
NULL, presence)));
gst_caps_unref (vorbis);
return (GstEncodingProfile *) cprof;
}
static GstEncodingProfile *
create_ogg_theora_vorbis_profile (guint theorapresence, guint vorbispresence)
{
GstEncodingContainerProfile *prof;
GstCaps *ogg, *vorbis, *theora;
ogg = gst_caps_new_simple ("application/ogg", NULL);
prof =
gst_encoding_container_profile_new ((gchar *) "myprofile", NULL, ogg,
NULL);
gst_caps_unref (ogg);
vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL);
fail_unless (gst_encoding_container_profile_add_profile (prof,
(GstEncodingProfile *) gst_encoding_audio_profile_new (vorbis, NULL,
NULL, vorbispresence)));
gst_caps_unref (vorbis);
theora = gst_caps_new_simple ("video/x-theora", NULL);
fail_unless (gst_encoding_container_profile_add_profile (prof,
(GstEncodingProfile *) gst_encoding_video_profile_new (theora, NULL,
NULL, theorapresence)));
gst_caps_unref (theora);
return (GstEncodingProfile *) prof;
}
GST_START_TEST (test_encodebin_states)
{
GstElement *ebin;
GstEncodingProfile *prof, *prof2;
GstCaps *ogg;
GstPad *srcpad;
GstPad *target;
/* Create an encodebin and check that it correctly changes states
* according to whether a profile is set or not */
ebin = gst_element_factory_make ("encodebin", NULL);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
/* At this point, the ghostpad has *NO* target */
target = gst_ghost_pad_get_target (GST_GHOST_PAD (srcpad));
fail_unless (target == NULL);
g_object_unref (srcpad);
/* No profile,
* switching to READY should succeed,
* but switching to PAUSED should fail
*/
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_READY),
GST_STATE_CHANGE_SUCCESS);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_FAILURE);
/* Set a profile on encodebin... */
ogg = gst_caps_new_simple ("application/ogg", NULL);
prof = (GstEncodingProfile *) gst_encoding_container_profile_new ((gchar *)
"myprofile", NULL, ogg, NULL);
gst_caps_unref (ogg);
g_object_set (ebin, "profile", prof, NULL);
/* ... and check the profile has been properly set */
g_object_get (ebin, "profile", &prof2, NULL);
fail_unless (gst_encoding_profile_is_equal (prof, prof2));
gst_encoding_profile_unref (prof);
gst_encoding_profile_unref (prof2);
/* Make sure we can go to PAUSED */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* At this point, the source pad *HAS* a target */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
target = gst_ghost_pad_get_target (GST_GHOST_PAD (srcpad));
fail_unless (target != NULL);
g_object_unref (target);
g_object_unref (srcpad);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_sink_pads_static)
{
GstElement *ebin;
GstEncodingProfile *prof;
GstPad *srcpad, *sinkpad;
/* Create an encodebin and check that it properly creates the sink pads
* for a single-stream profile with fixed presence */
ebin = gst_element_factory_make ("encodebin", NULL);
/* streamprofile that has a forced presence of 1 */
prof = create_ogg_vorbis_profile (1, NULL);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
g_object_unref (srcpad);
/* Check if the audio sink pad was properly created */
sinkpad = gst_element_get_static_pad (ebin, "audio_0");
fail_unless (sinkpad != NULL);
g_object_unref (sinkpad);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_sink_pads_nopreset_static)
{
GstElement *ebin;
GstEncodingProfile *prof;
/* Create an encodebin with a bogus preset and check it fails switching states */
ebin = gst_element_factory_make ("encodebin", NULL);
/* streamprofile that has a forced presence of 1 */
prof = create_ogg_vorbis_profile (1, (gchar *) "nowaythispresetexists");
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
/* It will go to READY... */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_READY),
GST_STATE_CHANGE_SUCCESS);
/* ... but to not PAUSED */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_FAILURE);
gst_element_set_state (ebin, GST_STATE_NULL);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_sink_pads_dynamic)
{
GstElement *ebin;
GstEncodingProfile *prof;
GstPad *srcpad, *sinkpad;
GstCaps *sinkcaps;
/* Create an encodebin and check that it properly creates the sink pads
* for a single-stream profile with a unfixed presence */
ebin = gst_element_factory_make ("encodebin", NULL);
/* streamprofile that has non-forced presence */
prof = create_ogg_vorbis_profile (0, NULL);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
g_object_unref (srcpad);
/* Check if the audio sink pad can be requested */
sinkpad = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (sinkpad != NULL);
gst_element_release_request_pad (ebin, sinkpad);
sinkpad = NULL;
/* Check again with the 'request-pad' signal */
sinkcaps = gst_caps_new_simple ("audio/x-raw-int", NULL);
g_signal_emit_by_name (ebin, "request-pad", sinkcaps, &sinkpad);
gst_caps_unref (sinkcaps);
fail_unless (sinkpad != NULL);
gst_element_release_request_pad (ebin, sinkpad);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_sink_pads_multiple_static)
{
GstElement *ebin;
GstEncodingProfile *prof;
GstPad *srcpad, *sinkpadvorbis, *sinkpadtheora;
/* Create an encodebin and check that it properly creates the sink pads */
ebin = gst_element_factory_make ("encodebin", NULL);
/* First try is with a streamprofile that has a forced presence of 1 */
prof = create_ogg_theora_vorbis_profile (1, 1);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
g_object_unref (srcpad);
/* Check if the audio sink pad was properly created */
sinkpadvorbis = gst_element_get_static_pad (ebin, "audio_0");
fail_unless (sinkpadvorbis != NULL);
g_object_unref (sinkpadvorbis);
/* Check if the video sink pad was properly created */
sinkpadtheora = gst_element_get_static_pad (ebin, "video_1");
fail_unless (sinkpadtheora != NULL);
g_object_unref (sinkpadtheora);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_sink_pads_multiple_dynamic)
{
GstElement *ebin;
GstEncodingProfile *prof;
GstPad *srcpad, *sinkpadvorbis, *sinkpadtheora;
/* Create an encodebin and check that it properly creates the sink pads
* for a multiple-stream with unfixed presence */
ebin = gst_element_factory_make ("encodebin", NULL);
/* multi-stream profile that has non-forced presence */
prof = create_ogg_theora_vorbis_profile (0, 0);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
g_object_unref (srcpad);
/* Check if the audio sink pad was properly created */
sinkpadvorbis = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (sinkpadvorbis != NULL);
g_object_unref (sinkpadvorbis);
/* Check if the video sink pad was properly created */
sinkpadtheora = gst_element_get_request_pad (ebin, "video_1");
fail_unless (sinkpadtheora != NULL);
g_object_unref (sinkpadtheora);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_sink_pads_dynamic_encoder)
{
GstElement *ebin;
GstEncodingProfile *prof;
GstPad *srcpad, *sinkpad = NULL;
GstCaps *vorbiscaps;
/* Create an encodebin and check that it properly creates the sink pads
* for a single-stream profile with a unfixed presence */
ebin = gst_element_factory_make ("encodebin", NULL);
/* streamprofile that has non-forced presence */
prof = create_ogg_vorbis_profile (0, NULL);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
g_object_unref (srcpad);
/* Check if the audio sink pad was properly created */
vorbiscaps = gst_caps_from_string ("audio/x-vorbis,channels=2,rate=44100");
g_signal_emit_by_name (ebin, "request-pad", vorbiscaps, &sinkpad);
gst_caps_unref (vorbiscaps);
fail_unless (sinkpad != NULL);
gst_element_release_request_pad (ebin, sinkpad);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
};
GST_END_TEST;
GST_START_TEST (test_encodebin_render_audio_static)
{
GstElement *ebin, *pipeline, *audiotestsrc, *fakesink;
GstEncodingProfile *prof;
GstBus *bus;
gboolean done = FALSE;
/* Create an encodebin and render 5s of vorbis/ogg */
pipeline = gst_pipeline_new ("encodebin-pipeline");
bus = gst_pipeline_get_bus ((GstPipeline *) pipeline);
audiotestsrc = gst_element_factory_make ("audiotestsrc", NULL);
g_object_set (audiotestsrc, "num-buffers", 10, NULL);
fakesink = gst_element_factory_make ("fakesink", NULL);
ebin = gst_element_factory_make ("encodebin", NULL);
prof = create_ogg_vorbis_profile (1, NULL);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
gst_bin_add_many ((GstBin *) pipeline, audiotestsrc, ebin, fakesink, NULL);
fail_unless (gst_element_link_many (audiotestsrc, ebin, fakesink, NULL));
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
GST_STATE_CHANGE_ASYNC);
while (!done) {
GstMessage *msg;
/* poll the bus until we get EOS without any errors */
msg = gst_bus_timed_pop (bus, GST_SECOND / 10);
if (msg) {
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
fail ("GST_MESSAGE_ERROR");
break;
case GST_MESSAGE_EOS:
done = TRUE;
break;
default:
break;
}
gst_message_unref (msg);
}
}
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
g_object_unref (bus);
gst_object_unref (pipeline);
}
GST_END_TEST;
GST_START_TEST (test_encodebin_render_audio_dynamic)
{
GstElement *ebin, *pipeline, *audiotestsrc, *fakesink;
GstEncodingProfile *prof;
GstBus *bus;
GstPad *sinkpad, *srcpad;
gboolean done = FALSE;
/* Create an encodebin and render 5s of vorbis/ogg */
pipeline = gst_pipeline_new ("encodebin-pipeline");
bus = gst_pipeline_get_bus ((GstPipeline *) pipeline);
audiotestsrc = gst_element_factory_make ("audiotestsrc", NULL);
g_object_set (audiotestsrc, "num-buffers", 10, NULL);
fakesink = gst_element_factory_make ("fakesink", NULL);
ebin = gst_element_factory_make ("encodebin", NULL);
prof = create_ogg_vorbis_profile (0, NULL);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
gst_bin_add_many ((GstBin *) pipeline, audiotestsrc, ebin, fakesink, NULL);
srcpad = gst_element_get_static_pad (audiotestsrc, "src");
fail_unless (srcpad != NULL);
sinkpad = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (sinkpad != NULL);
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
g_object_unref (srcpad);
g_object_unref (sinkpad);
fail_unless (gst_element_link (ebin, fakesink));
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
GST_STATE_CHANGE_ASYNC);
while (!done) {
GstMessage *msg;
/* poll the bus until we get EOS without any errors */
msg = gst_bus_timed_pop (bus, GST_SECOND / 10);
if (msg) {
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
fail ("GST_MESSAGE_ERROR");
break;
case GST_MESSAGE_EOS:
done = TRUE;
break;
default:
break;
}
gst_message_unref (msg);
}
}
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
g_object_unref (bus);
gst_object_unref (pipeline);
}
GST_END_TEST;
GST_START_TEST (test_encodebin_render_audio_video_static)
{
GstElement *ebin, *pipeline, *audiotestsrc, *videotestsrc, *fakesink;
GstEncodingProfile *prof;
GstBus *bus;
gboolean done = FALSE;
/* Create an encodebin and render 5s of vorbis/ogg */
pipeline = gst_pipeline_new ("encodebin-pipeline");
bus = gst_pipeline_get_bus ((GstPipeline *) pipeline);
audiotestsrc = gst_element_factory_make ("audiotestsrc", NULL);
g_object_set (audiotestsrc, "num-buffers", 10, NULL);
videotestsrc = gst_element_factory_make ("videotestsrc", NULL);
g_object_set (videotestsrc, "num-buffers", 5, NULL);
fakesink = gst_element_factory_make ("fakesink", NULL);
ebin = gst_element_factory_make ("encodebin", NULL);
prof = create_ogg_theora_vorbis_profile (1, 1);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
gst_bin_add_many ((GstBin *) pipeline, audiotestsrc, videotestsrc, ebin,
fakesink, NULL);
fail_unless (gst_element_link (videotestsrc, ebin));
fail_unless (gst_element_link_many (audiotestsrc, ebin, fakesink, NULL));
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
GST_STATE_CHANGE_ASYNC);
while (!done) {
GstMessage *msg;
/* poll the bus until we get EOS without any errors */
msg = gst_bus_timed_pop (bus, GST_SECOND / 10);
if (msg) {
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
fail ("GST_MESSAGE_ERROR");
break;
case GST_MESSAGE_EOS:
done = TRUE;
break;
default:
break;
}
gst_message_unref (msg);
}
}
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
g_object_unref (bus);
gst_object_unref (pipeline);
}
GST_END_TEST;
GST_START_TEST (test_encodebin_render_audio_video_dynamic)
{
GstElement *ebin, *pipeline, *audiotestsrc, *videotestsrc, *fakesink;
GstEncodingProfile *prof;
GstBus *bus;
gboolean done = FALSE;
GstPad *sinkpad, *srcpad;
/* Create an encodebin and render 5s of vorbis/ogg */
pipeline = gst_pipeline_new ("encodebin-pipeline");
bus = gst_pipeline_get_bus ((GstPipeline *) pipeline);
audiotestsrc = gst_element_factory_make ("audiotestsrc", NULL);
g_object_set (audiotestsrc, "num-buffers", 10, NULL);
videotestsrc = gst_element_factory_make ("videotestsrc", NULL);
g_object_set (videotestsrc, "num-buffers", 5, NULL);
fakesink = gst_element_factory_make ("fakesink", NULL);
ebin = gst_element_factory_make ("encodebin", NULL);
prof = create_ogg_theora_vorbis_profile (0, 0);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
gst_bin_add_many ((GstBin *) pipeline, audiotestsrc, videotestsrc, ebin,
fakesink, NULL);
fail_unless (gst_element_link (ebin, fakesink));
srcpad = gst_element_get_static_pad (audiotestsrc, "src");
sinkpad = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (srcpad != NULL);
fail_unless (sinkpad != NULL);
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
g_object_unref (srcpad);
g_object_unref (sinkpad);
srcpad = gst_element_get_static_pad (videotestsrc, "src");
sinkpad = gst_element_get_request_pad (ebin, "video_1");
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
g_object_unref (srcpad);
g_object_unref (sinkpad);
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
GST_STATE_CHANGE_ASYNC);
while (!done) {
GstMessage *msg;
/* poll the bus until we get EOS without any errors */
msg = gst_bus_timed_pop (bus, GST_SECOND / 10);
if (msg) {
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
fail ("GST_MESSAGE_ERROR");
break;
case GST_MESSAGE_EOS:
done = TRUE;
break;
default:
break;
}
gst_message_unref (msg);
}
}
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
g_object_unref (bus);
gst_object_unref (pipeline);
}
GST_END_TEST;
GST_START_TEST (test_encodebin_impossible_element_combination)
{
GstElement *ebin;
GstEncodingProfile *prof;
GstCaps *ogg, *x264;
ebin = gst_element_factory_make ("x264enc", NULL);
if (ebin == NULL) {
GST_DEBUG ("No available h264 encoder, skipping test");
return;
}
gst_object_unref (ebin);
/* Make sure that impossible combinations of encoders and muxer
* properly fail. In this case we try putting h264 in ogg.
*
* To properly test we abort early, we use a presence of zero for the
* h264 stream profile. */
ebin = gst_element_factory_make ("encodebin", NULL);
ogg = gst_caps_new_simple ("application/ogg", NULL);
prof = (GstEncodingProfile *) gst_encoding_container_profile_new ((gchar *)
"myprofile", NULL, ogg, NULL);
gst_caps_unref (ogg);
x264 = gst_caps_new_simple ("video/x-h264", NULL);
fail_unless (gst_encoding_container_profile_add_profile
(GST_ENCODING_CONTAINER_PROFILE (prof),
(GstEncodingProfile *) gst_encoding_video_profile_new (x264, NULL,
NULL, 0)));
gst_caps_unref (x264);
g_object_set (ebin, "profile", prof, NULL);
gst_encoding_profile_unref (prof);
/* It will go to READY... */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_READY),
GST_STATE_CHANGE_SUCCESS);
/* ... but to not PAUSED */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_FAILURE);
gst_element_set_state (ebin, GST_STATE_NULL);
gst_object_unref (ebin);
};
GST_END_TEST;
static Suite *
encodebin_suite (void)
{
Suite *s = suite_create ("encodebin element");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_encodebin_states);
tcase_add_test (tc_chain, test_encodebin_sink_pads_static);
tcase_add_test (tc_chain, test_encodebin_sink_pads_nopreset_static);
tcase_add_test (tc_chain, test_encodebin_sink_pads_dynamic);
tcase_add_test (tc_chain, test_encodebin_sink_pads_multiple_static);
tcase_add_test (tc_chain, test_encodebin_sink_pads_multiple_dynamic);
tcase_add_test (tc_chain, test_encodebin_sink_pads_dynamic_encoder);
tcase_add_test (tc_chain, test_encodebin_render_audio_static);
tcase_add_test (tc_chain, test_encodebin_render_audio_dynamic);
tcase_add_test (tc_chain, test_encodebin_render_audio_video_static);
tcase_add_test (tc_chain, test_encodebin_render_audio_video_dynamic);
tcase_add_test (tc_chain, test_encodebin_impossible_element_combination);
return s;
}
GST_CHECK_MAIN (encodebin);