gst: New encoding plugin
authorEdward Hervey <edward.hervey@collabora.co.uk>
Fri, 13 Aug 2010 15:36:38 +0000 (17:36 +0200)
committerEdward Hervey <edward.hervey@collabora.co.uk>
Wed, 15 Dec 2010 10:54:32 +0000 (11:54 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=627476

24 files changed:
configure.ac
docs/plugins/Makefile.am
docs/plugins/gst-plugins-base-plugins-docs.sgml
docs/plugins/gst-plugins-base-plugins-sections.txt
docs/plugins/gst-plugins-base-plugins.args
docs/plugins/gst-plugins-base-plugins.hierarchy
docs/plugins/gst-plugins-base-plugins.interfaces
docs/plugins/gst-plugins-base-plugins.signals
docs/plugins/inspect/plugin-encoding.xml [new file with mode: 0644]
docs/plugins/inspect/plugin-libvisual.xml
gst/encoding/.gitignore [new file with mode: 0644]
gst/encoding/Makefile.am [new file with mode: 0644]
gst/encoding/gstencode-marshal.list [new file with mode: 0644]
gst/encoding/gstencodebin.c [new file with mode: 0644]
gst/encoding/gstencodebin.h [new file with mode: 0644]
gst/encoding/gstsmartencoder.c [new file with mode: 0644]
gst/encoding/gstsmartencoder.h [new file with mode: 0644]
gst/encoding/gststreamcombiner.c [new file with mode: 0644]
gst/encoding/gststreamcombiner.h [new file with mode: 0644]
gst/encoding/gststreamsplitter.c [new file with mode: 0644]
gst/encoding/gststreamsplitter.h [new file with mode: 0644]
tests/check/Makefile.am
tests/check/elements/.gitignore
tests/check/elements/encodebin.c [new file with mode: 0644]

index b1df1c6..64f877c 100644 (file)
@@ -417,6 +417,7 @@ AG_GST_CHECK_PLUGIN(app)
 AG_GST_CHECK_PLUGIN(audioconvert)
 AG_GST_CHECK_PLUGIN(audiorate)
 AG_GST_CHECK_PLUGIN(audiotestsrc)
+AG_GST_CHECK_PLUGIN(encoding)
 AG_GST_CHECK_PLUGIN(ffmpegcolorspace)
 AG_GST_CHECK_PLUGIN(gdp)
 AG_GST_CHECK_PLUGIN(playback)
@@ -914,6 +915,7 @@ gst/app/Makefile
 gst/audioconvert/Makefile
 gst/audiorate/Makefile
 gst/audiotestsrc/Makefile
+gst/encoding/Makefile
 gst/ffmpegcolorspace/Makefile
 gst/gdp/Makefile
 gst/playback/Makefile
index 4b78433..9beea7e 100644 (file)
@@ -50,7 +50,7 @@ MKDB_OPTIONS=--sgml-mode
 # Extra options to supply to gtkdoc-fixref.
 FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \
        --extra-dir=$(GST_PREFIX)/share/gtk-doc/html \
-               --extra-dir=$(datadir)/gtk-doc/html
+       --extra-dir=$(datadir)/gtk-doc/html
 
 # Used for dependencies.
 HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
@@ -102,6 +102,7 @@ EXTRA_HFILES = \
        $(top_srcdir)/gst/audioconvert/audioconvert.h \
        $(top_srcdir)/gst/audioconvert/gstaudioconvert.h \
        $(top_srcdir)/gst/audiotestsrc/gstaudiotestsrc.h \
+       $(top_srcdir)/gst/encoding/gstencodebin.h \
        $(top_srcdir)/gst/ffmpegcolorspace/gstffmpegcolorspace.h \
        $(top_srcdir)/gst/gdp/gstgdpdepay.h \
        $(top_srcdir)/gst/gdp/gstgdppay.h \
index 9bcbbf8..ea603d6 100644 (file)
@@ -31,6 +31,7 @@
     <xi:include href="xml/element-clockoverlay.xml" />
     <xi:include href="xml/element-decodebin.xml" />
     <xi:include href="xml/element-decodebin2.xml" />
+    <xi:include href="xml/element-encodebin.xml" />
     <xi:include href="xml/element-ffmpegcolorspace.xml" />
     <xi:include href="xml/element-gdpdepay.xml" />
     <xi:include href="xml/element-gdppay.xml" />
@@ -80,6 +81,7 @@
     <xi:include href="xml/plugin-audiotestsrc.xml" />
     <xi:include href="xml/plugin-cdparanoia.xml" />
     <xi:include href="xml/plugin-decodebin.xml" />
+    <xi:include href="xml/plugin-encoding.xml" />
     <xi:include href="xml/plugin-ffmpegcolorspace.xml" />
     <xi:include href="xml/plugin-gdp.xml" />
     <xi:include href="xml/plugin-gio.xml" />
index e241371..e94c450 100644 (file)
@@ -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
index 05ef80f..d3a40c5 100644 (file)
 </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>
 </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>
 <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>
+
index 4a355e6..80aed2e 100644 (file)
@@ -56,6 +56,7 @@ GObject
       GstBin
         GstDecodeBin
         GstDecodeBin2
+        GstEncodeBin
         GstPipeline
           GstPlayBaseBin
             GstPlayBin
index 36963b5..630084d 100644 (file)
@@ -8,6 +8,7 @@ GstPlaySink GstChildProxy
 GstSubtitleOverlay GstChildProxy
 GstDecodeBin2 GstChildProxy
 GstURIDecodeBin GstChildProxy
+GstEncodeBin GstChildProxy
 GstCddaBaseSrc GstURIHandler
 GstCdParanoiaSrc GstURIHandler
 GstAlsaSrc GstImplementsInterface GstMixer GstPropertyProbe
index 2f5fab5..621f880 100644 (file)
@@ -471,3 +471,11 @@ GstPlaySink *gstplaysink
 GstCaps *arg1
 </SIGNAL>
 
+<SIGNAL>
+<NAME>GstEncodeBin::request-pad</NAME>
+<RETURNS>GstPad*</RETURNS>
+<FLAGS>la</FLAGS>
+GstEncodeBin *gstencodebin
+GstCaps *arg1
+</SIGNAL>
+
diff --git a/docs/plugins/inspect/plugin-encoding.xml b/docs/plugins/inspect/plugin-encoding.xml
new file mode 100644 (file)
index 0000000..ab61ef7
--- /dev/null
@@ -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
index 22f4e8f..0af3fc7 100644 (file)
@@ -9,173 +9,5 @@
   <package>GStreamer Base Plug-ins git</package>
   <origin>Unknown package origin</origin>
   <elements>
-    <element>
-      <name>libvisual_bumpscope</name>
-      <longname>libvisual Bumpscope plugin plugin v.0.0.1</longname>
-      <class>Visualization</class>
-      <description>Bumpscope visual plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_corona</name>
-      <longname>libvisual libvisual corona plugin plugin v.0.1</longname>
-      <class>Visualization</class>
-      <description>Libvisual corona plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_infinite</name>
-      <longname>libvisual infinite plugin plugin v.0.1</longname>
-      <class>Visualization</class>
-      <description>Infinite visual plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_jakdaw</name>
-      <longname>libvisual Jakdaw plugin plugin v.0.0.1</longname>
-      <class>Visualization</class>
-      <description>jakdaw visual plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_jess</name>
-      <longname>libvisual jess plugin plugin v.0.1</longname>
-      <class>Visualization</class>
-      <description>Jess visual plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_lv_analyzer</name>
-      <longname>libvisual libvisual analyzer plugin v.1.0</longname>
-      <class>Visualization</class>
-      <description>Libvisual analyzer plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_lv_scope</name>
-      <longname>libvisual libvisual scope plugin v.0.1</longname>
-      <class>Visualization</class>
-      <description>Libvisual scope plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
-    <element>
-      <name>libvisual_oinksie</name>
-      <longname>libvisual oinksie plugin plugin v.0.1</longname>
-      <class>Visualization</class>
-      <description>Libvisual Oinksie visual plugin</description>
-      <author>Benjamin Otte &lt;otte@gnome.org&gt;</author>
-      <pads>
-        <caps>
-          <name>sink</name>
-          <direction>sink</direction>
-          <presence>always</presence>
-          <details>audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int)1234, signed=(boolean)true, channels=(int){ 1, 2 }, rate=(int){ 8000, 11250, 22500, 32000, 44100, 48000, 96000 }</details>
-        </caps>
-        <caps>
-          <name>src</name>
-          <direction>source</direction>
-          <presence>always</presence>
-          <details>video/x-raw-rgb, bpp=(int)32, depth=(int)24, endianness=(int)4321, red_mask=(int)65280, green_mask=(int)16711680, blue_mask=(int)-16777216, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)24, depth=(int)24, endianness=(int)4321, red_mask=(int)255, green_mask=(int)65280, blue_mask=(int)16711680, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-rgb, bpp=(int)16, depth=(int)16, endianness=(int)1234, red_mask=(int)63488, green_mask=(int)2016, blue_mask=(int)31, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
-        </caps>
-      </pads>
-    </element>
   </elements>
 </plugin>
\ No newline at end of file
diff --git a/gst/encoding/.gitignore b/gst/encoding/.gitignore
new file mode 100644 (file)
index 0000000..ff44545
--- /dev/null
@@ -0,0 +1 @@
+gstencode-marshal.[ch]
diff --git a/gst/encoding/Makefile.am b/gst/encoding/Makefile.am
new file mode 100644 (file)
index 0000000..0e287fa
--- /dev/null
@@ -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 (file)
index 0000000..00f26ed
--- /dev/null
@@ -0,0 +1 @@
+OBJECT:BOXED
diff --git a/gst/encoding/gstencodebin.c b/gst/encoding/gstencodebin.c
new file mode 100644 (file)
index 0000000..3ba5a40
--- /dev/null
@@ -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 (file)
index 0000000..8082817
--- /dev/null
@@ -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 (file)
index 0000000..ed0e42b
--- /dev/null
@@ -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 (file)
index 0000000..1536626
--- /dev/null
@@ -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 (file)
index 0000000..72d40fe
--- /dev/null
@@ -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 (file)
index 0000000..ce67277
--- /dev/null
@@ -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 (file)
index 0000000..a2fa589
--- /dev/null
@@ -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 (file)
index 0000000..b503c00
--- /dev/null
@@ -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__ */
index 59ba740..03387af 100644 (file)
@@ -106,6 +106,7 @@ check_PROGRAMS = \
        elements/audiotestsrc \
        elements/decodebin \
        elements/decodebin2 \
+       elements/encodebin \
        elements/ffmpegcolorspace \
        elements/gdpdepay \
        elements/gdppay \
@@ -298,6 +299,9 @@ elements_playbin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
 elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD)
 elements_decodebin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
 
+elements_encodebin_LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la $(GST_BASE_LIBS) $(LDADD)
+elements_encodebin_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS)
+
 elements_decodebin2_LDADD = $(GST_BASE_LIBS) $(LDADD)
 elements_decodebin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
 
index 3e11d9e..20bd96e 100644 (file)
@@ -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 (file)
index 0000000..b325694
--- /dev/null
@@ -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);