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(audioconvert)
AG_GST_CHECK_PLUGIN(audiorate) AG_GST_CHECK_PLUGIN(audiorate)
AG_GST_CHECK_PLUGIN(audiotestsrc) AG_GST_CHECK_PLUGIN(audiotestsrc)
AG_GST_CHECK_PLUGIN(encoding)
AG_GST_CHECK_PLUGIN(ffmpegcolorspace) AG_GST_CHECK_PLUGIN(ffmpegcolorspace)
AG_GST_CHECK_PLUGIN(gdp) AG_GST_CHECK_PLUGIN(gdp)
AG_GST_CHECK_PLUGIN(playback) AG_GST_CHECK_PLUGIN(playback)
@ -914,6 +915,7 @@ gst/app/Makefile
gst/audioconvert/Makefile gst/audioconvert/Makefile
gst/audiorate/Makefile gst/audiorate/Makefile
gst/audiotestsrc/Makefile gst/audiotestsrc/Makefile
gst/encoding/Makefile
gst/ffmpegcolorspace/Makefile gst/ffmpegcolorspace/Makefile
gst/gdp/Makefile gst/gdp/Makefile
gst/playback/Makefile gst/playback/Makefile

View file

@ -50,7 +50,7 @@ MKDB_OPTIONS=--sgml-mode
# Extra options to supply to gtkdoc-fixref. # Extra options to supply to gtkdoc-fixref.
FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
--extra-dir=$(GST_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. # Used for dependencies.
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
@ -102,6 +102,7 @@ EXTRA_HFILES = \
$(top_srcdir)/gst/audioconvert/audioconvert.h \ $(top_srcdir)/gst/audioconvert/audioconvert.h \
$(top_srcdir)/gst/audioconvert/gstaudioconvert.h \ $(top_srcdir)/gst/audioconvert/gstaudioconvert.h \
$(top_srcdir)/gst/audiotestsrc/gstaudiotestsrc.h \ $(top_srcdir)/gst/audiotestsrc/gstaudiotestsrc.h \
$(top_srcdir)/gst/encoding/gstencodebin.h \
$(top_srcdir)/gst/ffmpegcolorspace/gstffmpegcolorspace.h \ $(top_srcdir)/gst/ffmpegcolorspace/gstffmpegcolorspace.h \
$(top_srcdir)/gst/gdp/gstgdpdepay.h \ $(top_srcdir)/gst/gdp/gstgdpdepay.h \
$(top_srcdir)/gst/gdp/gstgdppay.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-clockoverlay.xml" />
<xi:include href="xml/element-decodebin.xml" /> <xi:include href="xml/element-decodebin.xml" />
<xi:include href="xml/element-decodebin2.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-ffmpegcolorspace.xml" />
<xi:include href="xml/element-gdpdepay.xml" /> <xi:include href="xml/element-gdpdepay.xml" />
<xi:include href="xml/element-gdppay.xml" /> <xi:include href="xml/element-gdppay.xml" />
@ -80,6 +81,7 @@
<xi:include href="xml/plugin-audiotestsrc.xml" /> <xi:include href="xml/plugin-audiotestsrc.xml" />
<xi:include href="xml/plugin-cdparanoia.xml" /> <xi:include href="xml/plugin-cdparanoia.xml" />
<xi:include href="xml/plugin-decodebin.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-ffmpegcolorspace.xml" />
<xi:include href="xml/plugin-gdp.xml" /> <xi:include href="xml/plugin-gdp.xml" />
<xi:include href="xml/plugin-gio.xml" /> <xi:include href="xml/plugin-gio.xml" />

View file

@ -247,6 +247,22 @@ GstDecodeBin2
<SUBSECTION Standard> <SUBSECTION Standard>
</SECTION> </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> <SECTION>
<FILE>element-ffmpegcolorspace</FILE> <FILE>element-ffmpegcolorspace</FILE>
<TITLE>ffmpegcolorspace</TITLE> <TITLE>ffmpegcolorspace</TITLE>

View file

@ -158,6 +158,26 @@
<DEFAULT>TRUE</DEFAULT> <DEFAULT>TRUE</DEFAULT>
</ARG> </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> <ARG>
<NAME>GstXImageSink::display</NAME> <NAME>GstXImageSink::display</NAME>
<TYPE>gchar*</TYPE> <TYPE>gchar*</TYPE>
@ -218,6 +238,26 @@
<DEFAULT>TRUE</DEFAULT> <DEFAULT>TRUE</DEFAULT>
</ARG> </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> <ARG>
<NAME>GstV4lSrc::autoprobe</NAME> <NAME>GstV4lSrc::autoprobe</NAME>
<TYPE>gboolean</TYPE> <TYPE>gboolean</TYPE>
@ -3378,3 +3418,63 @@
<DEFAULT>NULL</DEFAULT> <DEFAULT>NULL</DEFAULT>
</ARG> </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 GstBin
GstDecodeBin GstDecodeBin
GstDecodeBin2 GstDecodeBin2
GstEncodeBin
GstPipeline GstPipeline
GstPlayBaseBin GstPlayBaseBin
GstPlayBin GstPlayBin

View file

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

View file

@ -471,3 +471,11 @@ GstPlaySink *gstplaysink
GstCaps *arg1 GstCaps *arg1
</SIGNAL> </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> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <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> </elements>
</plugin> </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/audiotestsrc \
elements/decodebin \ elements/decodebin \
elements/decodebin2 \ elements/decodebin2 \
elements/encodebin \
elements/ffmpegcolorspace \ elements/ffmpegcolorspace \
elements/gdpdepay \ elements/gdpdepay \
elements/gdppay \ elements/gdppay \
@ -298,6 +299,9 @@ elements_playbin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD) elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD)
elements_decodebin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) 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_LDADD = $(GST_BASE_LIBS) $(LDADD)
elements_decodebin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_decodebin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)

View file

@ -9,6 +9,7 @@ audioresample
audiotestsrc audiotestsrc
decodebin decodebin
decodebin2 decodebin2
encodebin
gdpdepay gdpdepay
gdppay gdppay
gnomevfssink 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);