summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Hervey <edward.hervey@collabora.co.uk>2010-08-13 17:36:38 +0200
committerEdward Hervey <edward.hervey@collabora.co.uk>2010-12-15 11:54:32 +0100
commit8a3b45aa1f5d6fcff5d8020a6ceddc0b7fc84353 (patch)
tree6dbb7030fa836fe86cf0815567c2495e6afe1e5b
parent82b4f9bfef332393f95325d00d28d97ae784748c (diff)
gst: New encoding plugin
https://bugzilla.gnome.org/show_bug.cgi?id=627476
-rw-r--r--configure.ac2
-rw-r--r--docs/plugins/Makefile.am3
-rw-r--r--docs/plugins/gst-plugins-base-plugins-docs.sgml2
-rw-r--r--docs/plugins/gst-plugins-base-plugins-sections.txt16
-rw-r--r--docs/plugins/gst-plugins-base-plugins.args100
-rw-r--r--docs/plugins/gst-plugins-base-plugins.hierarchy1
-rw-r--r--docs/plugins/gst-plugins-base-plugins.interfaces1
-rw-r--r--docs/plugins/gst-plugins-base-plugins.signals8
-rw-r--r--docs/plugins/inspect/plugin-encoding.xml46
-rw-r--r--docs/plugins/inspect/plugin-libvisual.xml168
-rw-r--r--gst/encoding/.gitignore1
-rw-r--r--gst/encoding/Makefile.am41
-rw-r--r--gst/encoding/gstencode-marshal.list1
-rw-r--r--gst/encoding/gstencodebin.c1658
-rw-r--r--gst/encoding/gstencodebin.h39
-rw-r--r--gst/encoding/gstsmartencoder.c701
-rw-r--r--gst/encoding/gstsmartencoder.h71
-rw-r--r--gst/encoding/gststreamcombiner.c276
-rw-r--r--gst/encoding/gststreamcombiner.h60
-rw-r--r--gst/encoding/gststreamsplitter.c431
-rw-r--r--gst/encoding/gststreamsplitter.h62
-rw-r--r--tests/check/Makefile.am4
-rw-r--r--tests/check/elements/.gitignore1
-rw-r--r--tests/check/elements/encodebin.c742
24 files changed, 4266 insertions, 169 deletions
diff --git a/configure.ac b/configure.ac
index b1df1c68e..64f877cf6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am
index 4b784337d..9beea7e55 100644
--- a/docs/plugins/Makefile.am
+++ b/docs/plugins/Makefile.am
@@ -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 \
diff --git a/docs/plugins/gst-plugins-base-plugins-docs.sgml b/docs/plugins/gst-plugins-base-plugins-docs.sgml
index 9bcbbf895..ea603d601 100644
--- a/docs/plugins/gst-plugins-base-plugins-docs.sgml
+++ b/docs/plugins/gst-plugins-base-plugins-docs.sgml
@@ -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" />
diff --git a/docs/plugins/gst-plugins-base-plugins-sections.txt b/docs/plugins/gst-plugins-base-plugins-sections.txt
index e241371f6..e94c45098 100644
--- a/docs/plugins/gst-plugins-base-plugins-sections.txt
+++ b/docs/plugins/gst-plugins-base-plugins-sections.txt
@@ -248,6 +248,22 @@ GstDecodeBin2
</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>
GstFFMpegCsp
diff --git a/docs/plugins/gst-plugins-base-plugins.args b/docs/plugins/gst-plugins-base-plugins.args
index 05ef80fc0..d3a40c515 100644
--- a/docs/plugins/gst-plugins-base-plugins.args
+++ b/docs/plugins/gst-plugins-base-plugins.args
@@ -159,6 +159,26 @@
</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>
<RANGE></RANGE>
@@ -219,6 +239,26 @@
</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>
<RANGE></RANGE>
@@ -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>
+
diff --git a/docs/plugins/gst-plugins-base-plugins.hierarchy b/docs/plugins/gst-plugins-base-plugins.hierarchy
index 4a355e659..80aed2e9a 100644
--- a/docs/plugins/gst-plugins-base-plugins.hierarchy
+++ b/docs/plugins/gst-plugins-base-plugins.hierarchy
@@ -56,6 +56,7 @@ GObject
GstBin
GstDecodeBin
GstDecodeBin2
+ GstEncodeBin
GstPipeline
GstPlayBaseBin
GstPlayBin
diff --git a/docs/plugins/gst-plugins-base-plugins.interfaces b/docs/plugins/gst-plugins-base-plugins.interfaces
index 36963b528..630084dd3 100644
--- a/docs/plugins/gst-plugins-base-plugins.interfaces
+++ b/docs/plugins/gst-plugins-base-plugins.interfaces
@@ -8,6 +8,7 @@ GstPlaySink GstChildProxy
GstSubtitleOverlay GstChildProxy
GstDecodeBin2 GstChildProxy
GstURIDecodeBin GstChildProxy
+GstEncodeBin GstChildProxy
GstCddaBaseSrc GstURIHandler
GstCdParanoiaSrc GstURIHandler
GstAlsaSrc GstImplementsInterface GstMixer GstPropertyProbe
diff --git a/docs/plugins/gst-plugins-base-plugins.signals b/docs/plugins/gst-plugins-base-plugins.signals
index 2f5fab5ae..621f880b3 100644
--- a/docs/plugins/gst-plugins-base-plugins.signals
+++ b/docs/plugins/gst-plugins-base-plugins.signals
@@ -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>
+
diff --git a/docs/plugins/inspect/plugin-encoding.xml b/docs/plugins/inspect/plugin-encoding.xml
new file mode 100644
index 000000000..ab61ef771
--- /dev/null
+++ b/docs/plugins/inspect/plugin-encoding.xml
@@ -0,0 +1,46 @@
+<plugin>
+ <name>encoding</name>
+ <description>various encoding-related elements</description>
+ <filename>../../gst/encoding/.libs/libgstencodebin.so</filename>
+ <basename>libgstencodebin.so</basename>
+ <version>0.10.31.1</version>
+ <license>LGPL</license>
+ <source>gst-plugins-base</source>
+ <package>GStreamer Base Plug-ins git</package>
+ <origin>Unknown package origin</origin>
+ <elements>
+ <element>
+ <name>encodebin</name>
+ <longname>Encoder Bin</longname>
+ <class>Generic/Bin/Encoder</class>
+ <description>Convenience encoding/muxing element</description>
+ <author>Edward Hervey &lt;edward.hervey@collabora.co.uk&gt;</author>
+ <pads>
+ <caps>
+ <name>audio_%d</name>
+ <direction>sink</direction>
+ <presence>request</presence>
+ <details>ANY</details>
+ </caps>
+ <caps>
+ <name>private_%d</name>
+ <direction>sink</direction>
+ <presence>request</presence>
+ <details>ANY</details>
+ </caps>
+ <caps>
+ <name>video_%d</name>
+ <direction>sink</direction>
+ <presence>request</presence>
+ <details>ANY</details>
+ </caps>
+ <caps>
+ <name>src</name>
+ <direction>source</direction>
+ <presence>always</presence>
+ <details>ANY</details>
+ </caps>
+ </pads>
+ </element>
+ </elements>
+</plugin> \ No newline at end of file
diff --git a/docs/plugins/inspect/plugin-libvisual.xml b/docs/plugins/inspect/plugin-libvisual.xml
index 22f4e8f04..0af3fc75d 100644
--- a/docs/plugins/inspect/plugin-libvisual.xml
+++ b/docs/plugins/inspect/plugin-libvisual.xml
@@ -9,173 +9,5 @@
<package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin>
<elements>
- <element>
- <name>libvisual_bumpscope</name>
- <longname>libvisual Bumpscope plugin plugin v.0.0.1</longname>
- <class>Visualization</class>
- <description>Bumpscope visual plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_corona</name>
- <longname>libvisual libvisual corona plugin plugin v.0.1</longname>
- <class>Visualization</class>
- <description>Libvisual corona plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_infinite</name>
- <longname>libvisual infinite plugin plugin v.0.1</longname>
- <class>Visualization</class>
- <description>Infinite visual plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_jakdaw</name>
- <longname>libvisual Jakdaw plugin plugin v.0.0.1</longname>
- <class>Visualization</class>
- <description>jakdaw visual plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_jess</name>
- <longname>libvisual jess plugin plugin v.0.1</longname>
- <class>Visualization</class>
- <description>Jess visual plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_lv_analyzer</name>
- <longname>libvisual libvisual analyzer plugin v.1.0</longname>
- <class>Visualization</class>
- <description>Libvisual analyzer plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_lv_scope</name>
- <longname>libvisual libvisual scope plugin v.0.1</longname>
- <class>Visualization</class>
- <description>Libvisual scope plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>libvisual_oinksie</name>
- <longname>libvisual oinksie plugin plugin v.0.1</longname>
- <class>Visualization</class>
- <description>Libvisual Oinksie visual plugin</description>
- <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
- <pads>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
- </caps>
- </pads>
- </element>
</elements>
</plugin> \ No newline at end of file
diff --git a/gst/encoding/.gitignore b/gst/encoding/.gitignore
new file mode 100644
index 000000000..ff4454589
--- /dev/null
+++ b/gst/encoding/.gitignore
@@ -0,0 +1 @@
+gstencode-marshal.[ch]
diff --git a/gst/encoding/Makefile.am b/gst/encoding/Makefile.am
new file mode 100644
index 000000000..0e287fad1
--- /dev/null
+++ b/gst/encoding/Makefile.am
@@ -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
+
diff --git a/gst/encoding/gstencode-marshal.list b/gst/encoding/gstencode-marshal.list
new file mode 100644
index 000000000..00f26ed2b
--- /dev/null
+++ b/gst/encoding/gstencode-marshal.list
@@ -0,0 +1 @@
+OBJECT:BOXED
diff --git a/gst/encoding/gstencodebin.c b/gst/encoding/gstencodebin.c
new file mode 100644
index 000000000..3ba5a403d
--- /dev/null
+++ b/gst/encoding/gstencodebin.c
@@ -0,0 +1,1658 @@
+/* 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "gstencodebin.h"
+#include "gstsmartencoder.h"
+#include "gststreamsplitter.h"
+#include "gststreamcombiner.h"
+#include <gst/gst-i18n-plugin.h>
+
+/**
+ * SECTION:element-encodebin
+ *
+ * encodebin provides a bin for encoding/muxing various streams according to
+ * a specified #GstEncodingProfile.
+ *
+ *
+ */
+
+
+/* TODO/FIXME
+ *
+ * Handling mp3!xing!idv3 and theora!ogg tagsetting scenarios:
+ * Once we have chosen a muxer:
+ * When a new stream is requested:
+ * If muxer is 'Formatter' OR doesn't have a TagSetter interface:
+ * Find a Formatter for the given stream (preferably with TagSetter)
+ * Insert that before muxer
+ **/
+
+#define fast_pad_link(a,b) gst_pad_link_full((a),(b),GST_PAD_LINK_CHECK_NOTHING)
+#define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING)
+
+/* generic templates */
+static GstStaticPadTemplate muxer_src_template =
+GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate video_sink_template =
+GST_STATIC_PAD_TEMPLATE ("video_%d",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS_ANY);
+static GstStaticPadTemplate audio_sink_template =
+GST_STATIC_PAD_TEMPLATE ("audio_%d",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS_ANY);
+/* static GstStaticPadTemplate text_sink_template = */
+/* GST_STATIC_PAD_TEMPLATE ("text_%d", */
+/* GST_PAD_SINK, */
+/* GST_PAD_REQUEST, */
+/* GST_STATIC_CAPS_ANY); */
+static GstStaticPadTemplate private_sink_template =
+GST_STATIC_PAD_TEMPLATE ("private_%d",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS_ANY);
+
+struct _GstEncodeBin
+{
+ GstBin parent;
+
+ /* the profile field is only valid if it could be entirely setup */
+ GstEncodingProfile *profile;
+
+ GList *streams; /* List of StreamGroup, not sorted */
+
+ GstElement *muxer;
+ GstPad *srcpad;
+
+ /* TRUE if in PAUSED/PLAYING */
+ gboolean active;
+
+ /* available muxers, encoders and parsers */
+ GList *muxers;
+ GList *encoders;
+ GList *parsers;
+
+ /* Increasing counter for unique pad name */
+ guint last_pad_id;
+
+ /* Cached caps for identification */
+ GstCaps *raw_video_caps;
+ GstCaps *raw_audio_caps;
+ /* GstCaps *raw_text_caps; */
+
+ guint queue_buffers_max;
+ guint queue_bytes_max;
+ guint64 queue_time_max;
+
+ guint64 tolerance;
+ gboolean avoid_reencoding;
+};
+
+struct _GstEncodeBinClass
+{
+ GstBinClass parent;
+
+ /* Action Signals */
+ GstPad *(*request_pad) (GstEncodeBin * encodebin, GstCaps * caps);
+};
+
+typedef struct _StreamGroup StreamGroup;
+
+struct _StreamGroup
+{
+ GstEncodeBin *ebin;
+ GstEncodingProfile *profile;
+ GstPad *ghostpad; /* Sink ghostpad */
+ GstElement *inqueue; /* Queue just after the ghostpad */
+ GstElement *splitter;
+ GList *converters; /* List of conversion GstElement */
+ GstElement *capsfilter; /* profile->restriction (if non-NULL/ANY) */
+ GstElement *encoder; /* Encoder (can be NULL) */
+ GstElement *combiner;
+ GstElement *parser;
+ GstElement *smartencoder;
+ GstElement *outfilter; /* Output capsfilter (streamprofile.format) */
+ GstElement *outqueue; /* Queue just before the muxer */
+};
+
+/* Default for queues (same defaults as queue element) */
+#define DEFAULT_QUEUE_BUFFERS_MAX 200
+#define DEFAULT_QUEUE_BYTES_MAX 10 * 1024 * 1024
+#define DEFAULT_QUEUE_TIME_MAX GST_SECOND
+#define DEFAULT_AUDIO_JITTER_TOLERANCE 20 * GST_MSECOND
+#define DEFAULT_AVOID_REENCODING FALSE
+
+#define DEFAULT_RAW_CAPS \
+ "video/x-raw-yuv; " \
+ "video/x-raw-rgb; " \
+ "video/x-raw-gray; " \
+ "audio/x-raw-int; " \
+ "audio/x-raw-float; " \
+ "text/plain; " \
+ "text/x-pango-markup; " \
+ "video/x-dvd-subpicture; " \
+ "subpicture/x-pgs"
+
+/* Properties */
+enum
+{
+ PROP_0,
+ PROP_PROFILE,
+ PROP_QUEUE_BUFFERS_MAX,
+ PROP_QUEUE_BYTES_MAX,
+ PROP_QUEUE_TIME_MAX,
+ PROP_AUDIO_JITTER_TOLERANCE,
+ PROP_AVOID_REENCODING,
+ PROP_LAST
+};
+
+/* Signals */
+enum
+{
+ SIGNAL_REQUEST_PAD,
+ LAST_SIGNAL
+};
+
+static guint gst_encode_bin_signals[LAST_SIGNAL] = { 0 };
+
+static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
+
+GST_DEBUG_CATEGORY_STATIC (gst_encode_bin_debug);
+#define GST_CAT_DEFAULT gst_encode_bin_debug
+
+G_DEFINE_TYPE (GstEncodeBin, gst_encode_bin, GST_TYPE_BIN);
+
+static void gst_encode_bin_dispose (GObject * object);
+static void gst_encode_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_encode_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static GstStateChangeReturn gst_encode_bin_change_state (GstElement * element,
+ GstStateChange transition);
+
+static GstPad *gst_encode_bin_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * name);
+static void gst_encode_bin_release_pad (GstElement * element, GstPad * pad);
+
+static gboolean
+gst_encode_bin_set_profile (GstEncodeBin * ebin, GstEncodingProfile * profile);
+static void gst_encode_bin_tear_down_profile (GstEncodeBin * ebin);
+static gboolean gst_encode_bin_setup_profile (GstEncodeBin * ebin,
+ GstEncodingProfile * profile);
+
+static StreamGroup *_create_stream_group (GstEncodeBin * ebin,
+ GstEncodingProfile * sprof, const gchar * sinkpadname, GstCaps * sinkcaps);
+static void stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup);
+static GstPad *gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin,
+ GstCaps * caps);
+
+static void
+gst_encode_bin_class_init (GstEncodeBinClass * klass)
+{
+ GObjectClass *gobject_klass;
+ GstElementClass *gstelement_klass;
+
+ gobject_klass = (GObjectClass *) klass;
+ gstelement_klass = (GstElementClass *) klass;
+
+ gobject_klass->dispose = gst_encode_bin_dispose;
+ gobject_klass->set_property = gst_encode_bin_set_property;
+ gobject_klass->get_property = gst_encode_bin_get_property;
+
+ /* Properties */
+ g_object_class_install_property (gobject_klass, PROP_PROFILE,
+ gst_param_spec_mini_object ("profile", "Profile",
+ "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_klass, PROP_QUEUE_BUFFERS_MAX,
+ g_param_spec_uint ("queue-bytes-max", "Max. size (kB)",
+ "Max. amount of data in the queue (bytes, 0=disable)",
+ 0, G_MAXUINT, DEFAULT_QUEUE_BYTES_MAX,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_klass, PROP_QUEUE_BYTES_MAX,
+ g_param_spec_uint ("queue-buffers-max", "Max. size (buffers)",
+ "Max. number of buffers in the queue (0=disable)", 0, G_MAXUINT,
+ DEFAULT_QUEUE_BUFFERS_MAX,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_klass, PROP_QUEUE_TIME_MAX,
+ g_param_spec_uint64 ("queue-time-max", "Max. size (ns)",
+ "Max. amount of data in the queue (in ns, 0=disable)", 0, G_MAXUINT64,
+ DEFAULT_QUEUE_TIME_MAX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_klass, PROP_AUDIO_JITTER_TOLERANCE,
+ g_param_spec_uint64 ("audio-jitter-tolerance", "Audio jitter tolerance",
+ "Amount of timestamp jitter/imperfection to allow on audio streams before inserting/dropping samples (ns)",
+ 0, G_MAXUINT64, DEFAULT_AUDIO_JITTER_TOLERANCE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_klass, PROP_AVOID_REENCODING,
+ g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding",
+ "Whether to re-encode portions of compatible video streams that lay on segment boundaries",
+ DEFAULT_AVOID_REENCODING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /* Signals */
+ gst_encode_bin_signals[SIGNAL_REQUEST_PAD] =
+ g_signal_new ("request-pad", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstEncodeBinClass,
+ request_pad), NULL, NULL, gst_encode_marshal_OBJECT__BOXED,
+ GST_TYPE_PAD, 1, GST_TYPE_CAPS);
+
+ klass->request_pad = gst_encode_bin_request_pad_signal;
+
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&muxer_src_template));
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&video_sink_template));
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&audio_sink_template));
+ /* gst_element_class_add_pad_template (gstelement_klass, */
+ /* gst_static_pad_template_get (&text_sink_template)); */
+ gst_element_class_add_pad_template (gstelement_klass,
+ gst_static_pad_template_get (&private_sink_template));
+
+ gstelement_klass->change_state =
+ GST_DEBUG_FUNCPTR (gst_encode_bin_change_state);
+ gstelement_klass->request_new_pad =
+ GST_DEBUG_FUNCPTR (gst_encode_bin_request_new_pad);
+ gstelement_klass->release_pad =
+ GST_DEBUG_FUNCPTR (gst_encode_bin_release_pad);
+
+ gst_element_class_set_details_simple (gstelement_klass,
+ "Encoder Bin",
+ "Generic/Bin/Encoder",
+ "Convenience encoding/muxing element",
+ "Edward Hervey <edward.hervey@collabora.co.uk>");
+}
+
+static void
+gst_encode_bin_dispose (GObject * object)
+{
+ GstEncodeBin *ebin = (GstEncodeBin *) object;
+
+ if (ebin->muxers)
+ gst_plugin_feature_list_free (ebin->muxers);
+
+ if (ebin->encoders)
+ gst_plugin_feature_list_free (ebin->encoders);
+
+ if (ebin->parsers)
+ gst_plugin_feature_list_free (ebin->parsers);
+
+ gst_encode_bin_tear_down_profile (ebin);
+
+ if (ebin->raw_video_caps)
+ gst_caps_unref (ebin->raw_video_caps);
+ if (ebin->raw_audio_caps)
+ gst_caps_unref (ebin->raw_audio_caps);
+ /* if (ebin->raw_text_caps) */
+ /* gst_caps_unref (ebin->raw_text_caps); */
+
+ G_OBJECT_CLASS (gst_encode_bin_parent_class)->dispose (object);
+}
+
+static void
+gst_encode_bin_init (GstEncodeBin * encode_bin)
+{
+ GstPadTemplate *tmpl;
+
+ encode_bin->muxers =
+ gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER,
+ GST_RANK_MARGINAL);
+ encode_bin->encoders =
+ gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER,
+ GST_RANK_MARGINAL);
+ encode_bin->parsers =
+ gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER,
+ GST_RANK_MARGINAL);
+
+ encode_bin->raw_video_caps =
+ gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb;video/x-raw-gray");
+ encode_bin->raw_audio_caps =
+ gst_caps_from_string ("audio/x-raw-int;audio/x-raw-float");
+ /* encode_bin->raw_text_caps = */
+ /* gst_caps_from_string ("text/plain;text/x-pango-markup"); */
+
+ encode_bin->queue_buffers_max = DEFAULT_QUEUE_BUFFERS_MAX;
+ encode_bin->queue_bytes_max = DEFAULT_QUEUE_BYTES_MAX;
+ encode_bin->queue_time_max = DEFAULT_QUEUE_TIME_MAX;
+ encode_bin->tolerance = DEFAULT_AUDIO_JITTER_TOLERANCE;
+ encode_bin->avoid_reencoding = DEFAULT_AVOID_REENCODING;
+
+ tmpl = gst_static_pad_template_get (&muxer_src_template);
+ encode_bin->srcpad = gst_ghost_pad_new_no_target_from_template ("src", tmpl);
+ gst_object_unref (tmpl);
+ gst_element_add_pad (GST_ELEMENT_CAST (encode_bin), encode_bin->srcpad);
+}
+
+static void
+gst_encode_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstEncodeBin *ebin = (GstEncodeBin *) object;
+
+ switch (prop_id) {
+ case PROP_PROFILE:
+ gst_encode_bin_set_profile (ebin,
+ (GstEncodingProfile *) gst_value_get_mini_object (value));
+ break;
+ case PROP_QUEUE_BUFFERS_MAX:
+ ebin->queue_buffers_max = g_value_get_uint (value);
+ break;
+ case PROP_QUEUE_BYTES_MAX:
+ ebin->queue_bytes_max = g_value_get_uint (value);
+ break;
+ case PROP_QUEUE_TIME_MAX:
+ ebin->queue_time_max = g_value_get_uint64 (value);
+ break;
+ case PROP_AUDIO_JITTER_TOLERANCE:
+ ebin->tolerance = g_value_get_uint64 (value);
+ break;
+ case PROP_AVOID_REENCODING:
+ ebin->avoid_reencoding = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_encode_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstEncodeBin *ebin = (GstEncodeBin *) object;
+
+ switch (prop_id) {
+ case PROP_PROFILE:
+ gst_value_set_mini_object (value, (GstMiniObject *) ebin->profile);
+ break;
+ case PROP_QUEUE_BUFFERS_MAX:
+ g_value_set_uint (value, ebin->queue_buffers_max);
+ break;
+ case PROP_QUEUE_BYTES_MAX:
+ g_value_set_uint (value, ebin->queue_bytes_max);
+ break;
+ case PROP_QUEUE_TIME_MAX:
+ g_value_set_uint64 (value, ebin->queue_time_max);
+ break;
+ case PROP_AUDIO_JITTER_TOLERANCE:
+ g_value_set_uint64 (value, ebin->tolerance);
+ break;
+ case PROP_AVOID_REENCODING:
+ g_value_set_boolean (value, ebin->avoid_reencoding);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static inline gboolean
+are_raw_caps (const GstCaps * caps)
+{
+ GstCaps *raw = gst_static_caps_get (&default_raw_caps);
+
+ if (gst_caps_can_intersect (caps, raw)) {
+ gst_caps_unref (raw);
+ return TRUE;
+ }
+ gst_caps_unref (raw);
+ return FALSE;
+}
+
+/* Returns the number of time a given stream profile is currently used
+ * in encodebin */
+static inline guint
+stream_profile_used_count (GstEncodeBin * ebin, GstEncodingProfile * sprof)
+{
+ guint nbprofused = 0;
+ GList *tmp;
+
+ for (tmp = ebin->streams; tmp; tmp = tmp->next) {
+ StreamGroup *sgroup = (StreamGroup *) tmp->data;
+
+ if (sgroup->profile == sprof)
+ nbprofused++;
+ }
+
+ return nbprofused;
+}
+
+static inline GstEncodingProfile *
+next_unused_stream_profile (GstEncodeBin * ebin, GType ptype, GstCaps * caps)
+{
+ GST_DEBUG_OBJECT (ebin, "ptype:%d, caps:%" GST_PTR_FORMAT, ptype, caps);
+
+ if (G_UNLIKELY (ptype == G_TYPE_NONE && caps != NULL)) {
+ /* Identify the profile type based on raw caps */
+ if (gst_caps_can_intersect (ebin->raw_video_caps, caps))
+ ptype = GST_TYPE_ENCODING_VIDEO_PROFILE;
+ else if (gst_caps_can_intersect (ebin->raw_audio_caps, caps))
+ ptype = GST_TYPE_ENCODING_AUDIO_PROFILE;
+ /* else if (gst_caps_can_intersect (ebin->raw_text_caps, caps)) */
+ /* ptype = GST_TYPE_ENCODING_TEXT_PROFILE; */
+ GST_DEBUG_OBJECT (ebin, "Detected profile type as being %d", ptype);
+ }
+
+ if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) {
+ const GList *tmp;
+
+ for (tmp =
+ gst_encoding_container_profile_get_profiles
+ (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); tmp;
+ tmp = tmp->next) {
+ GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
+
+ /* Pick an available Stream profile for which:
+ * * either it is of the compatibly raw type,
+ * * OR we can pass it through directly without encoding
+ */
+ if (G_TYPE_FROM_INSTANCE (sprof) == ptype) {
+ guint presence = gst_encoding_profile_get_presence (sprof);
+ GST_DEBUG ("Found a stream profile with the same type");
+ if ((presence == 0)
+ || (presence > stream_profile_used_count (ebin, sprof)))
+ return sprof;
+ } else if ((caps != NULL) && (ptype == G_TYPE_NONE)) {
+ GstCaps *outcaps;
+ gboolean res;
+
+ outcaps = gst_encoding_profile_get_output_caps (sprof);
+ GST_DEBUG ("Unknown stream, seeing if it's compatible with %"
+ GST_PTR_FORMAT, outcaps);
+ res = gst_caps_can_intersect (outcaps, caps);
+ gst_caps_unref (outcaps);
+
+ if (res)
+ return sprof;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static GstPad *
+gst_encode_bin_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * name)
+{
+ GstEncodeBin *ebin = (GstEncodeBin *) element;
+ GType ptype;
+ StreamGroup *sgroup;
+ GstEncodingProfile *sprof;
+
+ GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name);
+
+ /* Identify the stream group */
+
+ if (!strcmp (templ->name_template, "video_%d"))
+ ptype = GST_TYPE_ENCODING_VIDEO_PROFILE;
+ else if (!strcmp (templ->name_template, "audio_%d"))
+ ptype = GST_TYPE_ENCODING_AUDIO_PROFILE;
+ /* else if (!strcmp (templ->name_template, "text_%d")) */
+ /* ptype = GST_TYPE_ENCODING_TEXT_PROFILE; */
+ else
+ ptype = G_TYPE_NONE;
+
+ /* FIXME : Check uniqueness of pad */
+ /* FIXME : Check that the requested number is the last one, and if not,
+ * update the last_pad_id variable so that we don't create a pad with
+ * the same name/number in the future */
+
+ /* Find GstEncodingProfile which we need */
+ sprof = next_unused_stream_profile (ebin, ptype, NULL);
+ if (G_UNLIKELY (sprof == NULL))
+ goto no_stream_profile;
+
+ sgroup = _create_stream_group (ebin, sprof, name, NULL);
+ if (G_UNLIKELY (sgroup == NULL))
+ goto no_stream_group;
+
+ return sgroup->ghostpad;
+
+no_stream_profile:
+ {
+ GST_WARNING_OBJECT (ebin, "Couldn't find a compatible stream profile");
+ return NULL;
+ }
+
+no_stream_group:
+ {
+ GST_WARNING_OBJECT (ebin, "Couldn't create a StreamGroup");
+ return NULL;
+ }
+}
+
+static GstPad *
+gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, GstCaps * caps)
+{
+ GstEncodingProfile *sprof;
+ StreamGroup *sgroup;
+
+ GST_DEBUG_OBJECT (encodebin, "caps:%" GST_PTR_FORMAT, caps);
+
+ /* Figure out if we have a unused GstEncodingProfile we can use for
+ * these caps */
+ sprof = next_unused_stream_profile (encodebin, G_TYPE_NONE, caps);
+ if (G_UNLIKELY (sprof == NULL))
+ goto no_stream_profile;
+
+ sgroup = _create_stream_group (encodebin, sprof, NULL, caps);
+ if (G_UNLIKELY (sgroup == NULL))
+ goto no_stream_group;
+
+ return sgroup->ghostpad;
+
+no_stream_profile:
+ {
+ GST_WARNING_OBJECT (encodebin, "Couldn't find a compatible stream profile");
+ return NULL;
+ }
+
+no_stream_group:
+ {
+ GST_WARNING_OBJECT (encodebin, "Couldn't create a StreamGroup");
+ return NULL;
+ }
+}
+
+static inline StreamGroup *
+find_stream_group_from_pad (GstEncodeBin * ebin, GstPad * pad)
+{
+ GList *tmp;
+
+ for (tmp = ebin->streams; tmp; tmp = tmp->next) {
+ StreamGroup *sgroup = (StreamGroup *) tmp->data;
+ if (G_UNLIKELY (sgroup->ghostpad == pad))
+ return sgroup;
+ }
+
+ return NULL;
+}
+
+static void
+gst_encode_bin_release_pad (GstElement * element, GstPad * pad)
+{
+ GstEncodeBin *ebin = (GstEncodeBin *) element;
+ StreamGroup *sgroup;
+
+ /* Find the associated StreamGroup */
+
+ sgroup = find_stream_group_from_pad (ebin, pad);
+ if (G_UNLIKELY (sgroup == NULL))
+ goto no_stream_group;
+
+ /* Release objects/data associated with the StreamGroup */
+ stream_group_remove (ebin, sgroup);
+
+ return;
+
+no_stream_group:
+ {
+ GST_WARNING_OBJECT (ebin, "Couldn't find corresponding StreamGroup");
+ return;
+ }
+}
+
+/* Create a parser for the given stream profile */
+static inline GstElement *
+_get_parser (GstEncodeBin * ebin, GstEncodingProfile * sprof)
+{
+ GList *parsers1, *parsers, *tmp;
+ GstElement *parser = NULL;
+ GstElementFactory *parserfact = NULL;
+ const GstCaps *format;
+
+ format = gst_encoding_profile_get_format (sprof);
+
+ GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format);
+
+ /* FIXME : requesting twice the parsers twice is a bit ugly, we should
+ * have a method to request on more than one condition */
+ parsers1 =
+ gst_element_factory_list_filter (ebin->parsers, format,
+ GST_PAD_SRC, FALSE);
+ parsers =
+ gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE);
+ gst_plugin_feature_list_free (parsers1);
+
+ if (G_UNLIKELY (parsers == NULL)) {
+ GST_DEBUG ("Couldn't find any compatible parsers");
+ return NULL;
+ }
+
+ for (tmp = parsers; tmp; tmp = tmp->next) {
+ /* FIXME : We're only picking the first one so far */
+ /* FIXME : signal the user if he wants this */
+ parserfact = (GstElementFactory *) tmp->data;
+ break;
+ }
+
+ if (parserfact)
+ parser = gst_element_factory_create (parserfact, NULL);
+
+ gst_plugin_feature_list_free (parsers);
+
+ return parser;
+}
+
+static GstElement *
+_create_element_and_set_preset (GstElementFactory * factory,
+ const gchar * preset, const gchar * name)
+{
+ GstElement *res = NULL;
+
+ GST_DEBUG ("Creating element from factory %s",
+ GST_PLUGIN_FEATURE_NAME (factory));
+ res = gst_element_factory_create (factory, name);
+ if (preset && GST_IS_PRESET (res) &&
+ !gst_preset_load_preset (GST_PRESET (res), preset)) {
+ GST_WARNING ("Couldn't set preset [%s] on element [%s]",
+ preset, GST_PLUGIN_FEATURE_NAME (factory));
+ gst_object_unref (res);
+ res = NULL;
+ }
+
+ return res;
+}
+
+/* Create the encoder for the given stream profile */
+static inline GstElement *
+_get_encoder (GstEncodeBin * ebin, GstEncodingProfile * sprof)
+{
+ GList *encoders, *tmp;
+ GstElement *encoder = NULL;
+ GstElementFactory *encoderfact = NULL;
+ const GstCaps *format;
+ const gchar *preset;
+
+ format = gst_encoding_profile_get_format (sprof);
+ preset = gst_encoding_profile_get_preset (sprof);
+
+ GST_DEBUG ("Getting list of encoders for format %" GST_PTR_FORMAT, format);
+
+ /* If stream caps are raw, return identity */
+ if (G_UNLIKELY (are_raw_caps (format))) {
+ GST_DEBUG ("Stream format is raw, returning identity as the encoder");
+ encoder = gst_element_factory_make ("identity", NULL);
+ goto beach;
+ }
+
+ encoders =
+ gst_element_factory_list_filter (ebin->encoders, format,
+ GST_PAD_SRC, FALSE);
+
+ if (G_UNLIKELY (encoders == NULL)) {
+ GST_DEBUG ("Couldn't find any compatible encoders");
+ goto beach;
+ }
+
+ for (tmp = encoders; tmp; tmp = tmp->next) {
+ encoderfact = (GstElementFactory *) tmp->data;
+ if ((encoder = _create_element_and_set_preset (encoderfact, preset, NULL)))
+ break;
+ }
+
+ gst_plugin_feature_list_free (encoders);
+
+beach:
+ return encoder;
+}
+
+static GstPad *
+gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
+ const gchar * name)
+{
+ GstPad *newpad = NULL;
+ GstElementClass *oclass;
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->request_new_pad)
+ newpad = (oclass->request_new_pad) (element, templ, name);
+
+ if (newpad)
+ gst_object_ref (newpad);
+
+ return newpad;
+}
+
+static GstPad *
+gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ)
+{
+ GstPad *ret = NULL;
+ GstPadPresence presence;
+
+ /* If this function is ever exported, we need check the validity of `element'
+ * and `templ', and to make sure the template actually belongs to the
+ * element. */
+
+ presence = GST_PAD_TEMPLATE_PRESENCE (templ);
+
+ switch (presence) {
+ case GST_PAD_ALWAYS:
+ case GST_PAD_SOMETIMES:
+ ret = gst_element_get_static_pad (element, templ->name_template);
+ if (!ret && presence == GST_PAD_ALWAYS)
+ g_warning
+ ("Element %s has an ALWAYS template %s, but no pad of the same name",
+ GST_OBJECT_NAME (element), templ->name_template);
+ break;
+
+ case GST_PAD_REQUEST:
+ ret = gst_element_request_pad (element, templ, NULL);
+ break;
+ }
+
+ return ret;
+}
+
+/* FIXME : Improve algorithm for finding compatible muxer sink pad */
+static inline GstPad *
+get_compatible_muxer_sink_pad (GstEncodeBin * ebin, GstElement * encoder,
+ const GstCaps * sinkcaps)
+{
+ GstPad *sinkpad;
+ GstPadTemplate *srctempl = NULL;
+ GstPadTemplate *sinktempl;
+
+ if (encoder) {
+ GstPad *srcpad;
+ srcpad = gst_element_get_static_pad (encoder, "src");
+
+ srctempl = gst_pad_get_pad_template (srcpad);
+
+ GST_DEBUG_OBJECT (ebin,
+ "Attempting to find pad from muxer %s compatible with %s:%s",
+ GST_ELEMENT_NAME (ebin->muxer), GST_DEBUG_PAD_NAME (srcpad));
+
+ gst_object_unref (srcpad);
+ sinktempl = gst_element_get_compatible_pad_template (ebin->muxer, srctempl);
+ } else {
+ srctempl =
+ gst_pad_template_new ("whatever", GST_PAD_SRC, GST_PAD_ALWAYS,
+ gst_caps_copy (sinkcaps));
+ g_assert (srctempl != NULL);
+ sinktempl = gst_element_get_compatible_pad_template (ebin->muxer, srctempl);
+ g_object_unref (srctempl);
+ }
+
+ if (G_UNLIKELY (sinktempl == NULL))
+ goto no_template;
+
+ sinkpad = gst_element_get_pad_from_template (ebin->muxer, sinktempl);
+
+ return sinkpad;
+
+no_template:
+ {
+ GST_WARNING_OBJECT (ebin, "No compatible pad available on muxer");
+ return NULL;
+ }
+}
+
+/* FIXME : Add handling of streams that don't need encoding */
+/* FIXME : Add handling of streams that don't require conversion elements */
+/*
+ * Create the elements, StreamGroup, add the sink pad, link it to the muxer
+ *
+ * sinkpadname: If non-NULL, that name will be assigned to the sink ghost pad
+ * sinkcaps: If non-NULL will be used to figure out how to setup the group */
+static StreamGroup *
+_create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
+ const gchar * sinkpadname, GstCaps * sinkcaps)
+{
+ StreamGroup *sgroup = NULL;
+ GstPad *sinkpad, *srcpad, *muxerpad;
+ /* Element we will link to the encoder */
+ GstElement *last = NULL;
+ GList *tmp, *tosync = NULL;
+ const GstCaps *format;
+ const GstCaps *restriction;
+
+ format = gst_encoding_profile_get_format (sprof);
+ restriction = gst_encoding_profile_get_restriction (sprof);
+
+ GST_DEBUG ("Creating group. format %" GST_PTR_FORMAT ", for caps %"
+ GST_PTR_FORMAT, format, sinkcaps);
+ GST_DEBUG ("avoid_reencoding:%d", ebin->avoid_reencoding);
+
+ sgroup = g_slice_new0 (StreamGroup);
+ sgroup->ebin = ebin;
+ sgroup->profile = sprof;
+
+ /* NOTE for people reading this code:
+ *
+ * We construct the group starting by the furthest downstream element
+ * and making our way up adding/syncing/linking as we go.
+ *
+ * There are two parallel paths:
+ * * One for raw data which goes through converters and encoders
+ * * One for already encoded data
+ */
+
+ /* Exception to the rule above:
+ * We check if we have an available encoder so we can abort early */
+ /* FIXME : What if we only want to do passthrough ??? */
+ GST_LOG ("Checking for encoder availability");
+ sgroup->encoder = _get_encoder (ebin, sprof);
+ if (G_UNLIKELY (sgroup->encoder == NULL))
+ goto no_encoder;
+
+ /* Muxer
+ * We first figure out if the muxer has a sinkpad compatible with the selected
+ * profile */
+ muxerpad = get_compatible_muxer_sink_pad (ebin, NULL, format);
+ if (G_UNLIKELY (muxerpad == NULL))
+ goto no_muxer_pad;
+
+ /* Output Queue.
+ * We only use a 1buffer long queue here, the actual queueing will be done
+ * in the intput queue */
+ last = sgroup->outqueue = gst_element_factory_make ("queue", NULL);
+ g_object_set (sgroup->outqueue, "max-size-buffers", (guint32) 1,
+ "max-size-bytes", (guint32) 0, "max-size-time", (guint64) 0, NULL);
+
+ gst_bin_add (GST_BIN (ebin), sgroup->outqueue);
+ tosync = g_list_append (tosync, sgroup->outqueue);
+ srcpad = gst_element_get_static_pad (sgroup->outqueue, "src");
+ if (G_UNLIKELY (fast_pad_link (srcpad, muxerpad) != GST_PAD_LINK_OK)) {
+ goto muxer_link_failure;
+ }
+ gst_object_unref (srcpad);
+ gst_object_unref (muxerpad);
+
+ /* Output capsfilter
+ * This will receive the format caps from the streamprofile */
+ GST_DEBUG ("Adding output capsfilter for %" GST_PTR_FORMAT, format);
+ sgroup->outfilter = gst_element_factory_make ("capsfilter", NULL);
+ g_object_set (sgroup->outfilter, "caps", format, NULL);
+
+ gst_bin_add (GST_BIN (ebin), sgroup->outfilter);
+ tosync = g_list_append (tosync, sgroup->outfilter);
+ if (G_UNLIKELY (!fast_element_link (sgroup->outfilter, last)))
+ goto outfilter_link_failure;
+ last = sgroup->outfilter;
+
+
+ /* FIXME :
+ *
+ * The usage of parsers in encoding/muxing scenarios is
+ * just too undefined to just use as-is.
+ *
+ * Take the use-case where you want to re-mux a stream of type
+ * "my/media". You create a StreamEncodingProfile with that type
+ * as the target (as-is). And you use decodebin2/uridecodebin
+ * upstream.
+ *
+ * * demuxer exposes "my/media"
+ * * a parser is available for "my/media" which has a source pad
+ * caps of "my/media,parsed=True"
+ * * decodebin2/uridecodebin exposes a new pad with the parsed caps
+ * * You request a new stream from encodebin, which will match the
+ * streamprofile and creates a group (i.e. going through this method)
+ * There is a matching parser (the same used in the decoder) whose
+ * source pad caps intersects with the stream profile caps, you
+ * therefore use it...
+ * * ... but that parser has a "my/media,parsed=False" sink pad caps
+ * * ... and you can't link your decodebin pad to encodebin.
+ *
+ * In the end, it comes down to parsers only taking into account the
+ * decoding use-cases.
+ *
+ * One way to solve that might be to :
+ * * Make parsers sink pad caps be "framed={False,True}" and the
+ * source pad caps be "framed=True"
+ * * Modify decodebin2 accordingly to avoid looping and chaining
+ * an infinite number of parsers
+ *
+ * Another way would be to have "well-known" caps properties to specify
+ * whether a stream has been parsed or not.
+ * * currently we fail. aacparse uses 'framed' and mp3parse uses 'parsed'
+ */
+ /* FIXME : Re-enable once parser situation is un-$#*@(%$#ed */
+#if 0
+ /* Parser.
+ * FIXME : identify smart parsers (used for re-encoding) */
+ sgroup->parser = _get_parser (ebin, sprof);
+
+ if (sgroup->parser != NULL) {
+ GST_DEBUG ("Got a parser %s", GST_ELEMENT_NAME (sgroup->parser));
+ gst_bin_add (GST_BIN (ebin), sgroup->parser);
+ tosync = g_list_append (tosync, sgroup->parser);
+ if (G_UNLIKELY (!gst_element_link (sgroup->parser, last)))
+ goto parser_link_failure;
+ last = sgroup->parser;
+ }
+#endif
+
+ /* Stream combiner */
+ sgroup->combiner = g_object_new (GST_TYPE_STREAM_COMBINER, NULL);
+
+ gst_bin_add (GST_BIN (ebin), sgroup->combiner);
+ tosync = g_list_append (tosync, sgroup->combiner);
+ if (G_UNLIKELY (!fast_element_link (sgroup->combiner, last)))
+ goto combiner_link_failure;
+
+
+ /* Stream splitter */
+ sgroup->splitter = g_object_new (GST_TYPE_STREAM_SPLITTER, NULL);
+
+ gst_bin_add (GST_BIN (ebin), sgroup->splitter);
+ tosync = g_list_append (tosync, sgroup->splitter);
+
+ /* Input queue
+ * FIXME : figure out what max-size to use for the input queue */
+ sgroup->inqueue = gst_element_factory_make ("queue", NULL);
+ g_object_set (sgroup->inqueue, "max-size-buffers",
+ (guint32) ebin->queue_buffers_max, "max-size-bytes",
+ (guint32) ebin->queue_bytes_max, "max-size-time",
+ (guint64) ebin->queue_time_max, NULL);
+
+ gst_bin_add (GST_BIN (ebin), sgroup->inqueue);
+ tosync = g_list_append (tosync, sgroup->inqueue);
+ if (G_UNLIKELY (!fast_element_link (sgroup->inqueue, sgroup->splitter)))
+ goto splitter_link_failure;
+
+ /* Expose input queue sink pad as ghostpad */
+ sinkpad = gst_element_get_static_pad (sgroup->inqueue, "sink");
+ if (sinkpadname == NULL) {
+ gchar *pname =
+ g_strdup_printf ("%s_%d", gst_encoding_profile_get_type_nick (sprof),
+ ebin->last_pad_id++);
+ GST_DEBUG ("Adding ghost pad %s", pname);
+ sgroup->ghostpad = gst_ghost_pad_new (pname, sinkpad);
+ g_free (pname);
+ } else
+ sgroup->ghostpad = gst_ghost_pad_new (sinkpadname, sinkpad);
+ gst_object_unref (sinkpad);
+
+
+ /* Path 1 : Already-encoded data */
+ sinkpad = gst_element_request_pad (sgroup->combiner, NULL, "passthroughsink");
+ if (G_UNLIKELY (sinkpad == NULL))
+ goto no_combiner_sinkpad;
+
+ if (ebin->avoid_reencoding) {
+ GstCaps *tmpcaps;
+
+ GST_DEBUG ("Asked to use Smart Encoder");
+ sgroup->smartencoder = g_object_new (GST_TYPE_SMART_ENCODER, NULL);
+
+ /* Check if stream format is compatible */
+ srcpad = gst_element_get_static_pad (sgroup->smartencoder, "src");
+ tmpcaps = gst_pad_get_caps (srcpad);
+ if (!gst_caps_can_intersect (tmpcaps, format)) {
+ GST_DEBUG ("We don't have a smart encoder for the stream format");
+ gst_object_unref (sgroup->smartencoder);
+ sgroup->smartencoder = NULL;
+ } else {
+ gst_bin_add ((GstBin *) ebin, sgroup->smartencoder);
+ fast_pad_link (srcpad, sinkpad);
+ tosync = g_list_append (tosync, sgroup->smartencoder);
+ sinkpad = gst_element_get_static_pad (sgroup->smartencoder, "sink");
+ }
+ gst_caps_unref (tmpcaps);
+ g_object_unref (srcpad);
+ }
+
+ srcpad = gst_element_request_pad (sgroup->splitter, NULL, "passthroughsrc");
+ if (G_UNLIKELY (srcpad == NULL))
+ goto no_splitter_srcpad;
+
+ /* Go straight to splitter */
+ if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK))
+ goto passthrough_link_failure;
+ g_object_unref (sinkpad);
+ g_object_unref (srcpad);
+
+
+ /* Path 2 : Conversion / Encoding */
+
+ /* 1. Create the encoder */
+ GST_LOG ("Adding encoder");
+ last = sgroup->encoder;
+ gst_bin_add ((GstBin *) ebin, sgroup->encoder);
+ tosync = g_list_append (tosync, sgroup->encoder);
+
+ sinkpad = gst_element_request_pad (sgroup->combiner, NULL, "encodingsink");
+ if (G_UNLIKELY (sinkpad == NULL))
+ goto no_combiner_sinkpad;
+ srcpad = gst_element_get_static_pad (sgroup->encoder, "src");
+ if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK))
+ goto encoder_link_failure;
+ g_object_unref (sinkpad);
+ g_object_unref (srcpad);
+
+
+ /* 3. Create the conversion/restriction elements */
+ /* 3.1. capsfilter */
+ if (restriction && !gst_caps_is_any (restriction)) {
+ GST_LOG ("Adding capsfilter for restriction caps : %" GST_PTR_FORMAT,
+ restriction);
+
+ last = sgroup->capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ g_object_set (sgroup->capsfilter, "caps", restriction, NULL);
+ gst_bin_add ((GstBin *) ebin, sgroup->capsfilter);
+ tosync = g_list_append (tosync, sgroup->capsfilter);
+ fast_element_link (sgroup->capsfilter, sgroup->encoder);
+ }
+
+ /* 3.2. restriction elements */
+ /* FIXME : Once we have properties for specific converters, use those */
+ if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
+ GstElement *cspace, *scale, *vrate, *cspace2;
+
+ GST_LOG ("Adding conversion elements for video stream");
+
+ cspace = gst_element_factory_make ("ffmpegcolorspace", NULL);
+ scale = gst_element_factory_make ("videoscale", NULL);
+ /* 4-tap scaling and black borders */
+ g_object_set (scale, "method", 2, "add-borders", TRUE, NULL);
+ cspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
+
+ gst_bin_add_many ((GstBin *) ebin, cspace, scale, cspace2, NULL);
+ tosync = g_list_append (tosync, cspace);
+ tosync = g_list_append (tosync, scale);
+ tosync = g_list_append (tosync, cspace2);
+
+ sgroup->converters = g_list_prepend (sgroup->converters, cspace);
+ sgroup->converters = g_list_prepend (sgroup->converters, scale);
+ sgroup->converters = g_list_prepend (sgroup->converters, cspace2);
+
+ if (!fast_element_link (cspace, scale) ||
+ !fast_element_link (scale, cspace2))
+ goto converter_link_failure;
+
+ if (!gst_encoding_video_profile_get_variableframerate
+ (GST_ENCODING_VIDEO_PROFILE (sprof))) {
+ vrate = gst_element_factory_make ("videorate", NULL);
+ gst_bin_add ((GstBin *) ebin, vrate);
+ tosync = g_list_prepend (tosync, vrate);
+ sgroup->converters = g_list_prepend (sgroup->converters, vrate);
+ if (!fast_element_link (cspace2, vrate) ||
+ !fast_element_link (vrate, last))
+ goto converter_link_failure;
+ } else if (!fast_element_link (cspace2, last))
+ goto converter_link_failure;
+
+ last = cspace;
+
+ } else if (GST_IS_ENCODING_AUDIO_PROFILE (sprof)) {
+ GstElement *aconv, *ares, *arate;
+
+ GST_LOG ("Adding conversion elements for audio stream");
+
+ arate = gst_element_factory_make ("audiorate", NULL);
+ g_object_set (arate, "tolerance", (guint64) ebin->tolerance, NULL);
+ aconv = gst_element_factory_make ("audioconvert", NULL);
+ ares = gst_element_factory_make ("audioresample", NULL);
+
+ gst_bin_add_many ((GstBin *) ebin, arate, aconv, ares, NULL);
+ tosync = g_list_append (tosync, arate);
+ tosync = g_list_append (tosync, aconv);
+ tosync = g_list_append (tosync, ares);
+ if (!fast_element_link (arate, aconv) ||
+ !fast_element_link (aconv, ares) || !fast_element_link (ares, last))
+ goto converter_link_failure;
+
+ sgroup->converters = g_list_prepend (sgroup->converters, arate);
+ sgroup->converters = g_list_prepend (sgroup->converters, aconv);
+ sgroup->converters = g_list_prepend (sgroup->converters, ares);
+
+ last = arate;
+ }
+
+ /* Link to stream splitter */
+ sinkpad = gst_element_get_static_pad (last, "sink");
+ srcpad = gst_element_request_pad (sgroup->splitter, NULL, "encodingsrc");
+ if (G_UNLIKELY (srcpad == NULL))
+ goto no_splitter_srcpad;
+ if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK))
+ goto splitter_encoding_failure;
+ g_object_unref (sinkpad);
+ g_object_unref (srcpad);
+
+ /* End of Stream 2 setup */
+
+ /* Sync all elements to parent state */
+ for (tmp = tosync; tmp; tmp = tmp->next)
+ gst_element_sync_state_with_parent ((GstElement *) tmp->data);
+ g_list_free (tosync);
+
+ /* Add ghostpad */
+ GST_DEBUG ("Adding ghostpad %s:%s", GST_DEBUG_PAD_NAME (sgroup->ghostpad));
+ gst_pad_set_active (sgroup->ghostpad, TRUE);
+ gst_element_add_pad ((GstElement *) ebin, sgroup->ghostpad);
+
+ /* Add StreamGroup to our list of streams */
+
+ GST_DEBUG
+ ("Done creating elements, adding StreamGroup to our controlled stream list");
+
+ ebin->streams = g_list_prepend (ebin->streams, sgroup);
+
+ return sgroup;
+
+splitter_encoding_failure:
+ GST_ERROR_OBJECT (ebin, "Error linking splitter to encoding stream");
+ goto cleanup;
+
+no_encoder:
+ GST_ERROR_OBJECT (ebin, "Couldn't create encoder for format %" GST_PTR_FORMAT,
+ format);
+ goto cleanup;
+
+no_muxer_pad:
+ GST_ERROR_OBJECT (ebin,
+ "Couldn't find a compatible muxer pad to link encoder to");
+ goto cleanup;
+
+encoder_link_failure:
+ GST_ERROR_OBJECT (ebin, "Failed to link the encoder");
+ goto cleanup;
+
+muxer_link_failure:
+ GST_ERROR_OBJECT (ebin, "Couldn't link encoder to muxer");
+ goto cleanup;
+
+outfilter_link_failure:
+ GST_ERROR_OBJECT (ebin, "Couldn't link output filter to output queue");
+ goto cleanup;
+
+passthrough_link_failure:
+ GST_ERROR_OBJECT (ebin, "Failed linking splitter in passthrough mode");
+ goto cleanup;
+
+no_splitter_srcpad:
+ GST_ERROR_OBJECT (ebin, "Couldn't get a source pad from the splitter");
+ goto cleanup;
+
+no_combiner_sinkpad:
+ GST_ERROR_OBJECT (ebin, "Couldn't get a sink pad from the combiner");
+ goto cleanup;
+
+splitter_link_failure:
+ GST_ERROR_OBJECT (ebin, "Failure linking to the splitter");
+ goto cleanup;
+
+combiner_link_failure:
+ GST_ERROR_OBJECT (ebin, "Failure linking to the combiner");
+ goto cleanup;
+
+#if 0
+parser_link_failure:
+ GST_ERROR_OBJECT (ebin, "Failure linking the parser");
+ goto cleanup;
+#endif
+
+converter_link_failure:
+ GST_ERROR_OBJECT (ebin, "Failure linking the video converters");
+ goto cleanup;
+
+cleanup:
+ /* FIXME : Actually properly cleanup everything */
+ g_slice_free (StreamGroup, sgroup);
+ return NULL;
+}
+
+static gboolean
+_factory_can_sink_caps (GstElementFactory * factory, const GstCaps * caps)
+{
+ GList *templates = factory->staticpadtemplates;
+
+ while (templates) {
+ GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data;
+
+ if (template->direction == GST_PAD_SINK) {
+ GstCaps *tmp = gst_static_caps_get (&template->static_caps);
+
+ if (gst_caps_can_intersect (tmp, caps)) {
+ gst_caps_unref (tmp);
+ return TRUE;
+ }
+ gst_caps_unref (tmp);
+ }
+ templates = g_list_next (templates);
+ }
+
+ return FALSE;
+}
+
+static inline GstElement *
+_get_muxer (GstEncodeBin * ebin)
+{
+ GList *muxers, *tmpmux;
+ GstElement *muxer = NULL;
+ GstElementFactory *muxerfact = NULL;
+ const GList *tmp;
+ const GstCaps *format;
+ const gchar *preset;
+
+ format = gst_encoding_profile_get_format (ebin->profile);
+ preset = gst_encoding_profile_get_preset (ebin->profile);
+
+ if (format == NULL) {
+ GST_DEBUG ("Container-less profile, using identity");
+ muxer = gst_element_factory_make ("identity", NULL);
+ goto beach;
+ }
+
+ GST_DEBUG ("Getting list of muxers for format %" GST_PTR_FORMAT, format);
+
+ muxers =
+ gst_element_factory_list_filter (ebin->muxers, format, GST_PAD_SRC, TRUE);
+
+ if (muxers == NULL)
+ goto beach;
+
+ /* FIXME : signal the user if he wants this */
+ for (tmpmux = muxers; tmpmux; tmpmux = tmpmux->next) {
+ gboolean cansinkstreams = TRUE;
+ const GList *profiles =
+ gst_encoding_container_profile_get_profiles
+ (GST_ENCODING_CONTAINER_PROFILE (ebin->profile));
+
+ muxerfact = (GstElementFactory *) tmpmux->data;
+
+ GST_DEBUG ("Trying muxer %s", GST_PLUGIN_FEATURE_NAME (muxerfact));
+
+ /* See if the muxer can sink all of our stream profile caps */
+ for (tmp = profiles; tmp; tmp = tmp->next) {
+ GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
+
+ if (!_factory_can_sink_caps (muxerfact,
+ gst_encoding_profile_get_format (sprof))) {
+ GST_DEBUG ("Skipping muxer because it can't sink caps %" GST_PTR_FORMAT,
+ gst_encoding_profile_get_format (sprof));
+ cansinkstreams = FALSE;
+ break;
+ }
+ }
+
+ /* Only use a muxer than can use all streams and than can accept the
+ * preset (which may be present or not) */
+ if (cansinkstreams && (muxer =
+ _create_element_and_set_preset (muxerfact, preset, "muxer")))
+ break;
+ }
+
+ gst_plugin_feature_list_free (muxers);
+
+beach:
+ return muxer;
+}
+
+static gboolean
+create_elements_and_pads (GstEncodeBin * ebin)
+{
+ gboolean ret = TRUE;
+ GstElement *muxer;
+ GstPad *muxerpad;
+ const GList *tmp, *profiles;
+ GstEncodingProfile *sprof;
+
+ GST_DEBUG ("Current profile : %s",
+ gst_encoding_profile_get_name (ebin->profile));
+
+ /* 1. Get the compatible muxer */
+ muxer = _get_muxer (ebin);
+ if (G_UNLIKELY (muxer == NULL))
+ goto no_muxer;
+
+ /* Record the muxer */
+ ebin->muxer = muxer;
+ gst_bin_add ((GstBin *) ebin, muxer);
+
+ /* 2. Ghost the muxer source pad */
+
+ /* FIXME : We should figure out if it's a static/request/dyamic pad,
+ * but for the time being let's assume it's a static pad :) */
+ muxerpad = gst_element_get_static_pad (muxer, "src");
+ if (G_UNLIKELY (muxerpad == NULL))
+ goto no_muxer_pad;
+
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), muxerpad))
+ goto no_muxer_ghost_pad;
+
+ gst_object_unref (muxerpad);
+
+ /* 3. Activate fixed presence streams */
+ profiles =
+ gst_encoding_container_profile_get_profiles
+ (GST_ENCODING_CONTAINER_PROFILE (ebin->profile));
+ for (tmp = profiles; tmp; tmp = tmp->next) {
+ sprof = (GstEncodingProfile *) tmp->data;
+
+ GST_DEBUG ("Trying stream profile with presence %d",
+ gst_encoding_profile_get_presence (sprof));
+
+ if (gst_encoding_profile_get_presence (sprof) != 0) {
+ if (G_UNLIKELY (_create_stream_group (ebin, sprof, NULL, NULL) == NULL))
+ goto stream_error;
+ }
+ }
+
+ return ret;
+
+no_muxer:
+ {
+ GST_WARNING ("No available muxer for %" GST_PTR_FORMAT,
+ gst_encoding_profile_get_format (ebin->profile));
+ return FALSE;
+ }
+
+no_muxer_pad:
+ {
+ GST_WARNING ("Can't get source pad from muxer (%s)",
+ GST_ELEMENT_NAME (muxer));
+ gst_bin_remove (GST_BIN (ebin), muxer);
+ return FALSE;
+ }
+
+no_muxer_ghost_pad:
+ {
+ GST_WARNING ("Couldn't set %s:%s as source ghostpad target",
+ GST_DEBUG_PAD_NAME (muxerpad));
+ gst_bin_remove (GST_BIN (ebin), muxer);
+ gst_object_unref (muxerpad);
+ return FALSE;
+ }
+
+stream_error:
+ {
+ GST_WARNING ("Could not create Streams");
+ gst_element_remove_pad ((GstElement *) ebin, ebin->srcpad);
+ gst_bin_remove (GST_BIN (ebin), muxer);
+ ebin->muxer = NULL;
+ ebin->srcpad = NULL;
+ return FALSE;
+ }
+}
+
+static void
+release_pads (GstPad * pad, GstElement * elt)
+{
+ GstPad *peer = NULL;
+
+ GST_DEBUG_OBJECT (elt, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ /* Unlink from its peer pad */
+ if ((peer = gst_pad_get_peer (pad))) {
+ if (GST_PAD_DIRECTION (peer) == GST_PAD_SRC)
+ gst_pad_unlink (peer, pad);
+ else
+ gst_pad_unlink (pad, peer);
+ gst_object_unref (peer);
+ }
+
+ /* Release it from the object */
+ gst_element_release_request_pad (elt, pad);
+
+ /* And remove the reference added by the iterator */
+ gst_object_unref (pad);
+}
+
+static void inline
+stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup)
+{
+ GList *tmp;
+ GstPad *tmppad;
+ GstPad *pad;
+
+ GST_DEBUG_OBJECT (ebin, "Freeing StreamGroup %p", sgroup);
+
+ /* outqueue - Muxer */
+ tmppad = gst_element_get_static_pad (sgroup->outqueue, "src");
+ pad = gst_pad_get_peer (tmppad);
+
+ /* Remove muxer request sink pad */
+ gst_pad_unlink (tmppad, pad);
+ gst_element_release_request_pad (ebin->muxer, pad);
+ gst_object_unref (tmppad);
+ gst_object_unref (pad);
+ if (sgroup->outqueue)
+ gst_element_set_state (sgroup->outqueue, GST_STATE_NULL);
+
+ /* Capsfilter - outqueue */
+ gst_element_set_state (sgroup->outfilter, GST_STATE_NULL);
+ gst_element_unlink (sgroup->outfilter, sgroup->outqueue);
+ gst_bin_remove (GST_BIN (ebin), sgroup->outqueue);
+
+ /* streamcombiner - parser - capsfilter */
+ if (sgroup->parser) {
+ gst_element_set_state (sgroup->parser, GST_STATE_NULL);
+ gst_element_unlink (sgroup->parser, sgroup->outfilter);
+ gst_element_unlink (sgroup->combiner, sgroup->parser);
+ }
+
+ /* Sink Ghostpad */
+ if (sgroup->ghostpad)
+ gst_element_remove_pad (GST_ELEMENT_CAST (ebin), sgroup->ghostpad);
+
+ if (sgroup->inqueue)
+ gst_element_set_state (sgroup->inqueue, GST_STATE_NULL);
+
+ if (sgroup->encoder)
+ gst_element_set_state (sgroup->encoder, GST_STATE_NULL);
+ if (sgroup->outfilter)
+ gst_element_set_state (sgroup->outfilter, GST_STATE_NULL);
+ if (sgroup->smartencoder)
+ gst_element_set_state (sgroup->smartencoder, GST_STATE_NULL);
+
+ if (sgroup->capsfilter) {
+ gst_element_set_state (sgroup->capsfilter, GST_STATE_NULL);
+ gst_element_unlink (sgroup->capsfilter, sgroup->encoder);
+ gst_bin_remove ((GstBin *) ebin, sgroup->capsfilter);
+ }
+
+ for (tmp = sgroup->converters; tmp; tmp = tmp->next) {
+ GstElement *elt = (GstElement *) tmp->data;
+
+ gst_element_set_state (elt, GST_STATE_NULL);
+ gst_bin_remove ((GstBin *) ebin, elt);
+ }
+ if (sgroup->converters)
+ g_list_free (sgroup->converters);
+
+ if (sgroup->combiner) {
+ GstIterator *it = gst_element_iterate_sink_pads (sgroup->combiner);
+ GstIteratorResult itret = GST_ITERATOR_OK;
+
+ while (itret == GST_ITERATOR_OK || itret == GST_ITERATOR_RESYNC) {
+ itret = gst_iterator_foreach (it, (GFunc) release_pads, sgroup->combiner);
+ gst_iterator_resync (it);
+ }
+ gst_iterator_free (it);
+ }
+
+ if (sgroup->splitter) {
+ GstIterator *it = gst_element_iterate_src_pads (sgroup->splitter);
+ GstIteratorResult itret = GST_ITERATOR_OK;
+ while (itret == GST_ITERATOR_OK || itret == GST_ITERATOR_RESYNC) {
+ itret = gst_iterator_foreach (it, (GFunc) release_pads, sgroup->splitter);
+ gst_iterator_resync (it);
+ }
+ gst_iterator_free (it);
+ }
+
+ if (sgroup->inqueue)
+ gst_bin_remove ((GstBin *) ebin, sgroup->inqueue);
+ if (sgroup->encoder)
+ gst_bin_remove ((GstBin *) ebin, sgroup->encoder);
+ if (sgroup->smartencoder)
+ gst_bin_remove ((GstBin *) ebin, sgroup->smartencoder);
+
+ g_slice_free (StreamGroup, sgroup);
+}
+
+static void
+stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup)
+{
+ ebin->streams = g_list_remove (ebin->streams, sgroup);
+
+ stream_group_free (ebin, sgroup);
+}
+
+static void
+gst_encode_bin_tear_down_profile (GstEncodeBin * ebin)
+{
+ if (G_UNLIKELY (ebin->profile == NULL))
+ return;
+
+ GST_DEBUG ("Tearing down profile %s",
+ gst_encoding_profile_get_name (ebin->profile));
+
+ while (ebin->streams)
+ stream_group_remove (ebin, (StreamGroup *) ebin->streams->data);
+
+ /* free/clear profile */
+ gst_encoding_profile_unref (ebin->profile);
+ ebin->profile = NULL;
+}
+
+static gboolean
+gst_encode_bin_setup_profile (GstEncodeBin * ebin, GstEncodingProfile * profile)
+{
+ gboolean res;
+
+ g_return_val_if_fail (ebin->profile == NULL, FALSE);
+
+ GST_DEBUG ("Setting up profile %s", gst_encoding_profile_get_name (profile));
+
+ ebin->profile = profile;
+ gst_mini_object_ref ((GstMiniObject *) ebin->profile);
+
+ /* Create elements */
+ res = create_elements_and_pads (ebin);
+ if (res == FALSE)
+ gst_encode_bin_tear_down_profile (ebin);
+
+ return res;
+}
+
+static gboolean
+gst_encode_bin_set_profile (GstEncodeBin * ebin, GstEncodingProfile * profile)
+{
+ GST_DEBUG_OBJECT (ebin, "profile : %s",
+ gst_encoding_profile_get_name (profile));
+
+ if (G_UNLIKELY (ebin->active)) {
+ GST_WARNING_OBJECT (ebin, "Element already active, can't change profile");
+ return FALSE;
+ }
+
+ /* If we're not active, we can deactivate the previous profile */
+ if (ebin->profile)
+ gst_encoding_profile_unref (ebin->profile);
+ ebin->profile = NULL;
+
+ return gst_encode_bin_setup_profile (ebin, profile);
+}
+
+static inline gboolean
+gst_encode_bin_activate (GstEncodeBin * ebin)
+{
+ ebin->active = ebin->profile != NULL;
+ return ebin->active;
+}
+
+static void
+gst_encode_bin_deactivate (GstEncodeBin * ebin)
+{
+ ebin->active = FALSE;
+}
+
+static GstStateChangeReturn
+gst_encode_bin_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstEncodeBin *ebin = (GstEncodeBin *) element;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ if (!gst_encode_bin_activate (ebin)) {
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto beach;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret =
+ GST_ELEMENT_CLASS (gst_encode_bin_parent_class)->change_state (element,
+ transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto beach;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_encode_bin_deactivate (ebin);
+ break;
+ default:
+ break;
+ }
+
+beach:
+ return ret;
+}
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean res;
+
+ GST_DEBUG_CATEGORY_INIT (gst_encode_bin_debug, "encodebin", 0, "encoder bin");
+
+#ifdef ENABLE_NLS
+ GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+ LOCALEDIR);
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif /* ENABLE_NLS */
+
+
+ res = gst_element_register (plugin, "encodebin", GST_RANK_NONE,
+ GST_TYPE_ENCODE_BIN);
+
+ return res;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "encoding",
+ "various encoding-related elements", plugin_init, VERSION, GST_LICENSE,
+ GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/encoding/gstencodebin.h b/gst/encoding/gstencodebin.h
new file mode 100644
index 000000000..8082817de
--- /dev/null
+++ b/gst/encoding/gstencodebin.h
@@ -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__ */
diff --git a/gst/encoding/gstsmartencoder.c b/gst/encoding/gstsmartencoder.c
new file mode 100644
index 000000000..ed0e42bd5
--- /dev/null
+++ b/gst/encoding/gstsmartencoder.c
@@ -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;
+}
diff --git a/gst/encoding/gstsmartencoder.h b/gst/encoding/gstsmartencoder.h
new file mode 100644
index 000000000..15366269c
--- /dev/null
+++ b/gst/encoding/gstsmartencoder.h
@@ -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__ */
diff --git a/gst/encoding/gststreamcombiner.c b/gst/encoding/gststreamcombiner.c
new file mode 100644
index 000000000..72d40fe75
--- /dev/null
+++ b/gst/encoding/gststreamcombiner.c
@@ -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;
+}
diff --git a/gst/encoding/gststreamcombiner.h b/gst/encoding/gststreamcombiner.h
new file mode 100644
index 000000000..ce67277d7
--- /dev/null
+++ b/gst/encoding/gststreamcombiner.h
@@ -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__ */
diff --git a/gst/encoding/gststreamsplitter.c b/gst/encoding/gststreamsplitter.c
new file mode 100644
index 000000000..a2fa589f3
--- /dev/null
+++ b/gst/encoding/gststreamsplitter.c
@@ -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;
+}
diff --git a/gst/encoding/gststreamsplitter.h b/gst/encoding/gststreamsplitter.h
new file mode 100644
index 000000000..b503c00fc
--- /dev/null
+++ b/gst/encoding/gststreamsplitter.h
@@ -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__ */
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index 59ba7403f..03387af51 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -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)
diff --git a/tests/check/elements/.gitignore b/tests/check/elements/.gitignore
index 3e11d9e01..20bd96e23 100644
--- a/tests/check/elements/.gitignore
+++ b/tests/check/elements/.gitignore
@@ -9,6 +9,7 @@ audioresample
audiotestsrc
decodebin
decodebin2
+encodebin
gdpdepay
gdppay
gnomevfssink
diff --git a/tests/check/elements/encodebin.c b/tests/check/elements/encodebin.c
new file mode 100644
index 000000000..b32569400
--- /dev/null
+++ b/tests/check/elements/encodebin.c
@@ -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);