mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
gst: New encoding plugin
https://bugzilla.gnome.org/show_bug.cgi?id=627476
This commit is contained in:
parent
82b4f9bfef
commit
8a3b45aa1f
24 changed files with 4266 additions and 169 deletions
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ GObject
|
|||
GstBin
|
||||
GstDecodeBin
|
||||
GstDecodeBin2
|
||||
GstEncodeBin
|
||||
GstPipeline
|
||||
GstPlayBaseBin
|
||||
GstPlayBin
|
||||
|
|
|
@ -8,6 +8,7 @@ GstPlaySink GstChildProxy
|
|||
GstSubtitleOverlay GstChildProxy
|
||||
GstDecodeBin2 GstChildProxy
|
||||
GstURIDecodeBin GstChildProxy
|
||||
GstEncodeBin GstChildProxy
|
||||
GstCddaBaseSrc GstURIHandler
|
||||
GstCdParanoiaSrc GstURIHandler
|
||||
GstAlsaSrc GstImplementsInterface GstMixer GstPropertyProbe
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
46
docs/plugins/inspect/plugin-encoding.xml
Normal file
46
docs/plugins/inspect/plugin-encoding.xml
Normal 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 <edward.hervey@collabora.co.uk></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>
|
|
@ -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 <otte@gnome.org></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 <otte@gnome.org></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 <otte@gnome.org></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 <otte@gnome.org></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 <otte@gnome.org></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 <otte@gnome.org></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 <otte@gnome.org></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 <otte@gnome.org></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
1
gst/encoding/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
gstencode-marshal.[ch]
|
41
gst/encoding/Makefile.am
Normal file
41
gst/encoding/Makefile.am
Normal 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
|
||||
|
1
gst/encoding/gstencode-marshal.list
Normal file
1
gst/encoding/gstencode-marshal.list
Normal file
|
@ -0,0 +1 @@
|
|||
OBJECT:BOXED
|
1658
gst/encoding/gstencodebin.c
Normal file
1658
gst/encoding/gstencodebin.c
Normal file
File diff suppressed because it is too large
Load diff
39
gst/encoding/gstencodebin.h
Normal file
39
gst/encoding/gstencodebin.h
Normal 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__ */
|
701
gst/encoding/gstsmartencoder.c
Normal file
701
gst/encoding/gstsmartencoder.c
Normal 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;
|
||||
}
|
71
gst/encoding/gstsmartencoder.h
Normal file
71
gst/encoding/gstsmartencoder.h
Normal 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__ */
|
276
gst/encoding/gststreamcombiner.c
Normal file
276
gst/encoding/gststreamcombiner.c
Normal 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;
|
||||
}
|
60
gst/encoding/gststreamcombiner.h
Normal file
60
gst/encoding/gststreamcombiner.h
Normal 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__ */
|
431
gst/encoding/gststreamsplitter.c
Normal file
431
gst/encoding/gststreamsplitter.c
Normal 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;
|
||||
}
|
62
gst/encoding/gststreamsplitter.h
Normal file
62
gst/encoding/gststreamsplitter.h
Normal 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__ */
|
|
@ -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)
|
||||
|
||||
|
|
1
tests/check/elements/.gitignore
vendored
1
tests/check/elements/.gitignore
vendored
|
@ -9,6 +9,7 @@ audioresample
|
|||
audiotestsrc
|
||||
decodebin
|
||||
decodebin2
|
||||
encodebin
|
||||
gdpdepay
|
||||
gdppay
|
||||
gnomevfssink
|
||||
|
|
742
tests/check/elements/encodebin.c
Normal file
742
tests/check/elements/encodebin.c
Normal 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);
|
Loading…
Reference in a new issue