diff --git a/ChangeLog b/ChangeLog index b79442ba8e..fecf15a46f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,1666 @@ -=== release 0.10.31 === +=== release 0.10.32 === -2010-11-30 Tim-Philipp Müller +2011-01-21 Tim-Philipp Müller * configure.ac: - releasing 0.10.31, "Dance Like It's 1982" + releasing 0.10.32, "Your Life You Like It Well" + +2011-01-18 10:45:01 +0000 Tim-Philipp Müller + + * configure.ac: + * win32/common/_stdint.h: + * win32/common/config.h: + 0.10.31.4 pre-releases + +2011-01-18 10:44:01 +0000 Tim-Philipp Müller + + * docs/plugins/gst-plugins-base-plugins.args: + * docs/plugins/inspect/plugin-adder.xml: + * docs/plugins/inspect/plugin-alsa.xml: + * docs/plugins/inspect/plugin-app.xml: + * docs/plugins/inspect/plugin-audioconvert.xml: + * docs/plugins/inspect/plugin-audiorate.xml: + * docs/plugins/inspect/plugin-audioresample.xml: + * docs/plugins/inspect/plugin-audiotestsrc.xml: + * docs/plugins/inspect/plugin-cdparanoia.xml: + * docs/plugins/inspect/plugin-decodebin.xml: + * docs/plugins/inspect/plugin-encoding.xml: + * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: + * docs/plugins/inspect/plugin-gdp.xml: + * docs/plugins/inspect/plugin-gio.xml: + * docs/plugins/inspect/plugin-gnomevfs.xml: + * docs/plugins/inspect/plugin-libvisual.xml: + * docs/plugins/inspect/plugin-ogg.xml: + * docs/plugins/inspect/plugin-pango.xml: + * docs/plugins/inspect/plugin-playback.xml: + * docs/plugins/inspect/plugin-subparse.xml: + * docs/plugins/inspect/plugin-tcp.xml: + * docs/plugins/inspect/plugin-theora.xml: + * docs/plugins/inspect/plugin-typefindfunctions.xml: + * docs/plugins/inspect/plugin-uridecodebin.xml: + * docs/plugins/inspect/plugin-video4linux.xml: + * docs/plugins/inspect/plugin-videorate.xml: + * docs/plugins/inspect/plugin-videoscale.xml: + * docs/plugins/inspect/plugin-videotestsrc.xml: + * docs/plugins/inspect/plugin-volume.xml: + * docs/plugins/inspect/plugin-vorbis.xml: + * docs/plugins/inspect/plugin-ximagesink.xml: + * docs/plugins/inspect/plugin-xvimagesink.xml: + docs: update docs + +2011-01-18 10:40:29 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/encoding-target.c: + * tests/check/libs/profile.c: + encoding-target: change keyfile header to 'GStreamer Encoding Target' + which is more in line with other files such as .desktop files. + +2011-01-18 01:06:50 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/encoding-target.c: + pbutils: don't assume LC_MESSAGES is always defined, also check for ENABLE_NLS + Should fix build with mingw32 build bot again. + +2011-01-18 00:09:37 +0000 Tim-Philipp Müller + + * gst-libs/gst/app/gstappsrc.c: + * gst-libs/gst/app/gstappsrc.h: + * win32/common/libgstapp.def: + app: export gst_app_stream_type_get_type() + API: gst_app_stream_type_get_type() + API: GST_TYPE_APP_STREAM_TYPE + https://bugzilla.gnome.org/show_bug.cgi?id=639747 + +2011-01-17 23:59:48 +0000 Tim-Philipp Müller + + * gst-libs/gst/app/gstappbuffer.c: + app: make GstAppBuffer get_type() function thread-safe + +2011-01-18 01:09:53 +0530 Arun Raghavan + + * gst-libs/gst/pbutils/gstdiscoverer.c: + discoverer: Drop new stream tags once preroll is done + This makes sure we do not touch the stream taglist once the pipeline has + been prerolled. Adding of stream tags happens in the pad event probe + which runs in a different thread from discoverer stream processing, so + modifying the tag list while discoverer might be processing it can + sometimes cause a crash. + https://bugzilla.gnome.org/show_bug.cgi?id=639778 + +2011-01-17 15:30:08 +0530 Arun Raghavan + + * gst-libs/gst/pbutils/gstdiscoverer.c: + discoverer: Validate timeouts before processing them + This avoids a race where the timeout callback is scheduled to run but we + get sufficient information to finish discovery before actually getting + around to executing the callback. See the documentation of + g_source_is_destroyed() for more details. + https://bugzilla.gnome.org/show_bug.cgi?id=639730 + +2011-01-18 00:08:32 +0530 Arun Raghavan + + * gst-libs/gst/pbutils/gstdiscoverer.c: + discoverer: Make sure we call _stop() before being freed + This ensures that everything is properly cleaned up before the + GstDiscoverer object is freed. Specifically, it makes sure that we've + removed the async timeout callback before freeing the object to avoid a + potential crash later on. + https://bugzilla.gnome.org/show_bug.cgi?id=639755 + +2011-01-16 14:55:46 -0800 David Schleef + + * gst/gdp/gstgdppay.c: + gdppay: make newsegment buffer metadata writable + +2011-01-16 16:46:22 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/encoding-target.c: + pbutils: save localised strings properly when writing encoding targets to a file + Use LC_MESSAGES rather than LC_ALL. Save/load description as untranslated string + when using an English language locale. Strip locale information to the language, + so we don't save keys like description[fr_FR.UTF-8]=... + https://bugzilla.gnome.org/show_bug.cgi?id=638860 + +2011-01-13 13:59:41 +0000 Tim-Philipp Müller + + * gst/typefind/gsttypefindfunctions.c: + typefinding: set framed=false on DTS caps + +2011-01-12 17:51:43 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-target.c: + docs: add some more Since: markers for new encoding-profile API + +2011-01-12 15:51:52 +0000 Tim-Philipp Müller + + * configure.ac: + configure: require gobject-introspection >= 0.9.12 + Earlier versions don't honour the -L/--library-path option, + which we need. See commit 4d0ccdad in gobject-introspection git. + Should "fix" build on lucid/maverick build bots. + +2011-01-11 19:19:50 +0000 Tim-Philipp Müller + + * configure.ac: + * docs/plugins/gst-plugins-base-plugins.prerequisites: + * docs/plugins/inspect/plugin-adder.xml: + * docs/plugins/inspect/plugin-alsa.xml: + * docs/plugins/inspect/plugin-app.xml: + * docs/plugins/inspect/plugin-audioconvert.xml: + * docs/plugins/inspect/plugin-audiorate.xml: + * docs/plugins/inspect/plugin-audioresample.xml: + * docs/plugins/inspect/plugin-audiotestsrc.xml: + * docs/plugins/inspect/plugin-cdparanoia.xml: + * docs/plugins/inspect/plugin-decodebin.xml: + * docs/plugins/inspect/plugin-encoding.xml: + * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: + * docs/plugins/inspect/plugin-gdp.xml: + * docs/plugins/inspect/plugin-gio.xml: + * docs/plugins/inspect/plugin-gnomevfs.xml: + * docs/plugins/inspect/plugin-libvisual.xml: + * docs/plugins/inspect/plugin-ogg.xml: + * docs/plugins/inspect/plugin-pango.xml: + * docs/plugins/inspect/plugin-playback.xml: + * docs/plugins/inspect/plugin-subparse.xml: + * docs/plugins/inspect/plugin-tcp.xml: + * docs/plugins/inspect/plugin-theora.xml: + * docs/plugins/inspect/plugin-typefindfunctions.xml: + * docs/plugins/inspect/plugin-uridecodebin.xml: + * docs/plugins/inspect/plugin-video4linux.xml: + * docs/plugins/inspect/plugin-videorate.xml: + * docs/plugins/inspect/plugin-videoscale.xml: + * docs/plugins/inspect/plugin-videotestsrc.xml: + * docs/plugins/inspect/plugin-volume.xml: + * docs/plugins/inspect/plugin-vorbis.xml: + * docs/plugins/inspect/plugin-ximagesink.xml: + * docs/plugins/inspect/plugin-xvimagesink.xml: + * win32/common/_stdint.h: + * win32/common/config.h: + 0.10.31.3 pre-release + +2011-01-11 18:59:39 +0000 Tim-Philipp Müller + + * po/da.po: + * po/gl.po: + * po/pt_BR.po: + po: update translations + +2011-01-11 14:41:53 +0000 Bastien Nocera + + * tests/examples/seek/jsseek.c: + * tests/examples/seek/scrubby.c: + * tests/examples/seek/seek.c: + examples: allow building with newer GTK+ + GtkFunction is gone, and there's no update policies for + GtkRanges any more (but the default was continuous anyway, + so no need to set it to that mode explicitly). + https://bugzilla.gnome.org/show_bug.cgi?id=639215 + +2011-01-11 14:59:38 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/Makefile.am: + gobject-introspection: pass --library-path as well to make it find the right libgstreamer + Makes things work again properly in uninstalled setups (and + presumably in installed setups where GStreamer is installed + into a non-standard prefix). Requires fixes from core git. + https://bugzilla.gnome.org/show_bug.cgi?id=639039 + +2011-01-11 14:52:51 +0000 Byeong-ryeol Kim + + * gst-libs/gst/pbutils/Makefile.am: + gobject-introspection: fix issue when gold linker is used + Need to pass libgstreamer-0.10 explicitly to linker, since we're + calling gst_init(), which in turn is needed because the encoding + target get_type() function calls gst_value_register(). + https://bugzilla.gnome.org/show_bug.cgi?id=639039 + +2011-01-11 15:49:54 +0200 Stefan Kost + + * common: + Automatic update of common submodule + From e572c87 to f94d739 + +2011-01-10 16:35:44 +0000 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From ccbaa85 to e572c87 + +2011-01-10 14:53:04 +0000 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 46445ad to ccbaa85 + +2011-01-10 15:55:26 +0800 Yang Xichuan + + * ext/ogg/gstoggdemux.c: + oggdemux: remove outdated comment + https://bugzilla.gnome.org/show_bug.cgi?id=639121 + +2011-01-08 02:16:19 +0000 Koop Mast + + * configure.ac: + configure: fix bash-ism + https://bugzilla.gnome.org/show_bug.cgi?id=638961 + +2011-01-08 02:10:03 +0000 Tim-Philipp Müller + + * gst-libs/gst/app/Makefile.am: + * gst-libs/gst/audio/Makefile.am: + * gst-libs/gst/cdda/Makefile.am: + * gst-libs/gst/fft/Makefile.am: + * gst-libs/gst/interfaces/Makefile.am: + * gst-libs/gst/netbuffer/Makefile.am: + * gst-libs/gst/pbutils/Makefile.am: + * gst-libs/gst/riff/Makefile.am: + * gst-libs/gst/rtp/Makefile.am: + * gst-libs/gst/rtsp/Makefile.am: + * gst-libs/gst/sdp/Makefile.am: + * gst-libs/gst/tag/Makefile.am: + * gst-libs/gst/video/Makefile.am: + gobject-introspection: use same PKG_CONFIG_PATH for g-ir-compiler as for g-ir-scanner + Make sure to use the PKG_CONFIG_PATH set at configure time instead of + just relying on an env-var set one. This makes sure both g-ir-compiler + and g-ir-scanner use the same PKG_CONFIG_PATH for determining include + paths etc. + +2011-01-08 01:12:02 +0000 Tim-Philipp Müller + + * pkgconfig/gstreamer-app-uninstalled.pc.in: + * pkgconfig/gstreamer-app.pc.in: + * pkgconfig/gstreamer-audio-uninstalled.pc.in: + * pkgconfig/gstreamer-audio.pc.in: + * pkgconfig/gstreamer-cdda-uninstalled.pc.in: + * pkgconfig/gstreamer-cdda.pc.in: + * pkgconfig/gstreamer-fft-uninstalled.pc.in: + * pkgconfig/gstreamer-fft.pc.in: + * pkgconfig/gstreamer-floatcast.pc.in: + * pkgconfig/gstreamer-interfaces-uninstalled.pc.in: + * pkgconfig/gstreamer-interfaces.pc.in: + * pkgconfig/gstreamer-netbuffer-uninstalled.pc.in: + * pkgconfig/gstreamer-netbuffer.pc.in: + * pkgconfig/gstreamer-pbutils-uninstalled.pc.in: + * pkgconfig/gstreamer-pbutils.pc.in: + * pkgconfig/gstreamer-riff-uninstalled.pc.in: + * pkgconfig/gstreamer-riff.pc.in: + * pkgconfig/gstreamer-rtp-uninstalled.pc.in: + * pkgconfig/gstreamer-rtp.pc.in: + * pkgconfig/gstreamer-rtsp-uninstalled.pc.in: + * pkgconfig/gstreamer-rtsp.pc.in: + * pkgconfig/gstreamer-sdp-uninstalled.pc.in: + * pkgconfig/gstreamer-sdp.pc.in: + * pkgconfig/gstreamer-tag-uninstalled.pc.in: + * pkgconfig/gstreamer-tag.pc.in: + * pkgconfig/gstreamer-video-uninstalled.pc.in: + * pkgconfig/gstreamer-video.pc.in: + pkg-config: add girdir and typelibdir variables to .pc files + We need them when building gir and typelib files for + libraries that depend on these, such as gst-rtsp-server + for example, in an uninstalled setup. + +2011-01-07 12:50:07 +0000 Tim-Philipp Müller + + * configure.ac: + * win32/common/_stdint.h: + * win32/common/config.h: + * win32/common/pbutils-enumtypes.c: + * win32/common/video-enumtypes.c: + 0.10.31.2 pre-release + +2011-01-07 13:04:11 +0100 Edward Hervey + + * gst/encoding/gstencodebin.c: + * gst/encoding/gstencodebin.h: + encodebin: Add missing-plugin support + https://bugzilla.gnome.org/show_bug.cgi?id=638903 + +2011-01-07 12:51:11 +0100 Edward Hervey + + * gst/encoding/gstencodebin.c: + encodebin: Extend documentation + https://bugzilla.gnome.org/show_bug.cgi?id=638901 + +2011-01-07 00:43:07 +0000 Tim-Philipp Müller + + * tests/check/Makefile.am: + tests: never disable g_assert() and cast checks for the unit tests + The unit tests are riddled with g_assert() and friends, sometimes + containing functional code like set_state() calls in them even + (looking at you, pipeline/capsfilter-renegotiation). Make sure we + don't disable assert and cast checks for the unit tests even if + this has been specified for the rest of the code base, e.g. via + --disable-glib-asserts. + +2011-01-06 23:17:12 +0000 Tim-Philipp Müller + + * win32/common/libgstpbutils.def: + win32: udpate pbutils .def file for API change + +2011-01-06 23:13:53 +0000 Tim-Philipp Müller + + * docs/plugins/gst-plugins-base-plugins.hierarchy: + * docs/plugins/gst-plugins-base-plugins.interfaces: + * docs/plugins/gst-plugins-base-plugins.prerequisites: + * docs/plugins/inspect/plugin-adder.xml: + * docs/plugins/inspect/plugin-alsa.xml: + * docs/plugins/inspect/plugin-app.xml: + * docs/plugins/inspect/plugin-audioconvert.xml: + * docs/plugins/inspect/plugin-audiorate.xml: + * docs/plugins/inspect/plugin-audioresample.xml: + * docs/plugins/inspect/plugin-audiotestsrc.xml: + * docs/plugins/inspect/plugin-cdparanoia.xml: + * docs/plugins/inspect/plugin-decodebin.xml: + * docs/plugins/inspect/plugin-encoding.xml: + * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: + * docs/plugins/inspect/plugin-gdp.xml: + * docs/plugins/inspect/plugin-gio.xml: + * docs/plugins/inspect/plugin-gnomevfs.xml: + * docs/plugins/inspect/plugin-libvisual.xml: + * docs/plugins/inspect/plugin-ogg.xml: + * docs/plugins/inspect/plugin-pango.xml: + * docs/plugins/inspect/plugin-playback.xml: + * docs/plugins/inspect/plugin-subparse.xml: + * docs/plugins/inspect/plugin-tcp.xml: + * docs/plugins/inspect/plugin-theora.xml: + * docs/plugins/inspect/plugin-typefindfunctions.xml: + * docs/plugins/inspect/plugin-uridecodebin.xml: + * docs/plugins/inspect/plugin-video4linux.xml: + * docs/plugins/inspect/plugin-videorate.xml: + * docs/plugins/inspect/plugin-videoscale.xml: + * docs/plugins/inspect/plugin-videotestsrc.xml: + * docs/plugins/inspect/plugin-volume.xml: + * docs/plugins/inspect/plugin-vorbis.xml: + * docs/plugins/inspect/plugin-ximagesink.xml: + * docs/plugins/inspect/plugin-xvimagesink.xml: + docs: update docs + +2011-01-06 23:13:35 +0000 Tim-Philipp Müller + + * po/fi.po: + * po/ru.po: + po: update translations + +2011-01-06 23:08:34 +0000 Tim-Philipp Müller + + * ext/pango/gsttextoverlay.c: + textoverlay: make text property controllable too + Because we can, and because it's the most interesting one + to control really, after xpos/ypos. + +2011-01-06 23:01:20 +0000 Lane Brooks + + * ext/pango/Makefile.am: + * ext/pango/gsttextoverlay.c: + * ext/pango/gsttextoverlay.h: + textoverlay: make some properties controllable + https://bugzilla.gnome.org/show_bug.cgi?id=638859 + +2011-01-06 20:37:50 +0000 Tim-Philipp Müller + + * tests/check/libs/.gitignore: + tests: ignore new rtsp test binary + +2011-01-05 15:54:15 -0800 David Schleef + + * ext/ogg/gstoggdemux.c: + oggdemux: ignore header pages when looking for keyframe + This was causing keyframe_granule to be set to 0 for all streams + when seeking to the beginning of the stream, i.e., at the + beginning of playback. Fixes #619778. + +2010-12-29 15:27:44 +0000 Vincent Penquerc'h + + * ext/ogg/gstoggstream.c: + oggstream: when the last keyframe position is not known, do not use -1 + Instead, use either 0 or 1, depending on bitstream version, which give + the correct result for streams which aren't cut off at start. + This allows that function to not return negative granpos. + https://bugzilla.gnome.org/show_bug.cgi?id=638276 + +2011-01-06 17:57:41 +0000 christian schaller + + * gst-plugins-base.spec.in: + Update spec file with discoverer and encodebinchanges + +2011-01-05 15:53:09 +0530 Arun Raghavan + + * docs/libs/gst-plugins-base-libs-sections.txt: + * gst-libs/gst/pbutils/gstdiscoverer-types.c: + * gst-libs/gst/pbutils/gstdiscoverer.c: + * gst-libs/gst/pbutils/gstdiscoverer.h: + discoverer: Documentation updates + Some cosmetic changes and expands on some bits of the documentation to + make it more newbie-friendly. + +2011-01-06 13:08:53 +0100 Robert Swain + + * gst/videorate/gstvideorate.c: + * gst/videorate/gstvideorate.h: + videorate: Fix behaviour for frame rate cap changes + The outgoing buffer timestamp is calculated by scaling an output buffer + count by the src pad frame rate caps. If these caps change, we need to + reset the count and work from a new base timestamp. The new output + buffer timestamp is then the count scaled by the new caps values added + onto the base timestamp. + +2011-01-06 08:47:04 +0100 Edward Hervey + + * tools/gst-discoverer.c: + tools: Improve pretty-printing of tags + Avoids escaping strings for nothing and printing out useless buffer contents. + +2011-01-06 08:46:42 +0100 Edward Hervey + + * tools/gst-discoverer.c: + tools: don't leak the GMainLoop + +2011-01-06 00:28:39 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/encoding-target.c: + pbutils: config.h include should come before all other includes + +2011-01-05 22:02:35 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-profile.h: + * gst/encoding/gstencodebin.c: + * tests/check/libs/profile.c: + * tests/examples/encoding/encoding.c: + encoding: encoding_profile_get_output_caps => _get_input_caps + Makes more sense name-wise + +2011-01-05 20:40:39 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + docs: Add various new symbols + +2011-01-05 01:50:34 +0530 Arun Raghavan + + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-target.c: + encoding-profile: Minor documentation updates + +2011-01-03 19:07:45 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-profile.c: + encoding-profile: Give a better usage example + +2011-01-03 18:52:00 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + * tests/check/libs/profile.c: + * win32/common/libgstpbutils.def: + encoding-target: Fixup loading/saving methods + +2011-01-03 18:51:22 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + encoding-target: more docs cleanups + +2011-01-03 16:07:49 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-target.c: + * tests/check/libs/profile.c: + encoding-target: Change target suffix to .gep + Along with a bunch of other internal cleanups + +2011-01-03 13:21:26 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + encoding-target: Add more docs regarding categories + +2011-01-03 13:20:19 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + * tests/check/libs/profile.c: + * win32/common/libgstpbutils.def: + encoding-target: Add API for list all categories and targets + API: gst_encoding_list_available_categories + API: gst_encoding_list_all_targets + +2010-12-22 18:18:00 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + * gst-libs/gst/pbutils/Makefile.am: + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-profile.h: + * tests/check/libs/profile.c: + * win32/common/libgstpbutils.def: + encoding-profile: Add convenience method to find a profile + API: gst_encoding_profile_find + +2010-12-22 18:16:33 +0100 Edward Hervey + + * configure.ac: + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + * tests/check/libs/profile.c: + encoding-target: Implement save/load feature + Fixes #637735 + +2010-12-22 11:41:41 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + * tests/check/libs/profile.c: + * win32/common/libgstpbutils.def: + encoding-target: Add method to get a profile by name + API: gst_encoding_target_get_profile + +2011-01-05 19:30:50 +0100 Edward Hervey + + * gst/encoding/gstencodebin.c: + encodebin: Convert to new GstElementClass::request_new_pad_full vmethod + +2011-01-05 15:31:09 +0100 Edward Hervey + + * gst-libs/gst/pbutils/pbutils.h: + pbutils: Don't forget to include the encoding headers + +2011-01-05 12:02:02 +0100 Edward Hervey + + * gst-libs/gst/video/video.c: + video: Fix uninitialized variables + reported by macosx gcc + +2010-12-07 14:59:46 +0530 Arun Raghavan + + * gst-libs/gst/pbutils/codec-utils.c: + codec-utils: Minor documentation changes + +2011-01-02 15:48:47 -0800 David Schleef + + * gst/typefind/gsttypefindfunctions.c: + typefind: Add stream-format to h264 caps + +2011-01-02 17:21:54 +0000 Tim-Philipp Müller + + * gst-libs/gst/audio/gstbaseaudiosink.c: + baseaudiosink: default to enable-last-buffer=FALSE for audio sinks + There isn't really any good reason to get the last buffer from an + audio sink, so don't make the sink keep it around unnecessarily. + +2010-12-31 12:14:22 +0000 Tim-Philipp Müller + + * configure.ac: + * gst/playback/Makefile.am: + * gst/playback/gstinputselector.c: + * gst/playback/gstinputselector.h: + * gst/playback/gstplay-marshal.list: + * gst/playback/gstplaybin2.c: + playbin2: use input-selector from core instead of internal copy + +2010-12-31 01:24:50 +0000 Tim-Philipp Müller + + * tests/icles/.gitignore: + * tests/icles/Makefile.am: + tests: add input-selector-test and output-selector-test + Moved from gst-plugins-bad into -base, becasue it uses videotestsrc + and other elements from -base, so it can't be in core. + +2010-11-24 12:22:01 +0200 Stefan Kost + + * tests/icles/output-selector-test.c: + output-selector-test: don't hardcode videosinks and use more colorspace conv. + Use autovideosink instead of hardcoded sinks. Use an additional colorspace + converter between videotestsrc and timeoverlay. + +2009-10-27 11:51:05 -0700 Michael Smith + + * tests/icles/output-selector-test.c: + tests: Remove executable bits from non-executable files. + +2009-02-24 16:33:51 +0100 Sebastian Dröge + + * tests/icles/input-selector-test.c: + tests: move examples directory to tests/examples as in every other GStreamer module + +2008-06-19 13:18:24 +0000 Stefan Kost + + tests: Use BOILERPLATE macro and update output-selector test to the latest api changes. + Original commit message from CVS: + * gst/selector/gstoutputselector.c: + * tests/icles/output-selector-test.c: + Use BOILERPLATE macro and update test to the latest api changes. + +2008-02-07 13:48:20 +0000 Stefan Kost + + tests/icles/output-selector-test.c: Add a fixme comment. + Original commit message from CVS: + * gst/multifile/gstmultifilesink.c: + Add a fixme comment. + * gst/selector/gstoutputselector.c: + Fix same leak as in input-selector. + * tests/icles/output-selector-test.c: + Improve the test. + +2008-01-29 07:38:31 +0000 Stefan Kost + + Replace the switch plugin with the selector plugin. Add output-selector as the opposite of input-selector (was switc... + Original commit message from CVS: + * configure.ac: + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-bad-plugins-docs.sgml: + * docs/plugins/gst-plugins-bad-plugins-sections.txt: + * docs/plugins/gst-plugins-bad-plugins.args: + * docs/plugins/gst-plugins-bad-plugins.hierarchy: + * docs/plugins/gst-plugins-bad-plugins.interfaces: + * docs/plugins/gst-plugins-bad-plugins.signals: + * docs/plugins/inspect/plugin-metadata.xml: + * docs/plugins/inspect/plugin-selector.xml: + * docs/plugins/inspect/plugin-soundtouch.xml: + * docs/plugins/inspect/plugin-switch.xml: + * gst/selector/.cvsignore: + * gst/selector/Makefile.am: + * gst/selector/gstinputselector.c: + * gst/selector/gstinputselector.h: + * gst/selector/gstoutputselector.c: + * gst/selector/gstoutputselector.h: + * gst/selector/gstselector-marshal.list: + * gst/selector/gstselector.c: + * gst/selector/selector.vcproj: + * gst/switch/.cvsignore: + * gst/switch/Makefile.am: + * gst/switch/gstswitch-marshal.list: + * gst/switch/gstswitch.c: + * gst/switch/gstswitch.h: + * gst/switch/switch.vcproj: + * tests/icles/.cvsignore: + * tests/icles/Makefile.am: + * tests/icles/output-selector-test.c: + Replace the switch plugin with the selector plugin. Add output- + selector as the opposite of input-selectoo (was switch). Add a test + for output-selector. Add docs for the elements. The vcproj needs + update. Fixes #500142. + +2010-12-30 18:08:05 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertpaudiopayload.c: + baseaudiopay: fix timestamps on buffer lists + Fix the outgoing timestamps and RTP timestamps on outgoing buffers when using + buffer lists. + +2010-12-29 22:36:41 +0000 Tim-Philipp Müller + + * gst/typefind/gsttypefindfunctions.c: + typefinding: assume EBML files without doctype are matroska + https://bugzilla.gnome.org/show_bug.cgi?id=638019 + +2010-12-29 12:53:36 +0100 Wim Taymans + + * gst/tcp/gstmultifdsink.c: + multifdsink: only keep last valid timestamp + Fixes #634397 + +2010-10-13 17:09:13 +0200 Andoni Morales Alastruey + + * gst/tcp/gstmultifdsink.c: + * gst/tcp/gstmultifdsink.h: + multifdsink: add first and last buffer's timestamp to the stats + +2010-12-29 11:51:42 +0000 Tim-Philipp Müller + + * ext/ogg/gstoggstream.c: + ogg: fix typo in comment + +2010-12-28 17:39:58 +0000 Vincent Penquerc'h + + * ext/ogg/gstoggstream.c: + oggstream: fix interpretation of Theora granule position + The offset part of the granpos is not a sign of the newer encoding. + Use the version number instead. + This fixes the criticals thrown by theoraparse, and (at last) the + remaining part of #553244. + +2010-11-25 17:01:04 +0100 Havard Graff + + * gst-libs/gst/audio/gstbaseaudiosink.c: + baseaudiosink: protect against ringbuffer disappearing while in a query + Observed a case where the sink went to null-state during the query, + hence the ringbuffer-pointer was NULL, causing a crash. + Moving the ringbuffer-check code until after the query, and hold the + lock during the check and while using the spec-values. It should not matter + to the query wether the ringbuffer is present or not, and it actually + gets a time bit more time to get the ringbuffer set up in this case! + Fixes #635231 + +2010-12-28 19:39:18 +0100 Wim Taymans + + * ext/ogg/gstoggdemux.c: + oggdemux: handle pads that are not added yet + Don't try to stream data on pads that are not added yet. This happens while we + discover the different streams. + +2010-12-28 11:41:49 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertpdepayload.c: + basedepay: fix refcounting issue + Make sure that when _make_writable() returns a new buffer, we actually push that + one instead of the old one. + +2010-12-25 15:22:42 +0000 Vincent Penquerc'h + + * ext/ogg/gstoggstream.c: + oggstream: implement tag extraction for Kate streams + This will mainly allow Totem to know the language of those streams, + so the subtitle selection menu gets properly filled out. + https://bugzilla.gnome.org/show_bug.cgi?id=638005 + +2010-12-26 17:29:38 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/descriptions.c: + pbutils: add description for DVB subtitle caps + +2010-12-23 17:18:17 +0000 Vincent Penquerc'h + + * ext/ogg/gstoggdemux.c: + oggdemux: set headers on caps + This will allow switching from one stream to another without having to send + the headers for the new stream again. + https://bugzilla.gnome.org/show_bug.cgi?id=637927 + +2010-12-22 15:29:56 -0800 David Schleef + + * ext/ogg/gstoggstream.c: + oggstream: Fix parsing of theora size + +2010-12-22 19:06:56 +0000 Vincent Penquerc'h + + * ext/ogg/gstoggdemux.c: + oggdemux: Don't use gst_pad_alloc_buffer() + allocate buffers using gst_buffer_new_and_alloc() instead of + gst_pad_alloc_buffer_and_set_caps(), as the first one will + cause the pad to block, and we don't want that since that will + prevent subsequent pads from being fed if a block occurs at + start, when all pads must be fed for playback to start. + This fixes autoplugging of the tiger element and other things. + https://bugzilla.gnome.org/show_bug.cgi?id=637822 + +2010-12-22 18:12:14 +0100 Edward Hervey + + * gst/encoding/gstencodebin.c: + encodebin: Also use "Formatter"s for container formats + +2010-12-22 18:19:48 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-target.c: + encoding-target: Fix typo + +2010-12-22 10:32:03 -0300 Thiago Santos + + * gst-libs/gst/tag/gstexiftag.c: + tag: exif: Fix unitialized data warning + Fixes a valgrind warning on jifmux tests on -bad caused by + unitialized bytes. + Fixes #637758 + +2010-12-22 13:56:12 +0100 Alessandro Decina + + * gst/encoding/gstencodebin.c: + encodebin: minor fix in error handling. + Don't call gst_bin_remove (bin, ). + +2010-12-21 18:51:29 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/gstdiscoverer-types.c: + * gst-libs/gst/pbutils/gstdiscoverer.c: + * gst-libs/gst/pbutils/install-plugins.c: + * gst-libs/gst/pbutils/missing-plugins.c: + pbutils: More gtk-doc annotations + +2010-12-21 10:26:40 +0000 Vincent Penquerc'h + + * gst/playback/gstplaybin2.c: + playbin2: delay stream-changed messages + https://bugzilla.gnome.org/show_bug.cgi?id=637586 + +2010-12-21 16:33:50 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-target.c: + * tests/check/libs/profile.c: + encoding-target: Ensure target names and categories are valid + +2010-12-21 15:11:10 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertpdepayload.h: + depay: update some docs + +2010-12-21 15:02:18 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertpdepayload.c: + * gst-libs/gst/rtp/gstbasertpdepayload.h: + rtpdepayloade: add support for getting events + Add support for intercepting sink events in the depayloader by adding a new + vmethod. + +2010-12-21 13:37:41 +0100 Wim Taymans + + * ext/vorbis/gstvorbisdec.c: + vorbisdec: keep timestamps when no decoded output + Keep track of the timestamps even when we didn't generate decodable output. + +2010-12-21 13:19:38 +0100 Wim Taymans + + * ext/vorbis/gstvorbisdec.c: + vorbisdec: avoid using invalid timestamps + +2010-12-21 10:41:27 +0100 Wim Taymans + + * tests/examples/seek/seek.c: + seek: don't pause for live buffering messages + +2010-12-20 18:29:15 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertppayload.c: + basertppay: use RTP base time when invalid timestamps + When we have an invalid running-time (because we clipped, for example) use the + RTP base time for timestamping instead of generating wrong RTP timestamps. + +2010-12-20 18:28:14 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertppayload.c: + rtppayload: copy applied rate to segment + Use set_segment_full to copy all segment values to the segment structure. + +2010-12-21 13:09:34 +0100 Edward Hervey + + * tests/check/elements/encodebin.c: + * tests/check/libs/profile.c: + tests: Update container-less profile checks + +2010-12-21 13:08:15 +0100 Edward Hervey + + * gst-libs/gst/pbutils/encoding-profile.c: + encoding-profile: Add guard against profiles without format + +2010-12-21 13:07:27 +0100 Edward Hervey + + * gst/encoding/gstencodebin.c: + encodebin: Fix usage of non-container profiles + +2010-12-17 16:10:53 +0100 Edward Hervey + + * docs/plugins/inspect/plugin-videoscale.xml: + docs: Update for videoscale class changes + +2010-12-20 17:46:48 +0100 Edward Hervey + + * common: + Automatic update of common submodule + From 169462a to 46445ad + +2010-12-19 13:41:22 +0100 Edward Hervey + + * gst-libs/gst/pbutils/gstdiscoverer.c: + gstdiscoverer: Don't leak tags + +2010-12-19 13:22:23 +0100 Edward Hervey + + * tools/gst-discoverer.c: + gst-discoverer: show global tags by default + +2010-12-19 09:53:08 +0100 Sebastian Dröge + + * tests/check/libs/rtsp.c: + rtsp: Fix memory leaks in the gst_rtsp_url_decode_path_components() unit tests + +2010-12-18 20:47:00 +0100 Sebastian Dröge + + * tests/examples/encoding/Makefile.am: + examples: Fix encodebin example CFLAGS and LDFLAGS + Previously it would only succeed to link if a new enough + libgstpbutils-0.10 was installed in the default library + search path. + +2010-12-17 14:16:18 +0000 Vincent Penquerc'h + + * ext/ogg/gstoggdemux.c: + * ext/ogg/gstoggstream.c: + ogg: implement packet duration query for kate streams + https://bugzilla.gnome.org/show_bug.cgi?id=637519 + +2010-12-17 19:06:27 -0600 Rob Clark + + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-profile.h: + * gst/encoding/gstencodebin.c: + fix compile errors on macosx + with i686-apple-darwin10-gcc-4.2.1: + encoding-profile.h:134: warning: type qualifiers ignored on function return type + encoding-profile.c:240: warning: type qualifiers ignored on function return type + gstencodebin.c: In function 'next_unused_stream_profile': + gstencodebin.c:454: warning: format '%d' expects type 'int', but argument 8 has type 'GType' + gstencodebin.c:464: warning: format '%d' expects type 'int', but argument 8 has type 'GType' + +2010-12-17 00:49:26 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.c: + audioresample: corrected buffer duration calculation to account for nonzero initial timestamp + Since we calculate timestamps by: + timestamp = t0 + (out samples) / (out rate) + and durations by: + duration = ((out samples) + (processed samples)) / (out rate) - timestamp + if t0 is nonzero, this would simplify to + duration = t0 + (processed samples) / (out rate). + This duration is too large by the amount t0. We should have done: + duration = t0 + ((out samples) + (processed samples)) / (out rate) - timestamp + so that + duration = (processed samples) / (out rate). + +2010-12-16 20:40:33 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.h: + audioresample: changed num_gap_samples, num_nongap_samples from guint32 to guint64 so that gaps of greater than or equal to 2^32 samples do not cause integer overflow + +2010-12-16 20:38:31 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.c: + audioresample: push half a history length, instead of a full history length, at end-of-stream so that output segment and input segment have same duration + +2010-12-16 20:34:13 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.c: + * gst/audioresample/gstaudioresample.h: + audioresample: renamed count_gap, count_nongap to more descriptive num_gap_samples, num_nongap_samples + +2010-12-16 20:32:07 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.c: + audioresample: replaced void* with gpointer + +2010-12-16 20:30:24 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.c: + audioresample: initial filter transient discarded; unit tests passing + +2010-12-16 20:09:58 -0800 Leo Singer + + * gst/audioresample/gstaudioresample.c: + * gst/audioresample/gstaudioresample.h: + * gst/audioresample/resample.c: + * gst/audioresample/speex_resampler.h: + * gst/audioresample/speex_resampler_wrapper.h: + Revert "Revert "audioresample: Add GAP flag support"" + This reverts commit 35c76b3409dde7f2dcc8232388a47a1b99b661a7. + Conflicts: + gst/audioresample/gstaudioresample.c + gst/audioresample/gstaudioresample.h + +2010-12-16 10:26:43 +0000 Vincent Penquerc'h + + * ext/pango/gsttextoverlay.c: + timeoverlay: add missing break + https://bugzilla.gnome.org/show_bug.cgi?id=637377 + +2010-12-16 10:11:43 +0100 Sebastian Dröge + + * gst/videoscale/gstvideoscale.c: + videoscale: Change classification to Filter/Converter/Video/Scaler + +2010-12-15 23:47:29 +0200 Stefan Kost + + * win32/common/libgstrtsp.def: + win32: update the def file with the new rtsp api + +2010-12-15 17:51:36 +0100 Andy Wingo + + add gst_rtsp_url_decode_path_components + * gst-libs/gst/rtsp/gstrtspurl.h: + * gst-libs/gst/rtsp/gstrtspurl.c (gst_rtsp_url_decode_path_components): + New public function, returns a strv of uri-decoded path components. + * tests/check/Makefile.am: + * tests/check/libs/rtsp.c: Add tests. + +2010-12-15 16:35:43 +0100 Wim Taymans + + * win32/common/libgstrtp.def: + win32: update defs file + +2010-12-15 16:30:55 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstrtpbuffer.c: + rtpbuffer: relax arrangement for RTP bufferlists + Don't assume there are exactly 2 buffers but allow cases where the header and + payload are in 1 buffer or where the payload is in more buffers. + +2010-12-15 14:55:34 +0200 Stefan Kost + + * common: + Automatic update of common submodule + From 20742ae to 169462a + +2010-12-15 12:58:47 +0100 Wim Taymans + + * gst-libs/gst/rtp/gstbasertpdepayload.c: + * gst-libs/gst/rtp/gstbasertpdepayload.h: + basedepay: add support for buffer lists in the depayloader + Add support for buffer lists in the depayloader. + +2010-09-13 10:08:47 +0200 Edward Hervey + + * configure.ac: + * tests/examples/Makefile.am: + * tests/examples/encoding/.gitignore: + * tests/examples/encoding/Makefile.am: + * tests/examples/encoding/encoding.c: + * tests/examples/encoding/gstcapslist.c: + * tests/examples/encoding/gstcapslist.h: + examples: encoding example + Along with gstcapslist + +2010-08-13 17:36:38 +0200 Edward Hervey + + * 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: + * docs/plugins/inspect/plugin-libvisual.xml: + * gst/encoding/.gitignore: + * gst/encoding/Makefile.am: + * gst/encoding/gstencode-marshal.list: + * gst/encoding/gstencodebin.c: + * gst/encoding/gstencodebin.h: + * gst/encoding/gstsmartencoder.c: + * gst/encoding/gstsmartencoder.h: + * gst/encoding/gststreamcombiner.c: + * gst/encoding/gststreamcombiner.h: + * gst/encoding/gststreamsplitter.c: + * gst/encoding/gststreamsplitter.h: + * tests/check/Makefile.am: + * tests/check/elements/.gitignore: + * tests/check/elements/encodebin.c: + gst: New encoding plugin + https://bugzilla.gnome.org/show_bug.cgi?id=627476 + +2010-08-13 17:27:52 +0200 Edward Hervey + + * docs/design/Makefile.am: + * docs/design/design-encoding.txt: + * docs/libs/gst-plugins-base-libs-docs.sgml: + * docs/libs/gst-plugins-base-libs-sections.txt: + * docs/libs/gst-plugins-base-libs.types: + * gst-libs/gst/pbutils/Makefile.am: + * gst-libs/gst/pbutils/encoding-profile.c: + * gst-libs/gst/pbutils/encoding-profile.h: + * gst-libs/gst/pbutils/encoding-target.c: + * gst-libs/gst/pbutils/encoding-target.h: + * tests/check/Makefile.am: + * tests/check/libs/.gitignore: + * tests/check/libs/profile.c: + * win32/common/libgstpbutils.def: + pbutils: New Profile library + https://bugzilla.gnome.org/show_bug.cgi?id=627476 + +2010-12-15 12:21:05 +0200 Stefan Kost + + * configure.ac: + configure: use the -Bsymbolic-functions linker flag if supported + This feature turns intra library calls into direct function calls and thus makes + them a little faster. The downside is that this causes problems for e.g. + LD_PRELOAD based tools. Thus add a configure option to turn it off. + +2010-12-14 00:16:13 -0800 David Schleef + + * gst/typefind/gsttypefindfunctions.c: + typefind: Add check for yuv4mpeg + +2010-12-13 18:05:41 +0200 Stefan Kost + + * gst-libs/gst/pbutils/descriptions.c: + pbutils: spell out two more container formats + +2010-12-13 16:20:23 +0200 Stefan Kost + + * gst-libs/gst/pbutils/gstdiscoverer-types.c: + * gst-libs/gst/pbutils/gstdiscoverer.c: + * gst-libs/gst/pbutils/gstdiscoverer.h: + * gst-libs/gst/pbutils/pbutils-private.h: + * tools/gst-discoverer.c: + * win32/common/libgstpbutils.def: + discoverer: query seekability + Besides the duration we can also query the seekability of a stream. Use the new + API in the gst-discoverer tool. + API: gst_discoverer_info_get_seekable + +2010-12-13 16:23:04 +0200 Stefan Kost + + * common: + Automatic update of common submodule + From 011bcc8 to 20742ae + +2010-12-13 13:04:40 +0100 Mark Nauwelaerts + + * tests/check/elements/audioresample.c: + tests: audioresample: adjust unit test to relaxed discont checking + +2010-12-13 12:34:58 +0200 Stefan Kost + + * docs/Makefile.am: + * docs/design/Makefile.am: + make: move the design doc also on the Makefile.am level (for dist) + +2010-12-13 10:05:00 +0100 Mark Nauwelaerts + + * gst/audioresample/gstaudioresample.c: + audioresample: relax discont checking slightly + +2010-12-13 09:56:04 +0100 Mark Nauwelaerts + + * gst/audioresample/gstaudioresample.c: + * gst/audioresample/gstaudioresample.h: + audioresample: provide as much valid output ts and offset as valid input + ... by independently tracking time and offset, rather than having no offset + leading to no output ts. + +2010-12-13 10:41:24 +0200 Stefan Kost + + * gst/typefind/gsttypefindfunctions.c: + typefinders: name "aac" typefinder "audio/aac" + This is in sync how we call the others. + +2010-12-13 09:58:53 +0200 Stefan Kost + + * docs/design-audiosinks.txt: + * docs/design/design-audiosinks.txt: + docs: move design doc to design folder + +2010-12-11 19:33:33 +0200 Zeeshan Ali (Khattak) + + * gst/videotestsrc/generate_sine_table.c: + videotestsrc: Add a missing return statement + +2010-12-11 17:18:49 +0100 Sebastian Dröge + + * gst/playback/gstdecodebin2.c: + decodebin2: Deprecate new-decoded-pad and removed-decoded-pad signals + They're really the same as pad-added and pad-removed from GstElement + and it doesn't make sense to have two signals for the same thing. + +2010-12-11 17:14:36 +0100 Sebastian Dröge + + * gst/playback/gstdecodebin2.c: + decodebin2: Emit "remove-decoded-pad" signal when pads are removed from decodebin2 + Fixes bug #636198. + +2010-12-10 18:57:56 +0100 Wim Taymans + + * gst-libs/gst/app/gstappsink.c: + appsink: unset flushing flag when starting + When we start again after being stopped, clear the flushing flag or else + it will always be TRUE. + Fixes #636769 + +2010-12-09 16:57:35 +0100 Edward Hervey + + * gst-libs/gst/pbutils/descriptions.c: + pbutils: Add/Fix some media descriptions + Fixes #623413 + +2010-12-09 08:40:25 +0100 Gavin Stark + + * sys/xvimage/xvimagesink.c: + xvimagesink: Use gst_caps_can_intersect() instead of gst_caps_intersect() + Fixes a memory leak and bug #636827. + +2010-12-08 12:55:24 +0100 Mark Nauwelaerts + + * gst/typefind/gsttypefindfunctions.c: + typefinding: improve iso media typefinding + ... by also considering compatible brands rather than only aiming at major brand + (of which there are a seemingly ever expanding great many). + +2010-12-08 12:28:32 +0200 Stefan Kost + + * tests/check/libs/pbutils.c: + tests: remove superflous ';' and reindent + +2010-12-08 12:09:45 +0200 Stefan Kost + + * gst-libs/gst/pbutils/gstdiscoverer-types.c: + * gst-libs/gst/pbutils/gstdiscoverer.c: + * gst-libs/gst/rtp/gstrtpbuffer.c: + docs: fix wrong use of Since: keyword + +2010-12-07 20:28:37 +0200 René Stadler + + * tests/check/gst/typefindfunctions.c: + tests: add AC-3, E-AC-3 typefind tests + +2010-12-03 17:33:40 +0200 René Stadler + + * gst/typefind/gsttypefindfunctions.c: + typefind: ignore AC-3 BSIDs 9, 10 and >16 + These are reserved for future extensions which will not be backwards + compatible to E-AC-3. + +2010-12-03 16:54:21 +0200 René Stadler + + * gst/typefind/gsttypefindfunctions.c: + typefind: accept consecutive AC-3 frames of different sizes + This is perfectly valid and occurs in particular when there are + (in)dependent substreams present. + +2010-12-03 16:22:32 +0200 René Stadler + + * gst/typefind/gsttypefindfunctions.c: + typefind: remove useless masking in (E-)AC-3 typefinders + +2010-12-03 16:14:15 +0200 René Stadler + + * gst/typefind/gsttypefindfunctions.c: + typefind: stop scanning after suggesting E-AC-3 caps + +2010-12-03 18:08:58 +0200 René Stadler + + * gst/typefind/gsttypefindfunctions.c: + typefind: fix E-AC-3 frame size parsing + Frame size is given in words; it is already multiplied by two where + needed, so the left shift is superfluous. This extra multiplication + caused the code to inspect the third packet instead of the second, + which would fail for files where the second packet has a size + different from the first. + +2010-12-07 17:35:14 +0100 Edward Hervey + + * gst-libs/gst/rtsp/gstrtsptransport.h: + rtsp: Move around the typedefs to make GIR happy + Otherwise it will generate they symbols as _GstRTSP* (with the leading + underscore). + +2010-12-04 14:48:46 +0000 Tim-Philipp Müller + + * tests/examples/app/appsrc-ra.c: + * tests/examples/app/appsrc-seekable.c: + * tests/examples/app/appsrc-stream.c: + * tests/examples/app/appsrc-stream2.c: + tests: use GLib 2.22 API unconditionally + +2010-12-04 14:45:58 +0000 Tim-Philipp Müller + + * gst-libs/gst/pbutils/gstdiscoverer.c: + * gst-libs/gst/tag/lang.c: + * gst-libs/gst/tag/mklangtables.c: + * gst-libs/gst/video/convertframe.c: + libs: use GLib 2.22 API unconditionally + +2010-12-03 17:41:18 +0100 Benjamin Gaignard + + * Android.mk: + * android/NOTICE: + * android/alsa.mk: + * android/app.mk: + * android/app_plugin.mk: + * android/audio.mk: + * android/audioconvert.mk: + * android/decodebin.mk: + * android/decodebin2.mk: + * android/gdp.mk: + * android/gst-libs/gst/app/gstapp-marshal.c: + * android/gst-libs/gst/app/gstapp-marshal.h: + * android/gst-libs/gst/audio/audio-enumtypes.c: + * android/gst-libs/gst/audio/audio-enumtypes.h: + * android/gst-libs/gst/interfaces/interfaces-enumtypes.c: + * android/gst-libs/gst/interfaces/interfaces-enumtypes.h: + * android/gst-libs/gst/interfaces/interfaces-marshal.c: + * android/gst-libs/gst/interfaces/interfaces-marshal.h: + * android/gst-libs/gst/pbutils/pbutils-enumtypes.c: + * android/gst-libs/gst/pbutils/pbutils-enumtypes.h: + * android/gst-libs/gst/rtsp/gstrtsp-enumtypes.c: + * android/gst-libs/gst/rtsp/gstrtsp-enumtypes.h: + * android/gst-libs/gst/rtsp/gstrtsp-marshal.c: + * android/gst-libs/gst/rtsp/gstrtsp-marshal.h: + * android/gst-libs/gst/video/video-enumtypes.c: + * android/gst-libs/gst/video/video-enumtypes.h: + * android/gst/playback/gstplay-marshal.c: + * android/gst/playback/gstplay-marshal.h: + * android/gst/tcp/gsttcp-enumtypes.c: + * android/gst/tcp/gsttcp-enumtypes.h: + * android/gst/tcp/gsttcp-marshal.c: + * android/gst/tcp/gsttcp-marshal.h: + * android/interfaces.mk: + * android/netbuffer.mk: + * android/pbutils.mk: + * android/playbin.mk: + * android/queue2.mk: + * android/riff.mk: + * android/rtp.mk: + * android/rtsp.mk: + * android/sdp.mk: + * android/tag.mk: + * android/tcp.mk: + * android/typefindfunctions.mk: + * android/video.mk: + Add build system for Android + +2010-12-03 15:46:07 +0100 Wim Taymans + + * win32/common/libgstvideo.def: + defs: add new symbol + +2010-10-27 13:49:41 +0200 Mark Nauwelaerts + + * ext/ogg/gstoggstream.c: + oggstream: additional tag extraction + ... supporting theora, flac, speex, celt. + Fixes #629349. + +2010-10-27 12:08:25 +0200 Mark Nauwelaerts + + * ext/ogg/gstoggdemux.c: + * ext/ogg/gstoggstream.c: + * ext/ogg/gstoggstream.h: + oggstream: use separate tag extraction vfunction + +2010-10-27 11:58:53 +0200 Mark Nauwelaerts + + * ext/ogg/gstoggstream.c: + oggstream: refactor vorbis comment tag extraction + +2010-10-27 11:16:15 +0200 Mark Nauwelaerts + + * ext/ogg/gstoggdemux.c: + oggdemux: plug some oggstream leaks + +2010-10-27 10:59:03 +0200 Mark Nauwelaerts + + * ext/ogg/gstoggstream.c: + * ext/ogg/gstoggstream.h: + oggstream: streamline tag extraction and prevent some leaks + +2010-10-27 10:58:16 +0200 Mark Nauwelaerts + + * ext/ogg/gstoggdemux.c: + oggdemux: send stream tags after newsegment and global tags + +2010-09-14 23:08:51 +0300 Sreerenj Balachandran + + * ext/ogg/gstoggdemux.c: + * ext/ogg/gstoggstream.c: + * ext/ogg/gstoggstream.h: + oggdemux: perform more (vorbis comment header) tag extractions + In particular, move comment header parsing to gstoggstrem.c. + Thanks to Felipe Contreras. + Fixes #629349 (partially). + +2010-10-27 10:20:15 +0200 Mark Nauwelaerts + + * gst-libs/gst/riff/riff-ids.h: + riff: document omitted field in _gst_riff_strf_auds + (aka WAVEFORMATEX) + +2010-10-10 17:15:53 -0700 David Schleef + + * ext/ogg/gstoggstream.c: + oggstream: fix incorrect warning on skeleton headers + +2010-11-20 19:02:50 -0800 David Schleef + + * ext/ogg/gstoggparse.c: + * ext/ogg/gstoggstream.c: + * ext/ogg/gstoggstream.h: + oggparse: Set DELTA_UNIT on buffers + +2010-12-03 00:01:06 +0000 Tim-Philipp Müller + + * tests/check/libs/video.c: + tests: fix video library unit test and skip non-working YUV9/YVU9 parts for now + +2010-12-02 23:49:31 +0000 Tim-Philipp Müller + + * gst-libs/gst/video/video.c: + video: add missing break statement for the GST_VIDEO_FORMAT_RGB8_PALETTED case + +2010-11-15 22:02:07 +0200 Evan Broder + + * tools/gst-visualise-m.m: + gst-visualise: trim unused perl dependency + Remove an unused perl module. Fixes #634522. + +2010-11-01 23:07:12 +0200 Stefan Kost + + * gst/playback/gstplaybin2.c: + playbin2: add some logging for failure case + +2010-11-01 23:06:21 +0200 Stefan Kost + + * gst/playback/gstinputselector.c: + inputselector: log times in human readable form + +2010-11-01 22:44:16 +0200 Stefan Kost + + * gst/playback/gstinputselector.c: + inputselector: more G_PARAM_STATIC_STRINGS use + +2010-11-01 22:42:23 +0200 Stefan Kost + + * gst/playback/gstinputselector.c: + inputselector: move reoccuring logs to LOG and remove a double info + Less debug spew in DEBUG category. No need to log pad again if we use + GST_LOG_OBJECT(pad,...). + +2010-12-02 19:11:37 +0100 Edward Hervey + + * gst-libs/gst/rtsp/Makefile.am: + libgstrtsp: Fix typo in .pc to use for GIR + +2010-12-02 15:16:25 +0100 Edward Hervey + + * docs/libs/gst-plugins-base-libs-sections.txt: + * docs/plugins/gst-plugins-base-plugins.hierarchy: + * docs/plugins/gst-plugins-base-plugins.interfaces: + * docs/plugins/gst-plugins-base-plugins.prerequisites: + docs: Add a whole bunch of symbols that were unused to the proper sections + +2010-11-10 11:02:27 +0100 Wim Taymans + + * gst-libs/gst/sdp/gstsdpmessage.c: + sdp: only parse TTL for IP4 addresses + Only IP4 addresses can have a TTL in the address. + +2010-11-10 10:53:41 +0100 Wim Taymans + + * gst-libs/gst/sdp/gstsdpmessage.c: + * gst-libs/gst/sdp/gstsdpmessage.h: + * win32/common/libgstsdp.def: + sdp: add method to check for multicast addresses + Expose a previously internal method to check for multicast addresses. + See #634093 + +2010-11-03 11:13:08 +0100 Sebastian Dröge + + * gst-libs/gst/pbutils/gstpluginsbaseversion.h.in: + pbutils: Take nano version into account in GST_CHECK_PLUGINS_BASE_VERSION() + If the nano is > 0 the current version should be handled the same as + micro + 1. + +2010-11-03 09:51:40 +0100 Sebastian Dröge + + * gst-libs/gst/video/video.c: + * gst-libs/gst/video/video.h: + video: Add YUV9, YVU9 and IYU1 video formats + API: GST_VIDEO_FORMAT_YUV9: planar 4:1:0 YUV + API: GST_VIDEO_FORMAT_YVU9: planar 4:1:0 YUV (chroma planes swapped) + API: GST_VIDEO_FORMAT_IYU1: packed 4:1:1 YUV (Cr-Y0-Y1-Cb-Y2-Y3) + +2010-11-02 11:57:09 +0100 Sebastian Dröge + + * gst-libs/gst/video/video.c: + * gst-libs/gst/video/video.h: + video: Add 8-bit paletted RGB + API: Add GST_VIDEO_FORMAT_RGB8_PALETTED + API: Add GST_VIDEO_CAPS_RGB8_PALETTED + API: Add gst_video_parse_caps_palette() + +2010-10-31 19:17:28 +0100 Sebastian Dröge + + * ext/gnomevfs/gstgnomevfssrc.c: + gnomevfssrc: Remove dead assignment + +2010-10-31 19:14:27 +0100 Sebastian Dröge + + * gst/tcp/gsttcp.c: + tcp: Remove dead assignment + +2010-10-31 19:11:53 +0100 Sebastian Dröge + + * gst/playback/gstplaysink.c: + playsink: gen_video_chain() always returns a bin, no need to check for that + +2010-10-31 19:08:32 +0100 Sebastian Dröge + + * gst/playback/gststreamsynchronizer.c: + streamsynchronizer: If we get EOS for an unknown stream just do nothing + instead of dereferencing NULL pointers. This can happen if the stream + was just removed from the streamsynchronizer in a bad time. + +2010-10-31 19:06:00 +0100 Sebastian Dröge + + * gst/playback/gstplaysink.c: + playsink: gen_video_deinterlace_chain() always returns a bin, no need to check that + +2010-10-31 19:01:49 +0100 Sebastian Dröge + + * sys/v4l/v4l_calls.c: + v4l: If no video tuner is the requested one don't read unitialized data + +2010-10-25 14:13:16 +0100 Sebastian Dröge + + * sys/ximage/ximagesink.c: + ximagesink: Add docs for the new property + Including Since markers + +2010-10-25 14:11:01 +0100 Sebastian Dröge + + * sys/xvimage/xvimagesink.c: + xvimagesink: Add docs for the new property + Including Since markers + +2010-10-25 14:09:39 +0100 Sebastian Dröge + + * sys/xvimage/xvimagesink.c: + xvimagesink: Use PROP_ instead of ARG_ for the property enums + +2010-10-25 14:09:20 +0100 Andrea Sebastianutti + + * sys/xvimage/xvimagesink.c: + xvimagesink: Add read-only properties window-width and window-height + +2010-10-25 14:08:43 +0100 Andrea Sebastianutti + + * sys/ximage/ximagesink.c: + ximagsink: Add read-only properties window-width and window-height + +2010-10-17 14:26:23 +0200 Sebastian Dröge + + * gst-libs/gst/video/video.c: + video: Return correct component width/height for A420 + +2010-12-02 00:15:25 +0000 Tim-Philipp Müller + + * configure.ac: + Bump GLib requirement to >= 2.22 + See http://gstreamer.freedesktop.org/wiki/ReleasePlanning/GLibRequirement + +2010-12-02 00:12:51 +0000 Tim-Philipp Müller + + * configure.ac: + * docs/plugins/gst-plugins-base-plugins.hierarchy: + * docs/plugins/inspect/plugin-adder.xml: + * docs/plugins/inspect/plugin-alsa.xml: + * docs/plugins/inspect/plugin-app.xml: + * docs/plugins/inspect/plugin-audioconvert.xml: + * docs/plugins/inspect/plugin-audiorate.xml: + * docs/plugins/inspect/plugin-audioresample.xml: + * docs/plugins/inspect/plugin-audiotestsrc.xml: + * docs/plugins/inspect/plugin-cdparanoia.xml: + * docs/plugins/inspect/plugin-decodebin.xml: + * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: + * docs/plugins/inspect/plugin-gdp.xml: + * docs/plugins/inspect/plugin-gio.xml: + * docs/plugins/inspect/plugin-gnomevfs.xml: + * docs/plugins/inspect/plugin-libvisual.xml: + * docs/plugins/inspect/plugin-ogg.xml: + * docs/plugins/inspect/plugin-pango.xml: + * docs/plugins/inspect/plugin-playback.xml: + * docs/plugins/inspect/plugin-subparse.xml: + * docs/plugins/inspect/plugin-tcp.xml: + * docs/plugins/inspect/plugin-theora.xml: + * docs/plugins/inspect/plugin-typefindfunctions.xml: + * docs/plugins/inspect/plugin-uridecodebin.xml: + * docs/plugins/inspect/plugin-video4linux.xml: + * docs/plugins/inspect/plugin-videorate.xml: + * docs/plugins/inspect/plugin-videoscale.xml: + * docs/plugins/inspect/plugin-videotestsrc.xml: + * docs/plugins/inspect/plugin-volume.xml: + * docs/plugins/inspect/plugin-vorbis.xml: + * docs/plugins/inspect/plugin-ximagesink.xml: + * docs/plugins/inspect/plugin-xvimagesink.xml: + * win32/common/_stdint.h: + * win32/common/config.h: + Back to development + +=== release 0.10.31 === + +2010-11-30 19:25:44 +0000 Tim-Philipp Müller + + * ChangeLog: + * NEWS: + * RELEASE: + * configure.ac: + * docs/plugins/gst-plugins-base-plugins.args: + * docs/plugins/gst-plugins-base-plugins.hierarchy: + * docs/plugins/inspect/plugin-adder.xml: + * docs/plugins/inspect/plugin-alsa.xml: + * docs/plugins/inspect/plugin-app.xml: + * docs/plugins/inspect/plugin-audioconvert.xml: + * docs/plugins/inspect/plugin-audiorate.xml: + * docs/plugins/inspect/plugin-audioresample.xml: + * docs/plugins/inspect/plugin-audiotestsrc.xml: + * docs/plugins/inspect/plugin-cdparanoia.xml: + * docs/plugins/inspect/plugin-decodebin.xml: + * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: + * docs/plugins/inspect/plugin-gdp.xml: + * docs/plugins/inspect/plugin-gio.xml: + * docs/plugins/inspect/plugin-gnomevfs.xml: + * docs/plugins/inspect/plugin-libvisual.xml: + * docs/plugins/inspect/plugin-ogg.xml: + * docs/plugins/inspect/plugin-pango.xml: + * docs/plugins/inspect/plugin-playback.xml: + * docs/plugins/inspect/plugin-subparse.xml: + * docs/plugins/inspect/plugin-tcp.xml: + * docs/plugins/inspect/plugin-theora.xml: + * docs/plugins/inspect/plugin-typefindfunctions.xml: + * docs/plugins/inspect/plugin-uridecodebin.xml: + * docs/plugins/inspect/plugin-video4linux.xml: + * docs/plugins/inspect/plugin-videorate.xml: + * docs/plugins/inspect/plugin-videoscale.xml: + * docs/plugins/inspect/plugin-videotestsrc.xml: + * docs/plugins/inspect/plugin-volume.xml: + * docs/plugins/inspect/plugin-vorbis.xml: + * docs/plugins/inspect/plugin-ximagesink.xml: + * docs/plugins/inspect/plugin-xvimagesink.xml: + * gst-plugins-base.doap: + * win32/common/_stdint.h: + * win32/common/config.h: + Release 0.10.31 2010-11-24 17:34:21 +0200 Stefan Kost diff --git a/NEWS b/NEWS index 491aae962a..acd3e61e97 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,131 @@ -This is GStreamer Base Plug-ins 0.10.31, "Dance Like It's 1982" +This is GStreamer Base Plug-ins 0.10.32, "Your Life You Like It Well" + +Changes since 0.10.31: + + * GLib requirement is now >= 2.22, gobject-introspection >= 0.9.12 + * New encodebin element + * New encoding profile and encoding targets API in pbutils + * audioresample: corrected buffer duration calculation to account for nonzero initial timestamp + * audioresample: provide as much valid output ts and offset as valid input + * audioresample: push half a history length, instead of a full history length, at end-of-stream + so that output segment and input segment have same duration + * decodebin2: deprecate new-decoded-pad and removed-decoded-pad signals (use "pad-added" and "pad-removed" instead) + * multifdsink: add first and last buffer's timestamp to the stats; only keep last valid timestamp + * oggdemux: extract more tags (vorbis comment headers, Kate) + * oggdemux: ignore header pages when looking for keyframe; set headers on caps + * oggdemux: fix interpretation of Theora granule position and parsing of Theora size + * oggparse: Set DELTA_UNIT on buffers + * playbin2: delay stream-changed messages, fixing KATE subtitle recognition + * textoverlay: make text, xpos, ypos, color, and silent properties controllable + * typefinding: (E)AC-3 and ISO typefinder improvements; add yuv4mpeg typefinder + * typefinding: add "stream-format" to h264 caps, and framed=false to DTS caps + * typefinding: assume EBML files without doctype are matroska + * videorate: fix behaviour for frame rate cap changes + * vorbisdec: avoid using invalid timestamps; keep timestamps when no decoded output + * ximagesink, xvimagesink: add read-only window-width and window-height properties + * baseaudiopay: fix timestamps on buffer lists + * baseaudiosink: protect against ringbuffer disappearing while in a query + * basedepay: add support for buffer lists in the depayloader + * basertppay: use RTP base time when invalid timestamps + * rtpbuffer: relax arrangement for RTP bufferlists + * rtpdepayloader: add support for getting events + * rtppayload: copy applied rate to segment + * sdp: add method to check for multicast addresses + * sdp: only parse TTL for IP4 addresses + * video: add 8-bit paletted RGB, YUV9, YVU9 and IYU1 video formats + * video: return correct component width/height for A420 + +Bugs fixed since 0.10.31: + + * 619778 : oggdemux: fails on zero-length pages with Patent_Absurdity_HD_3540kbit.ogv + * 586570 : Add GAP Flag support to audioresample + * 623413 : pbutils: Add/Fix some media descriptions + * 627476 : New profile library and encoding plugin + * 629349 : [oggdemux] extract stream tags for tagreadbin and transcoding + * 632667 : [ximagesink] added read-only properties window-width and window-height + * 634397 : [multifdsink] [PATCH] Add the timestamp of the first and last buffer to the stats + * 634522 : gst-visualize-m.m imports but doesn't use File::Basename + * 635231 : baseaudiosink: protect against ringbuffer disappearing while in a query + * 636198 : decodebin2: " removed-decoded-pad " signal never fired + * 636769 : [appsink] Flushing property is never reset + * 636827 : Usage of gst_caps_interset where gst_caps_can_intersect was intended? + * 637324 : oggdemux: unable to demux Ogg files with Skeleton in push mode + * 637377 : timeoverlay: add missing break + * 637519 : ogg: implement packet duration query for kate streams + * 637586 : playbin2 fails to recognize subtitle caps from katedec + * 637735 : [encoding-profile] automatic load/save support and registry + * 637758 : [exiftag] Generates buffers with uninitialized data during taglist- > exif buffer serialization + * 637822 : oggdemux: allocate buffers using gst_buffer_new_and_alloc + * 637927 : oggdemux: set headers on caps + * 638200 : [oggdemux] fails to playback video file + * 638276 : oggstream: when the last keyframe position is not known, do not use -1 + * 638859 : textoverlay: make misc. properties controllable + * 638901 : [encodebin] proper element documentation + * 638903 : [encodebin] missing-plugin support + * 638961 : Small configure bashism 0.10.31.2 + * 639039 : gobject-introspection: GstPbutils gir scanner fails to link with gold linker + * 639121 : oggdemux: outdated comment for gst_ogg_demux_submit_buffer() + * 639215 : examples: Allow building with newer GTK+ + * 639730 : discoverer: Validate timeouts before processing them + * 639755 : discoverer: Clean up callbacks in dispose() + * 639778 : discoverer: Drop new stream tags once preroll is done + * 639790 : [gdp] Fix metadata g_warning + * 639747 : Please export GST_TYPE_APP_STREAM_TYPE + * 553244 : theoraparse doesn't work at all (throws criticals and asserts) + + +API added since 0.10.31: + + * gst_app_stream_type_get_type() + * gst_discoverer_info_get_seekable() + * gst_encoding_audio_profile_get_type() + * gst_encoding_audio_profile_new() + * gst_encoding_container_profile_add_profile() + * gst_encoding_container_profile_contains_profile() + * gst_encoding_container_profile_get_profiles() + * gst_encoding_container_profile_get_type() + * gst_encoding_container_profile_new() + * gst_encoding_list_all_targets() + * gst_encoding_list_available_categories() + * gst_encoding_profile_find() + * gst_encoding_profile_get_description() + * gst_encoding_profile_get_format() + * gst_encoding_profile_get_input_caps() + * gst_encoding_profile_get_name() + * gst_encoding_profile_get_presence() + * gst_encoding_profile_get_preset() + * gst_encoding_profile_get_restriction() + * gst_encoding_profile_get_type() + * gst_encoding_profile_get_type_nick() + * gst_encoding_profile_is_equal() + * gst_encoding_profile_set_description() + * gst_encoding_profile_set_format() + * gst_encoding_profile_set_name() + * gst_encoding_profile_set_presence() + * gst_encoding_profile_set_preset() + * gst_encoding_profile_set_restriction() + * gst_encoding_target_add_profile() + * gst_encoding_target_get_category() + * gst_encoding_target_get_description() + * gst_encoding_target_get_name() + * gst_encoding_target_get_profile() + * gst_encoding_target_get_profiles() + * gst_encoding_target_get_type() + * gst_encoding_target_load() + * gst_encoding_target_load_from_file() + * gst_encoding_target_new() + * gst_encoding_target_save() + * gst_encoding_target_save_to_file() + * gst_encoding_video_profile_get_pass() + * gst_encoding_video_profile_get_type() + * gst_encoding_video_profile_get_variableframerate() + * gst_encoding_video_profile_new() + * gst_encoding_video_profile_set_pass() + * gst_encoding_video_profile_set_variableframerate() + * gst_base_rtp_depayload_push_list() + * gst_rtsp_url_decode_path_components() + * gst_sdp_address_is_multicast() + * gst_video_parse_caps_palette() Changes since 0.10.30: diff --git a/RELEASE b/RELEASE index 3a273c9fd2..70d1baa3a7 100644 --- a/RELEASE +++ b/RELEASE @@ -1,5 +1,5 @@ -Release notes for GStreamer Base Plug-ins 0.10.31 "Dance Like It's 1982" +Release notes for GStreamer Base Plug-ins 0.10.32 "Your Life You Like It Well" @@ -9,8 +9,6 @@ GStreamer Base Plug-ins. The 0.10.x series is a stable series targeted at end users. -It is not API or ABI compatible with the stable 0.8.x series. -It is, however, parallel installable with the 0.8.x series. @@ -27,13 +25,14 @@ This module contains elements for, among others: containers: ogg codecs: vorbis, theora text: textoverlay, subparse - sources: audiotestsrc, videotestsrc, gnomevfssrc, giosrc, appsrc + sources: audiotestsrc, videotestsrc, gnomevfssrc, giosrc network: tcp typefind functions audio processing: audioconvert, adder, audiorate, audioresample, volume visualisation: libvisual video processing: ffmpegcolorspace - aggregate elements: uridecodebin, playbin2, decodebin2, decodebin, playbin + aggregate elements: uridecodebin, playbin2, decodebin2, decodebin, playbin, encodebin + libraries: app, audio, cdda, fft, interfaces, netbuffer, pbutils, riff, rtp, rtsp, sdp, tag, video Other modules containing plug-ins are: @@ -54,263 +53,130 @@ contains a set of less supported plug-ins that haven't passed the Features of this release - * adder: Make sure FLUSH_STOP is always sent after a flushing seek - * alsasrc, alsasink: add "card-name" property to get the card name in addition to the device name - * appsrc: don't override buffer caps if appsrc caps are NULL; fix element classification - * audioclock: add a function to invalidate the clock - * audioconvert: optimise remaining conversion code paths with Orc as well - * baseaudiosink,baseaudiosrc: post clock-provide and clock-lost messages when going from/to READY to/from PAUSED - * baseaudiosink: subtract the render_delay from our latency - * decodebin2: don't add non prerolled stream to topology - * ffmpegcolorspace: add support for A420 and fix support for 8 bit paletted RGB and IYU1 - * gnomevfsrc: set GST_PARAM_MUTABLE_READY flag on the "handle" property - * libvisual: add latency query; only drop frames that are really too old - * multifdsink: gdp protocol is deprecated. People should use gdppay instead - * oggdemux: fix seeking with negative rate with skeleton; fix wrong flowreturn handling - * pbutils: AAC profile and level detection utility functions - * pbutils: H.264 and MPEG-4 profile and level extraction utility functions - * pbutils: new GstDiscoverer utility API for extracting metadata and tags - * playbin2, decodebin2: declare stable, deprecate the old playbin/decodebin - * playbin2, uridecodebin: add property to configure ring buffer size - * rtcpbuffer: add function to manipulation the data in RTCP feedback packets - * rtpbuffer: add functions to add RFC 5285 header extensions to GstBufferLists - * rtpbuffer: add function to add RTP header extensions with a two bytes header - * rtpbuffer: add function to append RFC 5285 one byte header extensions - * rtpbuffer: add function to parse RFC 5285 header extensions - * rtpbuffer: add function to read RFC 5285 header extensions from GstBufferLists - * rtpbuffer: add function to transform a GstBuffer into a GstBufferList - * rtsp: improve rtsp timeout calculation and handling - * sdp: add methods to convert between uri and message - * tags: try ISO-8859-1 as second fallback in case WINDOWS-1252 is not supported - * tags: add many more photography/capture tags - * tags: EXIF and XMP tag handling improvements - * textoverlay: add support for NV12, NV21 and AYUV; configurable text color and position - * theoradec: expose telemetry properties only if libtheora was compiled with --enable-telemetry - * theoraenc: add support for two-pass encoding; allow change of bitrate and quality on-the-fly - * tools: standalone gst-discoverer-0.10 tool for discovering media file properties - * typefinding: detect avc1 ftyp as video/quicktime - * typefinding: export 3gp profile in caps - * typefinding: detect enhanced AC-3 - * typefinding: extend AAC typefinder to detect LOAS streams - * typefinding: fix ADTS caps stream-format detail - * typefinding: more reliable mpeg-ts typefinding - * uridecodebin: Only enable progressive downloading if the upstream duration in bytes is known - * video: add gst_video_convert_frame*() utility functions - * videorate: fixate the pixel-aspect-ratio if necessary - * videorate: mark duplicated frames with the GAP flag - * videoscale: add support for adding black borders to keep the DAR if necessary ("add-borders" property) - * videoscale: Fix caps fixating if the height is fixed but the width isn't - * videoscale: only set the PAR if the caps already had a PAR - * videoscale: refactor using more Orc code - * videotestsrc: new patterns: solid-color, ball, bar and smpte100 - * videotestsrc: add "foreground-color" and "background-color" properties, deprecate "colorspec" property - * videotestsrc: add support for UYVP format, fix NV21 rendering - * volume: use Orc to optimise many code paths - * vorbisdec: decode pending buffers upon EOS when doing reverse playback - * xoverlay: add set_window_handle() with guintptr argument, deprecate set_xwindow_id() which doesn't work on some platforms - * xoverlay: allow render rectangle coordinates to be negative + * GLib requirement is now >= 2.22, gobject-introspection >= 0.9.12 + * New encodebin element + * New encoding profile and encoding targets API in pbutils + * audioresample: corrected buffer duration calculation to account for nonzero initial timestamp + * audioresample: provide as much valid output ts and offset as valid input + * audioresample: push half a history length, instead of a full history length, at end-of-stream so that output segment and input segment have same duration + * decodebin2: deprecate new-decoded-pad and removed-decoded-pad signals (use "pad-added" and "pad-removed" instead) + * multifdsink: add first and last buffer's timestamp to the stats; only keep last valid timestamp + * oggdemux: extract more tags (vorbis comment headers, Kate) + * oggdemux: ignore header pages when looking for keyframe; set headers on caps + * oggdemux: fix interpretation of Theora granule position and parsing of Theora size + * oggparse: Set DELTA_UNIT on buffers + * playbin2: delay stream-changed messages, fixing KATE subtitle recognition + * textoverlay: make text, xpos, ypos, color, and silent properties controllable + * typefinding: (E)AC-3 and ISO typefinder improvements; add yuv4mpeg typefinder + * typefinding: add "stream-format" to h264 caps, and framed=false to DTS caps + * typefinding: assume EBML files without doctype are matroska + * videorate: fix behaviour for frame rate cap changes + * vorbisdec: avoid using invalid timestamps; keep timestamps when no decoded output + * ximagesink, xvimagesink: add read-only window-width and window-height properties + * baseaudiopay: fix timestamps on buffer lists + * baseaudiosink: protect against ringbuffer disappearing while in a query + * basedepay: add support for buffer lists in the depayloader + * basertppay: use RTP base time when invalid timestamps + * rtpbuffer: relax arrangement for RTP bufferlists + * rtpdepayloader: add support for getting events + * rtppayload: copy applied rate to segment + * sdp: add method to check for multicast addresses + * sdp: only parse TTL for IP4 addresses + * video: add 8-bit paletted RGB, YUV9, YVU9 and IYU1 video formats + * video: return correct component width/height for A420 Bugs fixed in this release - * 628028 : [uridecodebin] Don't enable progressive downloading for live streams - * 623846 : typefinding: add support for " enhanced ac3 " (eac3) - * 602437 : [playbin2] [gapless] Completely broken when switching between files with audio/video only - * 612264 : Notification needed when the first buffer is pushed by the basertppayloader - * 615471 : [videoscale] Interlaced handling makes output worse than no interlaced handling at all - * 616392 : videotestsrc colorspec=0/1 does not affect color-matrix in caps - * 617314 : pbutils: Add codec-specific utility functions for AAC, H.264, MPEG-4 video - * 617506 : [videoscale] Add support for adding black borders if necessary to keep the DAR - * 620291 : typefindfunctions: Export 3gp profile in caps - * 623663 : [typefinding] mpeg-ts file detected as audio/mpeg - * 623807 : [audioclock] Add gst_audio_clock_new_full() with GDestroyNotify for the user_data - * 623837 : typefind: only associate .webm with WebM - * 623918 : [typefind] Extend AAC typefinder to detect LOAS stream - * 624598 : [adder] crash in orc_sse_set_mxcsr() - * 624656 : [videoscale] UYVY scaling broken, introduces green lines - * 624919 : [videotestsrc] add solid color pattern - * 624920 : [textoverlay] configurable text color and position - * 624949 : [playbin2] declare playbin2 stable - * 625001 : [examples] Don't use GdkDraw/GdkGC - * 625118 : [playbin2] Race condition with EOS events in gapless mode - * 625944 : [pbutils] GstDiscoverer - API to discover metadata and stream information - * 626125 : [alsa] Conditional jump or move depends on uninitialised value(s) - * 626570 : [tag] Add resolution tags - * 626581 : [playbin2] regression: occasional deadlocks in streamsynchronizer - * 626621 : [playbin2] streamsynchronizer regressions - * 626629 : [ffmpegcolorspace] doesn't handle palettes any longer - * 626718 : playback: Delay usage of GstFactoryList - * 627203 : [alsa] alsasrc and alsasink should expose card name via property - * 627297 : [regression] build-failure - * 627565 : [xoverlay][win64] gulong can't hold a HANDLE - * 627768 : add NV12 support to textoverlay - * 627780 : GstClockOverlay re-renders string even if it hasn't changed, resulting in very high CPU usage. - * 627924 : riff: add support for 2vuy - * 628009 : [volume] Float processing with orc broken - * 628400 : [videorate] does not generate buffers to fill the duration of the last frame - * 628500 : videotestsrc: add moving color bars pattern - * 628747 : gst-plugins-base: unable to build because of compiler warning in libggsttag - * 629157 : Move video frame conversion from playback plugin to libgstvideo - * 629672 : gnomevfsrsrc: " handle " property should also have the GST_PARAM_MUTABLE_READY flag - * 629848 : build problem with current gtk+: implicit declaration of function 'gdk_draw_rectangle', GtkStyle' has no member named 'black_gc' - * 630303 : theoraenc: Make the bitrate/quality dynamically modifiable - * 630353 : [appsrc] Avoid losing buffers' caps - * 630440 : ringbuffer: use g_once for type-init - * 630443 : baseaudiosink: Add getter and setter for drift tolerance - * 630471 : [tag] ligatures " Œ " and " œ " are not supported in freeform strings - * 630496 : seek example: add new #define to set seek bar graininess - * 630802 : videotestsrc.c doesn't compile in Visual Studio 2008 - * 631128 : Add methods to manipulate RFC 5285 header extensions - * 631312 : [streamsynchronizer] Advancing segments too much - * 631633 : [oggdemux] fix seeking with negative rate with skeleton - * 631703 : [oggdemux] sintel ogv delay when playing - * 631756 : Fix build with glib 2.21.3 - * 631773 : [tags] Add new exif tags: sharpness, metering mode, file/capturing source - * 631774 : [xvimagesink] sets non-simple caps on pad_alloced buffer - * 632167 : [oggdemux] doesn't parse/push all headers in pull mode - * 632653 : [seek] Don't use deprecated combo box API - * 632656 : [uridecodebin] internal decodebin2 might fail to reach PLAYING in streaming case - * 632789 : [PATCH] tests/icles/: adapted test-colorkey.c and test-xoverlay.c to deprecation of gtk_widget_hide_all - * 632809 : [regression] build failure in 0.10.30.2 in tools/ - * 632988 : [discoverer] gst_caps_ref() critical for substreams of unknown streams - * 633023 : [discoverer] Add versionized gst-discoverer tool - * 633203 : Regression: streamsynchroniser + disabled deinterlacing = no DVD menu highlights/subtitles - * 633311 : discoverer: use specific types in getters, rename some boolean getters - * 633336 : [discoverer] Move documentation into the correct section - * 633455 : [rtsp] don't let the rtsp connection timeout - * 634014 : GTK+3 is a moving target, lets not compile against it by default. - * 634584 : decodebin2 docs should mention that " new-decoded-pad " signal may be emitted after " no-more-pads " - * 635067 : [*decodebin*] pad template leaked - * 635392 : Missing information on exported packages from GIRs - * 621349 : [theoraenc] Implement two-pass encoding - * 628488 : [theoradec] add properties to enable telemetry overlay - * 629746 : Enumerations have incorrect names of enum values (GEnumValue.value_name) - * 626869 : The RTP depayloader is sometimes sending partial frames down the pipeline without the DISCONT bit set + * 619778 : oggdemux: fails on zero-length pages with Patent_Absurdity_HD_3540kbit.ogv + * 586570 : Add GAP Flag support to audioresample + * 623413 : pbutils: Add/Fix some media descriptions + * 627476 : New profile library and encoding plugin + * 629349 : [oggdemux] extract stream tags for tagreadbin and transcoding + * 632667 : [ximagesink] added read-only properties window-width and window-height + * 634397 : [multifdsink] [PATCH] Add the timestamp of the first and last buffer to the stats + * 634522 : gst-visualize-m.m imports but doesn't use File::Basename + * 635231 : baseaudiosink: protect against ringbuffer disappearing while in a query + * 636198 : decodebin2: " removed-decoded-pad " signal never fired + * 636769 : [appsink] Flushing property is never reset + * 636827 : Usage of gst_caps_interset where gst_caps_can_intersect was intended? + * 637324 : oggdemux: unable to demux Ogg files with Skeleton in push mode + * 637377 : timeoverlay: add missing break + * 637519 : ogg: implement packet duration query for kate streams + * 637586 : playbin2 fails to recognize subtitle caps from katedec + * 637735 : [encoding-profile] automatic load/save support and registry + * 637758 : [exiftag] Generates buffers with uninitialized data during taglist- > exif buffer serialization + * 637822 : oggdemux: allocate buffers using gst_buffer_new_and_alloc + * 637927 : oggdemux: set headers on caps + * 638200 : [oggdemux] fails to playback video file + * 638276 : oggstream: when the last keyframe position is not known, do not use -1 + * 638859 : textoverlay: make misc. properties controllable + * 638901 : [encodebin] proper element documentation + * 638903 : [encodebin] missing-plugin support + * 638961 : Small configure bashism 0.10.31.2 + * 639039 : gobject-introspection: GstPbutils gir scanner fails to link with gold linker + * 639121 : oggdemux: outdated comment for gst_ogg_demux_submit_buffer() + * 639215 : examples: Allow building with newer GTK+ + * 639730 : discoverer: Validate timeouts before processing them + * 639755 : discoverer: Clean up callbacks in dispose() + * 639778 : discoverer: Drop new stream tags once preroll is done + * 639790 : [gdp] Fix metadata g_warning + * 639747 : Please export GST_TYPE_APP_STREAM_TYPE + * 553244 : theoraparse doesn't work at all (throws criticals and asserts) API changed in this release - API additions: - * gst_audio_clock_invalidate() - * gst_audio_clock_new_full() - * gst_base_audio_sink_get_drift_tolerance() - * gst_base_audio_sink_set_drift_tolerance() - * gst_x_overlay_got_window_handle() - * gst_x_overlay_set_window_handle() - * GstXOverlay::set_window_handle() - * gst_codec_utils_aac_caps_set_level_and_profile() - * gst_codec_utils_aac_get_level() - * gst_codec_utils_aac_get_profile() - * gst_codec_utils_aac_get_sample_rate_from_index() - * gst_codec_utils_h264_caps_set_level_and_profile() - * gst_codec_utils_h264_get_level() - * gst_codec_utils_h264_get_profile() - * gst_codec_utils_mpeg4video_caps_set_level_and_profile() - * gst_codec_utils_mpeg4video_get_level() - * gst_codec_utils_mpeg4video_get_profile() - * gst_discoverer_audio_info_get_bitrate() - * gst_discoverer_audio_info_get_channels() - * gst_discoverer_audio_info_get_depth() - * gst_discoverer_audio_info_get_max_bitrate() - * gst_discoverer_audio_info_get_sample_rate() - * gst_discoverer_audio_info_get_type() - * gst_discoverer_container_info_get_streams() - * gst_discoverer_container_info_get_type() - * gst_discoverer_discover_uri() - * gst_discoverer_discover_uri_async() - * gst_discoverer_get_type() - * gst_discoverer_info_copy() - * gst_discoverer_info_get_audio_streams() - * gst_discoverer_info_get_container_streams() - * gst_discoverer_info_get_duration() - * gst_discoverer_info_get_misc() - * gst_discoverer_info_get_result() - * gst_discoverer_info_get_stream_info() - * gst_discoverer_info_get_stream_list() - * gst_discoverer_info_get_streams() - * gst_discoverer_info_get_tags() - * gst_discoverer_info_get_type() - * gst_discoverer_info_get_uri() - * gst_discoverer_info_get_video_streams() - * gst_discoverer_new() - * gst_discoverer_result_get_type() - * gst_discoverer_start() - * gst_discoverer_stop() - * gst_discoverer_stream_info_get_caps() - * gst_discoverer_stream_info_get_misc() - * gst_discoverer_stream_info_get_next() - * gst_discoverer_stream_info_get_previous() - * gst_discoverer_stream_info_get_stream_type_nick() - * gst_discoverer_stream_info_get_tags() - * gst_discoverer_stream_info_get_type() - * gst_discoverer_stream_info_list_free() - * gst_discoverer_video_info_get_bitrate() - * gst_discoverer_video_info_get_depth() - * gst_discoverer_video_info_get_framerate_denom() - * gst_discoverer_video_info_get_framerate_num() - * gst_discoverer_video_info_get_height() - * gst_discoverer_video_info_get_max_bitrate() - * gst_discoverer_video_info_get_par_denom() - * gst_discoverer_video_info_get_par_num() - * gst_discoverer_video_info_get_type() - * gst_discoverer_video_info_get_width() - * gst_discoverer_video_info_is_image() - * gst_discoverer_video_info_is_interlaced() - * GST_PLUGINS_BASE_VERSION_MAJOR - * GST_PLUGINS_BASE_VERSION_MINOR - * GST_PLUGINS_BASE_VERSION_MICRO - * GST_PLUGINS_BASE_VERSION_NANO - * GST_CHECK_PLUGINS_BASE_VERSION - * gst_plugins_base_version() - * gst_plugins_base_version_string() - * gst_rtcp_packet_fb_get_fci() - * gst_rtcp_packet_fb_get_fci_length() - * gst_rtcp_packet_fb_set_fci_length() - * gst_rtp_buffer_add_extension_onebyte_header() - * gst_rtp_buffer_add_extension_twobytes_header() - * gst_rtp_buffer_get_extension_onebyte_header() - * gst_rtp_buffer_get_extension_twobytes_header() - * gst_rtp_buffer_list_add_extension_onebyte_header() - * gst_rtp_buffer_list_add_extension_twobytes_header() - * gst_rtp_buffer_list_from_buffer() - * gst_rtp_buffer_list_get_extension_onebyte_header() - * gst_rtp_buffer_list_get_extension_twobytes_header() - * gst_sdp_message_as_uri() - * gst_sdp_message_parse_uri() - * GST_TAG_CAPTURING_SOURCE - * GST_TAG_CAPTURING_METERING_MODE - * GST_TAG_CAPTURING_SHARPNESS - * GST_TAG_IMAGE_HORIZONTAL_PPI - * GST_TAG_IMAGE_VERTICAL_PPI - * GST_TAG_CAPTURING_FLASH_FIRED - * GST_TAG_CAPTURING_FLASH_MODE - * GST_TAG_CAPTURING_EXPOSURE_PROGRAM - * GST_TAG_CAPTURING_EXPOSURE_MODE - * GST_TAG_CAPTURING_SCENE_CAPTURE_TYPE - * GST_TAG_CAPTURING_GAIN_ADJUSTMENT - * GST_TAG_CAPTURING_WHITE_BALANCE - * GST_TAG_CAPTURING_CONTRAST - * GST_TAG_CAPTURING_SATURATION - * GST_TAG_CAPTURING_SHUTTER_SPEED - * GST_TAG_CAPTURING_FOCAL_RATIO - * GST_TAG_CAPTURING_FOCAL_LENGTH - * GST_TAG_CAPTURING_DIGITAL_ZOOM_RATIO - * GST_TAG_CAPTURING_ISO_SPEED - * GST_VIDEO_FORMAT_UYVP - * GST_VIDEO_FORMAT_A420 - * gst_video_convert_frame() - * gst_video_convert_frame_async() - * GstTextOverlay:xpos - * GstTextOverlay:ypos - * GstTextOverlay:color - * GstVideoTestSrc:solid-color - * GstVideoTestSrc::foreground-color - * GstVideoTestSrc::background-color - -- API deprecations: - - * gst_x_overlay_set_xwindow_id() - * gst_x_overlay_got_xwindow_id() - * GstXOverlay::set_xwindow_id() + * gst_app_stream_type_get_type() + * gst_discoverer_info_get_seekable() + * gst_encoding_audio_profile_get_type() + * gst_encoding_audio_profile_new() + * gst_encoding_container_profile_add_profile() + * gst_encoding_container_profile_contains_profile() + * gst_encoding_container_profile_get_profiles() + * gst_encoding_container_profile_get_type() + * gst_encoding_container_profile_new() + * gst_encoding_list_all_targets() + * gst_encoding_list_available_categories() + * gst_encoding_profile_find() + * gst_encoding_profile_get_description() + * gst_encoding_profile_get_format() + * gst_encoding_profile_get_input_caps() + * gst_encoding_profile_get_name() + * gst_encoding_profile_get_presence() + * gst_encoding_profile_get_preset() + * gst_encoding_profile_get_restriction() + * gst_encoding_profile_get_type() + * gst_encoding_profile_get_type_nick() + * gst_encoding_profile_is_equal() + * gst_encoding_profile_set_description() + * gst_encoding_profile_set_format() + * gst_encoding_profile_set_name() + * gst_encoding_profile_set_presence() + * gst_encoding_profile_set_preset() + * gst_encoding_profile_set_restriction() + * gst_encoding_target_add_profile() + * gst_encoding_target_get_category() + * gst_encoding_target_get_description() + * gst_encoding_target_get_name() + * gst_encoding_target_get_profile() + * gst_encoding_target_get_profiles() + * gst_encoding_target_get_type() + * gst_encoding_target_load() + * gst_encoding_target_load_from_file() + * gst_encoding_target_new() + * gst_encoding_target_save() + * gst_encoding_target_save_to_file() + * gst_encoding_video_profile_get_pass() + * gst_encoding_video_profile_get_type() + * gst_encoding_video_profile_get_variableframerate() + * gst_encoding_video_profile_new() + * gst_encoding_video_profile_set_pass() + * gst_encoding_video_profile_set_variableframerate() + * gst_base_rtp_depayload_push_list() + * gst_rtsp_url_decode_path_components() + * gst_sdp_address_is_multicast() + * gst_video_parse_caps_palette() Download @@ -340,25 +206,23 @@ Applications Contributors to this release * Alessandro Decina - * Alexey Fisher - * American Dynamics - * Andrzej K. Haczewski + * Andoni Morales Alastruey + * Andrea Sebastianutti + * Andy Wingo * Arun Raghavan - * Chris Shoemaker + * Bastien Nocera + * Benjamin Gaignard + * Byeong-ryeol Kim * David Schleef * Edward Hervey - * Evan Nemerson - * Guillaume Emont - * Harri Mähönen + * Evan Broder + * Gavin Stark * Havard Graff - * Jan Schmidt + * Koop Mast + * Lane Brooks * Leo Singer * Mark Nauwelaerts - * Matthias Clasen - * Olivier Crête - * Parthasarathi Susarla - * Philip Jägenstedt - * Philippe Normand + * Michael Smith * René Stadler * Rob Clark * Robert Swain @@ -366,11 +230,10 @@ Contributors to this release * Sreerenj Balachandran * Stefan Kost * Thiago Santos - * Thijs Vermeir * Tim-Philipp Müller - * Tristan Matthews * Vincent Penquerc'h - * Vladimir * Wim Taymans - * Zaheer Abbas Merali -  + * Yang Xichuan + * Zeeshan Ali (Khattak) + * christian schaller +  \ No newline at end of file diff --git a/common b/common index 011bcc8a0f..1de7f6ab2d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 011bcc8a0fc7f798ee874a7ba899123fb2470e22 +Subproject commit 1de7f6ab2d4bc1af69f06079cf0f4e2cbbfdc178 diff --git a/configure.ac b/configure.ac index 71d0349ed2..f33b9793c0 100644 --- a/configure.ac +++ b/configure.ac @@ -49,7 +49,7 @@ dnl - interfaces added/removed/changed -> increment CURRENT, REVISION = 0 dnl - interfaces added -> increment AGE dnl - interfaces removed -> AGE = 0 dnl sets GST_LT_LDFLAGS -AS_LIBTOOL(GST, 22, 0, 22) +AS_LIBTOOL(GST, 23, 0, 23) dnl FIXME: this macro doesn't actually work; dnl the generated libtool script has no support for the listed tags. @@ -151,7 +151,7 @@ AC_PATH_PROG(VALGRIND_PATH, valgrind, no) AM_CONDITIONAL(HAVE_VALGRIND, test ! "x$VALGRIND_PATH" = "xno") dnl check for gobject-introspection -GOBJECT_INTROSPECTION_CHECK([0.6.3]) +GOBJECT_INTROSPECTION_CHECK([0.9.12]) dnl check for documentation tools GTK_DOC_CHECK([1.3]) @@ -337,6 +337,22 @@ AC_COMPILE_IFELSE( ]) AM_CONDITIONAL(HAVE_LINUX_JOYSTICK_HEADERS, test "x$HAVE_LINUX_JOYSTICK_HEADERS" = "xyes") +dnl Check for -Bsymbolic-functions linker flag used to avoid +dnl intra-library PLT jumps, if available. +AC_ARG_ENABLE(Bsymbolic, + [AC_HELP_STRING([--disable-Bsymbolic], + [avoid linking with -Bsymbolic])],, + [SAVED_LDFLAGS="${LDFLAGS}" + AC_MSG_CHECKING([for -Bsymbolic-functions linker flag]) + LDFLAGS=-Wl,-Bsymbolic-functions + AC_TRY_LINK([], [int main (void) { return 0; }], + AC_MSG_RESULT(yes) + enable_Bsymbolic=yes, + AC_MSG_RESULT(no) + enable_Bsymbolic=no) + LDFLAGS="${SAVED_LDFLAGS}"]) + + dnl *** set variables based on configure arguments *** dnl set license and copyright notice @@ -373,6 +389,11 @@ AG_GST_SET_LEVEL_DEFAULT($GST_GIT) dnl used in examples AG_GST_DEFAULT_ELEMENTS +dnl needed for encoding-target +GST_DATADIR="$GST_PREFIX/share" +AC_DEFINE_UNQUOTED(GST_DATADIR, "$GST_DATADIR", [system wide data directory]) +AC_DEFINE_UNQUOTED(GST_MAJORMINOR, "$GST_MAJORMINOR", [major/minor version]) + dnl behaviour of speex based audio resampler AC_MSG_CHECKING(which audio resample format to use for integer) AC_ARG_WITH([audioresample_format], @@ -401,6 +422,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) @@ -870,6 +892,9 @@ AC_SUBST(GST_LIBS) dnl LDFLAGS really should only contain flags, not libs - they get added before dnl whatevertarget_LIBS and -L flags here affect the rest of the linking GST_ALL_LDFLAGS="-no-undefined" +if test "x${enable_Bsymbolic}" = "xyes"; then + GST_ALL_LDFLAGS="$GST_ALL_LDFLAGS -Wl,-Bsymbolic-functions" +fi AC_SUBST(GST_ALL_LDFLAGS) dnl GST_LIB_LDFLAGS @@ -895,6 +920,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 @@ -974,6 +1000,7 @@ tests/check/Makefile tests/examples/Makefile tests/examples/app/Makefile tests/examples/dynamic/Makefile +tests/examples/encoding/Makefile tests/examples/gio/Makefile tests/examples/overlay/Makefile tests/examples/seek/Makefile @@ -1034,7 +1061,7 @@ sed \ -e 's/.* PLUGINDIR$/#ifdef _DEBUG\n# define PLUGINDIR PREFIX "\\\\debug\\\\lib\\\\gstreamer-0.11"\n#else\n# define PLUGINDIR PREFIX "\\\\lib\\\\gstreamer-0.11"\n#endif/' \ -e 's/.* USE_BINARY_REGISTRY$/#define USE_BINARY_REGISTRY/' \ -e 's/.* VERSION$/#define VERSION "'$VERSION'"/' \ - -e "s/.* DEFAULT_AUDIOSINK$/#define DEFAULT_AUDIOSINK \"directaudiosink\"/" \ + -e "s/.* DEFAULT_AUDIOSINK$/#define DEFAULT_AUDIOSINK \"directsoundsink\"/" \ -e "s/.* DEFAULT_VIDEOSINK$/#define DEFAULT_VIDEOSINK \"directdrawsink\"/" \ -e "s/.* DEFAULT_VISUALIZER$/#define DEFAULT_VISUALIZER \"goom\"/" \ config.h.in >win32/common/config.h-new diff --git a/docs/Makefile.am b/docs/Makefile.am index 7ae8b996aa..b0d55f4298 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -12,7 +12,6 @@ SUBDIRS = design libs $(PLUGIN_DOCS_DIRS) DIST_SUBDIRS = design libs plugins EXTRA_DIST = \ - design-audiosinks.txt \ version.entities.in upload: diff --git a/docs/design/Makefile.am b/docs/design/Makefile.am index 8bb52366c1..357af4ce99 100644 --- a/docs/design/Makefile.am +++ b/docs/design/Makefile.am @@ -2,7 +2,9 @@ SUBDIRS = EXTRA_DIST = \ + design-audiosinks.txt \ design-decodebin.txt \ + design-encoding.txt \ design-orc-integration.txt \ draft-keyframe-force.txt \ draft-va.txt \ diff --git a/docs/design-audiosinks.txt b/docs/design/design-audiosinks.txt similarity index 100% rename from docs/design-audiosinks.txt rename to docs/design/design-audiosinks.txt diff --git a/docs/design/design-encoding.txt b/docs/design/design-encoding.txt new file mode 100644 index 0000000000..dda79addf6 --- /dev/null +++ b/docs/design/design-encoding.txt @@ -0,0 +1,571 @@ +Encoding and Muxing +------------------- + +Summary +------- + A. Problems + B. Goals + 1. EncodeBin + 2. Encoding Profile System + 3. Helper Library for Profiles + I. Use-cases researched + + +A. Problems this proposal attempts to solve +------------------------------------------- + +* Duplication of pipeline code for gstreamer-based applications + wishing to encode and or mux streams, leading to subtle differences + and inconsistencies accross those applications. + +* No unified system for describing encoding targets for applications + in a user-friendly way. + +* No unified system for creating encoding targets for applications, + resulting in duplication of code accross all applications, + differences and inconsistencies that come with that duplication, + and applications hardcoding element names and settings resulting in + poor portability. + + + +B. Goals +-------- + +1. Convenience encoding element + + Create a convenience GstBin for encoding and muxing several streams, + hereafter called 'EncodeBin'. + + This element will only contain one single property, which is a + profile. + +2. Define a encoding profile system + +2. Encoding profile helper library + + Create a helper library to: + * create EncodeBin instances based on profiles, and + * help applications to create/load/save/browse those profiles. + + + + +1. EncodeBin +------------ + +1.1 Proposed API +---------------- + + EncodeBin is a GstBin subclass. + + It implements the GstTagSetter interface, by which it will proxy the + calls to the muxer. + + Only two introspectable property (i.e. usable without extra API): + * A GstEncodingProfile* + * The name of the profile to use + + When a profile is selected, encodebin will: + * Add REQUEST sinkpads for all the GstStreamProfile + * Create the muxer and expose the source pad + + Whenever a request pad is created, encodebin will: + * Create the chain of elements for that pad + * Ghost the sink pad + * Return that ghost pad + + This allows reducing the code to the minimum for applications + wishing to encode a source for a given profile: + + ... + + encbin = gst_element_factory_make("encodebin, NULL); + g_object_set (encbin, "profile", "N900/H264 HQ", NULL); + gst_element_link (encbin, filesink); + + ... + + vsrcpad = gst_element_get_src_pad(source, "src1"); + vsinkpad = gst_element_get_request_pad (encbin, "video_%d"); + gst_pad_link(vsrcpad, vsinkpad); + + ... + + +1.2 Explanation of the Various stages in EncodeBin +-------------------------------------------------- + + This describes the various stages which can happen in order to end + up with a multiplexed stream that can then be stored or streamed. + +1.2.1 Incoming streams + + The streams fed to EncodeBin can be of various types: + + * Video + * Uncompressed (but maybe subsampled) + * Compressed + * Audio + * Uncompressed (audio/x-raw-{int|float}) + * Compressed + * Timed text + * Private streams + + +1.2.2 Steps involved for raw video encoding + +(0) Incoming Stream + +(1) Transform raw video feed (optional) + + Here we modify the various fundamental properties of a raw video + stream to be compatible with the intersection of: + * The encoder GstCaps and + * The specified "Stream Restriction" of the profile/target + + The fundamental properties that can be modified are: + * width/height + This is done with a video scaler. + The DAR (Display Aspect Ratio) MUST be respected. + If needed, black borders can be added to comply with the target DAR. + * framerate + * format/colorspace/depth + All of this is done with a colorspace converter + +(2) Actual encoding (optional for raw streams) + + An encoder (with some optional settings) is used. + +(3) Muxing + + A muxer (with some optional settings) is used. + +(4) Outgoing encoded and muxed stream + + +1.2.3 Steps involved for raw audio encoding + + This is roughly the same as for raw video, expect for (1) + +(1) Transform raw audo feed (optional) + + We modify the various fundamental properties of a raw audio stream to + be compatible with the intersection of: + * The encoder GstCaps and + * The specified "Stream Restriction" of the profile/target + + The fundamental properties that can be modifier are: + * Number of channels + * Type of raw audio (integer or floating point) + * Depth (number of bits required to encode one sample) + + +1.2.4 Steps involved for encoded audio/video streams + + Steps (1) and (2) are replaced by a parser if a parser is available + for the given format. + + +1.2.5 Steps involved for other streams + + Other streams will just be forwarded as-is to the muxer, provided the + muxer accepts the stream type. + + + + +2. Encoding Profile System +-------------------------- + + This work is based on: + * The existing GstPreset system for elements [0] + * The gnome-media GConf audio profile system [1] + * The investigation done into device profiles by Arista and + Transmageddon [2 and 3] + +2.2 Terminology +--------------- + +* Encoding Target Category + A Target Category is a classification of devices/systems/use-cases + for encoding. + + Such a classification is required in order for: + * Applications with a very-specific use-case to limit the number of + profiles they can offer the user. A screencasting application has + no use with the online services targets for example. + * Offering the user some initial classification in the case of a + more generic encoding application (like a video editor or a + transcoder). + + Ex: + Consumer devices + Online service + Intermediate Editing Format + Screencast + Capture + Computer + +* Encoding Profile Target + A Profile Target describes a specific entity for which we wish to + encode. + A Profile Target must belong to at least one Target Category. + It will define at least one Encoding Profile. + + Ex (with category): + Nokia N900 (Consumer device) + Sony PlayStation 3 (Consumer device) + Youtube (Online service) + DNxHD (Intermediate editing format) + HuffYUV (Screencast) + Theora (Computer) + +* Encoding Profile + A specific combination of muxer, encoders, presets and limitations. + + Ex: + Nokia N900/H264 HQ + Ipod/High Quality + DVD/Pal + Youtube/High Quality + HTML5/Low Bandwith + DNxHD + +2.3 Encoding Profile +-------------------- + +An encoding profile requires the following information: + + * Name + This string is not translatable and must be unique. + A recommendation to guarantee uniqueness of the naming could be: + / + * Description + This is a translatable string describing the profile + * Muxing format + This is a string containing the GStreamer media-type of the + container format. + * Muxing preset + This is an optional string describing the preset(s) to use on the + muxer. + * Multipass setting + This is a boolean describing whether the profile requires several + passes. + * List of Stream Profile + +2.3.1 Stream Profiles + +A Stream Profile consists of: + + * Type + The type of stream profile (audio, video, text, private-data) + * Encoding Format + This is a string containing the GStreamer media-type of the encoding + format to be used. If encoding is not to be applied, the raw audio + media type will be used. + * Encoding preset + This is an optional string describing the preset(s) to use on the + encoder. + * Restriction + This is an optional GstCaps containing the restriction of the + stream that can be fed to the encoder. + This will generally containing restrictions in video + width/heigh/framerate or audio depth. + * presence + This is an integer specifying how many streams can be used in the + containing profile. 0 means that any number of streams can be + used. + * pass + This is an integer which is only meaningful if the multipass flag + has been set in the profile. If it has been set it indicates which + pass this Stream Profile corresponds to. + +2.4 Example profile +------------------- + +The representation used here is XML only as an example. No decision is +made as to which formatting to use for storing targets and profiles. + + + Nokia N900 + Consumer Device + + Nokia N900/H264 HQ + Nokia N900/MP3 + Nokia N900/AAC + + + + + Nokia N900/H264 HQ + + High Quality H264/AAC for the Nokia N900 + + video/quicktime,variant=iso + + + audio + audio/mpeg,mpegversion=4 + Quality High/Main + audio/x-raw-int,channels=[1,2] + 1 + + + video + video/x-h264 + Profile Baseline/Quality High + + video/x-raw-yuv,width=[16, 800],\ + height=[16, 480],framerate=[1/1, 30000/1001] + + 1 + + + + + +2.5 API +------- + A proposed C API is contained in the gstprofile.h file in this directory. + + +2.6 Modifications required in the existing GstPreset system +----------------------------------------------------------- + +2.6.1. Temporary preset. + + Currently a preset needs to be saved on disk in order to be + used. + + This makes it impossible to have temporary presets (that exist only + during the lifetime of a process), which might be required in the + new proposed profile system + +2.6.2 Categorisation of presets. + + Currently presets are just aliases of a group of property/value + without any meanings or explanation as to how they exclude each + other. + + Take for example the H264 encoder. It can have presets for: + * passes (1,2 or 3 passes) + * profiles (Baseline, Main, ...) + * quality (Low, medium, High) + + In order to programmatically know which presets exclude each other, + we here propose the categorisation of these presets. + + This can be done in one of two ways + 1. in the name (by making the name be [:]) + This would give for example: "Quality:High", "Profile:Baseline" + 2. by adding a new _meta key + This would give for example: _meta/category:quality + +2.6.3 Aggregation of presets. + + There can be more than one choice of presets to be done for an + element (quality, profile, pass). + + This means that one can not currently describe the full + configuration of an element with a single string but with many. + + The proposal here is to extend the GstPreset API to be able to set + all presets using one string and a well-known separator ('/'). + + This change only requires changes in the core preset handling code. + + This would allow doing the following: + gst_preset_load_preset (h264enc, + "pass:1/profile:baseline/quality:high"); + +2.7 Points to be determined +--------------------------- + + This document hasn't determined yet how to solve the following + problems: + +2.7.1 Storage of profiles + + One proposal for storage would be to use a system wide directory + (like $prefix/share/gstreamer-0.10/profiles) and store XML files for + every individual profiles. + + Users could then add their own profiles in ~/.gstreamer-0.10/profiles + + This poses some limitations as to what to do if some applications + want to have some profiles limited to their own usage. + + +3. Helper library for profiles +------------------------------ + + These helper methods could also be added to existing libraries (like + GstPreset, GstPbUtils, ..). + + The various API proposed are in the accompanying gstprofile.h file. + +3.1 Getting user-readable names for formats + + This is already provided by GstPbUtils. + +3.2 Hierarchy of profiles + + The goal is for applications to be able to present to the user a list + of combo-boxes for choosing their output profile: + + [ Category ] # optional, depends on the application + [ Device/Site/.. ] # optional, depends on the application + [ Profile ] + + Convenience methods are offered to easily get lists of categories, + devices, and profiles. + +3.3 Creating Profiles + + The goal is for applications to be able to easily create profiles. + + The applications needs to be able to have a fast/efficient way to: + * select a container format and see all compatible streams he can use + with it. + * select a codec format and see which container formats he can use + with it. + + The remaining parts concern the restrictions to encoder + input. + +3.4 Ensuring availability of plugins for Profiles + + When an application wishes to use a Profile, it should be able to + query whether it has all the needed plugins to use it. + + This part will use GstPbUtils to query, and if needed install the + missing plugins through the installed distribution plugin installer. + + +I. Use-cases researched +----------------------- + + This is a list of various use-cases where encoding/muxing is being + used. + +* Transcoding + + The goal is to convert with as minimal loss of quality any input + file for a target use. + A specific variant of this is transmuxing (see below). + + Example applications: Arista, Transmageddon + +* Rendering timelines + + The incoming streams are a collection of various segments that need + to be rendered. + Those segments can vary in nature (i.e. the video width/height can + change). + This requires the use of identiy with the single-segment property + activated to transform the incoming collection of segments to a + single continuous segment. + + Example applications: PiTiVi, Jokosher + +* Encoding of live sources + + The major risk to take into account is the encoder not encoding the + incoming stream fast enough. This is outside of the scope of + encodebin, and should be solved by using queues between the sources + and encodebin, as well as implementing QoS in encoders and sources + (the encoders emitting QoS events, and the upstream elements + adapting themselves accordingly). + + Example applications: camerabin, cheese + +* Screencasting applications + + This is similar to encoding of live sources. + The difference being that due to the nature of the source (size and + amount/frequency of updates) one might want to do the encoding in + two parts: + * The actual live capture is encoded with a 'almost-lossless' codec + (such as huffyuv) + * Once the capture is done, the file created in the first step is + then rendered to the desired target format. + + Fixing sources to only emit region-updates and having encoders + capable of encoding those streams would fix the need for the first + step but is outside of the scope of encodebin. + + Example applications: Istanbul, gnome-shell, recordmydesktop + +* Live transcoding + + This is the case of an incoming live stream which will be + broadcasted/transmitted live. + One issue to take into account is to reduce the encoding latency to + a minimum. This should mostly be done by picking low-latency + encoders. + + Example applications: Rygel, Coherence + +* Transmuxing + + Given a certain file, the aim is to remux the contents WITHOUT + decoding into either a different container format or the same + container format. + Remuxing into the same container format is useful when the file was + not created properly (for example, the index is missing). + Whenever available, parsers should be applied on the encoded streams + to validate and/or fix the streams before muxing them. + + Metadata from the original file must be kept in the newly created + file. + + Example applications: Arista, Transmaggedon + +* Loss-less cutting + + Given a certain file, the aim is to extract a certain part of the + file without going through the process of decoding and re-encoding + that file. + This is similar to the transmuxing use-case. + + Example applications: PiTiVi, Transmageddon, Arista, ... + +* Multi-pass encoding + + Some encoders allow doing a multi-pass encoding. + The initial pass(es) are only used to collect encoding estimates and + are not actually muxed and outputted. + The final pass uses previously collected information, and the output + is then muxed and outputted. + +* Archiving and intermediary format + + The requirement is to have lossless + +* CD ripping + + Example applications: Sound-juicer + +* DVD ripping + + Example application: Thoggen + + + +* Research links + + Some of these are still active documents, some other not + +[0] GstPreset API documentation + http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstPreset.html + +[1] gnome-media GConf profiles + http://www.gnome.org/~bmsmith/gconf-docs/C/gnome-media.html + +[2] Research on a Device Profile API + http://gstreamer.freedesktop.org/wiki/DeviceProfile + +[3] Research on defining presets usage + http://gstreamer.freedesktop.org/wiki/PresetDesign + diff --git a/docs/libs/gst-plugins-base-libs-docs.sgml b/docs/libs/gst-plugins-base-libs-docs.sgml index 94a34519a3..b7b61a70a7 100644 --- a/docs/libs/gst-plugins-base-libs-docs.sgml +++ b/docs/libs/gst-plugins-base-libs-docs.sgml @@ -206,6 +206,7 @@ + diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index aff1cdb04f..41235a09ef 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -547,6 +547,7 @@ GST_MIXER_GET_CLASS GST_MIXER_TYPE GST_IS_MIXER GST_IS_MIXER_CLASS +GST_TYPE_STREAM_VOLUME_FORMAT gst_mixer_get_type gst_mixer_type_get_type gst_mixer_flags_get_type @@ -1098,6 +1099,7 @@ GST_BASE_RTP_DEPAYLOAD_SRCPAD gst_base_rtp_depayload_push gst_base_rtp_depayload_push_ts +gst_base_rtp_depayload_push_list GstBaseRTPDepayloadPrivate @@ -1573,6 +1575,7 @@ gst_rtsp_url_free gst_rtsp_url_get_request_uri gst_rtsp_url_set_port gst_rtsp_url_get_port +gst_rtsp_url_decode_path_components gst_rtsp_url_get_type @@ -1599,6 +1602,9 @@ GstSDPConnection GST_SDP_BWTYPE_CT GST_SDP_BWTYPE_AS GST_SDP_BWTYPE_EXT_PREFIX +GST_SDP_BWTYPE_RR +GST_SDP_BWTYPE_RS +GST_SDP_BWTYPE_TIAS GstSDPBandwidth GstSDPTime GstSDPZone @@ -1617,6 +1623,8 @@ gst_sdp_message_as_text gst_sdp_message_parse_uri gst_sdp_message_as_uri +gst_sdp_address_is_multicast + gst_sdp_message_get_version gst_sdp_message_set_version gst_sdp_message_get_origin @@ -1907,6 +1915,95 @@ gst_codec_utils_mpeg4video_get_level gst_codec_utils_mpeg4video_caps_set_level_and_profile +
+encoding-profile +gst/pbutils/encoding-profile.h +GstEncodingProfile +gst_encoding_profile_unref +gst_encoding_profile_ref +gst_encoding_profile_find +gst_encoding_profile_get_name +gst_encoding_profile_get_description +gst_encoding_profile_get_format +gst_encoding_profile_get_preset +gst_encoding_profile_get_presence +gst_encoding_profile_get_restriction +gst_encoding_profile_set_name +gst_encoding_profile_set_description +gst_encoding_profile_set_format +gst_encoding_profile_set_preset +gst_encoding_profile_set_restriction +gst_encoding_profile_set_presence +gst_encoding_profile_is_equal +gst_encoding_profile_get_input_caps +gst_encoding_profile_get_type_nick + +GstEncodingContainerProfile +gst_encoding_container_profile_new +gst_encoding_container_profile_add_profile +gst_encoding_container_profile_contains_profile +gst_encoding_container_profile_get_profiles + +GstEncodingAudioProfile +gst_encoding_audio_profile_new + +GstEncodingVideoProfile +gst_encoding_video_profile_new +gst_encoding_video_profile_get_pass +gst_encoding_video_profile_get_variableframerate +gst_encoding_video_profile_set_pass +gst_encoding_video_profile_set_variableframerate + +GST_ENCODING_CATEGORY_DEVICE +GST_ENCODING_CATEGORY_ONLINE_SERVICE +GST_ENCODING_CATEGORY_STORAGE_EDITING +GST_ENCODING_CATEGORY_CAPTURE +GstEncodingTarget +gst_encoding_target_unref +gst_encoding_target_ref +gst_encoding_target_new +gst_encoding_target_get_name +gst_encoding_target_get_category +gst_encoding_target_get_description +gst_encoding_target_get_profiles +gst_encoding_target_get_profile +gst_encoding_target_add_profile +gst_encoding_target_save +gst_encoding_target_save_to_file +gst_encoding_target_load +gst_encoding_target_load_from_file +gst_encoding_list_all_targets +gst_encoding_list_available_categories + +GST_ENCODING_PROFILE +GST_IS_ENCODING_PROFILE +GST_TYPE_ENCODING_PROFILE +gst_encoding_profile_get_type +GST_ENCODING_TARGET +GST_IS_ENCODING_TARGET +GST_TYPE_ENCODING_TARGET +gst_encoding_target_get_type +GstEncodingProfileClass +GST_TYPE_ENCODING_CONTAINER_PROFILE +GST_ENCODING_CONTAINER_PROFILE +gst_encoding_container_profile_get_type +GST_TYPE_ENCODING_VIDEO_PROFILE +GST_ENCODING_VIDEO_PROFILE +GST_IS_ENCODING_VIDEO_PROFILE +GstEncodingVideoProfileClass +gst_encoding_video_profile_get_type +GST_TYPE_ENCODING_AUDIO_PROFILE +GST_ENCODING_AUDIO_PROFILE +GST_IS_ENCODING_AUDIO_PROFILE +GstEncodingAudioProfileClass +gst_encoding_audio_profile_get_type +GST_IS_ENCODING_CONTAINER_PROFILE +GstEncodingContainerProfileClass +GstEncodingTargetClass +
+ + + # video
@@ -1945,6 +2042,9 @@ GST_VIDEO_CAPS_YUV GST_VIDEO_CAPS_xBGR GST_VIDEO_CAPS_xRGB GST_VIDEO_CAPS_xRGB_HOST_ENDIAN +GST_VIDEO_CAPS_BGR_15 +GST_VIDEO_CAPS_BGR_16 +GST_VIDEO_CAPS_RGB8_PALETTED GST_VIDEO_CAPS_GRAY8 GST_VIDEO_CAPS_GRAY16 GST_VIDEO_FPS_RANGE @@ -1985,6 +2085,7 @@ gst_video_parse_caps_framerate gst_video_parse_caps_pixel_aspect_ratio gst_video_parse_caps_color_matrix gst_video_parse_caps_chroma_site +gst_video_parse_caps_palette GstVideoConvertFrameCallback gst_video_convert_frame gst_video_convert_frame_async @@ -1992,6 +2093,7 @@ gst_video_event_new_still_frame gst_video_event_parse_still_frame gst_video_format_get_type +GST_TYPE_VIDEO_FORMAT
@@ -2051,6 +2153,8 @@ gst_discoverer_info_get_stream_info gst_discoverer_info_get_stream_list gst_discoverer_info_get_tags gst_discoverer_info_get_uri +gst_discoverer_info_get_seekable +gst_discoverer_info_ref gst_discoverer_info_unref GstDiscovererStreamInfo @@ -2066,8 +2170,6 @@ gst_discoverer_stream_info_ref gst_discoverer_stream_info_unref gst_discoverer_stream_info_list_free gst_discoverer_stream_info_get_stream_type_nick -gst_discoverer_info_copy -gst_discoverer_info_ref gst_discoverer_info_get_audio_streams gst_discoverer_info_get_container_streams gst_discoverer_info_get_streams @@ -2122,6 +2224,7 @@ gst_discoverer_audio_info_get_type gst_discoverer_container_info_get_type gst_discoverer_get_type gst_discoverer_info_get_type +gst_discoverer_info_copy gst_discoverer_result_get_type gst_discoverer_stream_info_get_type gst_discoverer_video_info_get_type diff --git a/docs/libs/gst-plugins-base-libs.types b/docs/libs/gst-plugins-base-libs.types index db3818a1d3..f6c06af2cd 100644 --- a/docs/libs/gst-plugins-base-libs.types +++ b/docs/libs/gst-plugins-base-libs.types @@ -59,3 +59,14 @@ gst_video_sink_get_type #include gst_discoverer_get_type + +#include +#include +gst_encoding_profile_get_type +gst_encoding_video_profile_get_type +gst_encoding_video_profile_get_type +gst_encoding_audio_profile_get_type +gst_encoding_container_profile_get_type +gst_encoding_target_get_type + + diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 4b784337dd..9beea7e55f 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -50,7 +50,7 @@ MKDB_OPTIONS=--sgml-mode # Extra options to supply to gtkdoc-fixref. FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ --extra-dir=$(GST_PREFIX)/share/gtk-doc/html \ - --extra-dir=$(datadir)/gtk-doc/html + --extra-dir=$(datadir)/gtk-doc/html # Used for dependencies. HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h @@ -102,6 +102,7 @@ EXTRA_HFILES = \ $(top_srcdir)/gst/audioconvert/audioconvert.h \ $(top_srcdir)/gst/audioconvert/gstaudioconvert.h \ $(top_srcdir)/gst/audiotestsrc/gstaudiotestsrc.h \ + $(top_srcdir)/gst/encoding/gstencodebin.h \ $(top_srcdir)/gst/ffmpegcolorspace/gstffmpegcolorspace.h \ $(top_srcdir)/gst/gdp/gstgdpdepay.h \ $(top_srcdir)/gst/gdp/gstgdppay.h \ diff --git a/docs/plugins/gst-plugins-base-plugins-docs.sgml b/docs/plugins/gst-plugins-base-plugins-docs.sgml index 9bcbbf895c..ea603d6012 100644 --- a/docs/plugins/gst-plugins-base-plugins-docs.sgml +++ b/docs/plugins/gst-plugins-base-plugins-docs.sgml @@ -31,6 +31,7 @@ + @@ -80,6 +81,7 @@ + diff --git a/docs/plugins/gst-plugins-base-plugins-sections.txt b/docs/plugins/gst-plugins-base-plugins-sections.txt index e241371f65..e94c450985 100644 --- a/docs/plugins/gst-plugins-base-plugins-sections.txt +++ b/docs/plugins/gst-plugins-base-plugins-sections.txt @@ -247,6 +247,22 @@ GstDecodeBin2
+
+element-encodebin +encodebin +GstEncodeBin + +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 +
+ + +
element-ffmpegcolorspace ffmpegcolorspace diff --git a/docs/plugins/gst-plugins-base-plugins.args b/docs/plugins/gst-plugins-base-plugins.args index 05ef80fc0d..9469513c3a 100644 --- a/docs/plugins/gst-plugins-base-plugins.args +++ b/docs/plugins/gst-plugins-base-plugins.args @@ -158,6 +158,26 @@ TRUE + +GstXvImageSink::window-height +guint64 + +r +window-height +Height of the window. +0 + + + +GstXvImageSink::window-width +guint64 + +r +window-width +Width of the window. +0 + + GstXImageSink::display gchar* @@ -218,6 +238,26 @@ TRUE + +GstXImageSink::window-height +guint64 + +r +window-height +Height of the window. +0 + + + +GstXImageSink::window-width +guint64 + +r +window-width +Width of the window. +0 + + GstV4lSrc::autoprobe gboolean @@ -3215,7 +3255,7 @@ rw Stream Type the type of the stream. -Stream +GST_APP_STREAM_TYPE_STREAM @@ -3378,3 +3418,63 @@ NULL + +GstEncodeBin::audio-jitter-tolerance +guint64 + +rw +Audio jitter tolerance +Amount of timestamp jitter/imperfection to allow on audio streams before inserting/dropping samples (ns). +20000000 + + + +GstEncodeBin::avoid-reencoding +gboolean + +rw +Avoid re-encoding +Whether to re-encode portions of compatible video streams that lay on segment boundaries. +FALSE + + + +GstEncodeBin::profile +GstEncodingProfile* + +rw +Profile +The GstEncodingProfile to use. + + + + +GstEncodeBin::queue-buffers-max +guint + +rw +Max. size (buffers) +Max. number of buffers in the queue (0=disable). +200 + + + +GstEncodeBin::queue-bytes-max +guint + +rw +Max. size (kB) +Max. amount of data in the queue (bytes, 0=disable). +10485760 + + + +GstEncodeBin::queue-time-max +guint64 + +rw +Max. size (ns) +Max. amount of data in the queue (in ns, 0=disable). +1000000000 + + diff --git a/docs/plugins/gst-plugins-base-plugins.hierarchy b/docs/plugins/gst-plugins-base-plugins.hierarchy index 4a355e6596..0ada208196 100644 --- a/docs/plugins/gst-plugins-base-plugins.hierarchy +++ b/docs/plugins/gst-plugins-base-plugins.hierarchy @@ -56,6 +56,7 @@ GObject GstBin GstDecodeBin GstDecodeBin2 + GstEncodeBin GstPipeline GstPlayBaseBin GstPlayBin @@ -73,7 +74,6 @@ GObject GstOgmAudioParse GstOgmTextParse GstOgmVideoParse - GstPlaybin2InputSelector GstSsaParse GstStreamSelector GstSubParse @@ -85,12 +85,20 @@ GObject GstTheoraEnc GstTheoraParse GstVideoRate + GstVisual + GstVisualbumpscope + GstVisualcorona + GstVisualinfinite + GstVisualjakdaw + GstVisualjess + GstVisuallv_analyzer + GstVisuallv_scope + GstVisualoinksie GstVorbisDec GstVorbisEnc GstVorbisParse GstVorbisTag GstPad - GstPlaybin2SelectorPad GstPadTemplate GstPlugin GstPluginFeature diff --git a/docs/plugins/gst-plugins-base-plugins.interfaces b/docs/plugins/gst-plugins-base-plugins.interfaces index 36963b528f..7e1560c365 100644 --- a/docs/plugins/gst-plugins-base-plugins.interfaces +++ b/docs/plugins/gst-plugins-base-plugins.interfaces @@ -3,29 +3,30 @@ GstPipeline GstChildProxy GstPlayBaseBin GstChildProxy GstPlayBin GstChildProxy GstPlayBin2 GstChildProxy GstStreamVolume +GstDecodeBin2 GstChildProxy +GstURIDecodeBin GstChildProxy GstDecodeBin GstChildProxy GstPlaySink GstChildProxy GstSubtitleOverlay GstChildProxy -GstDecodeBin2 GstChildProxy -GstURIDecodeBin GstChildProxy +GstEncodeBin GstChildProxy +GstVorbisEnc GstTagSetter GstPreset +GstVorbisTag GstTagSetter GstCddaBaseSrc GstURIHandler GstCdParanoiaSrc GstURIHandler GstAlsaSrc GstImplementsInterface GstMixer GstPropertyProbe GstV4lElement GstImplementsInterface GstTuner GstXOverlay GstColorBalance GstPropertyProbe GstV4lSrc GstImplementsInterface GstTuner GstXOverlay GstColorBalance GstPropertyProbe -GstGnomeVFSSrc GstURIHandler GstGioSrc GstURIHandler +GstGnomeVFSSrc GstURIHandler GstAppSrc GstURIHandler -GstGnomeVFSSink GstURIHandler -GstAlsaSink GstPropertyProbe GstGioSink GstURIHandler +GstAlsaSink GstPropertyProbe +GstGnomeVFSSink GstURIHandler GstXImageSink GstImplementsInterface GstNavigation GstXOverlay GstXvImageSink GstImplementsInterface GstNavigation GstXOverlay GstColorBalance GstPropertyProbe GstAppSink GstURIHandler -GstVorbisEnc GstTagSetter GstPreset -GstVorbisTag GstTagSetter -GstTheoraEnc GstPreset GstAlsaMixerElement GstImplementsInterface GstMixer GstPropertyProbe +GstTheoraEnc GstPreset GstOggMux GstPreset GstVolume GstImplementsInterface GstMixer GstStreamVolume PangoCairoFcFontMap PangoCairoFontMap diff --git a/docs/plugins/gst-plugins-base-plugins.prerequisites b/docs/plugins/gst-plugins-base-plugins.prerequisites index a6aa7d0abe..48604c0da2 100644 --- a/docs/plugins/gst-plugins-base-plugins.prerequisites +++ b/docs/plugins/gst-plugins-base-plugins.prerequisites @@ -2,9 +2,9 @@ GstChildProxy GstObject GstTagSetter GstElement GstImplementsInterface GstElement GstMixer GstImplementsInterface GstElement +GstTuner GstImplementsInterface GstElement GstXOverlay GstImplementsInterface GstElement GstColorBalance GstImplementsInterface GstElement -GstTuner GstImplementsInterface GstElement GstStreamVolume GObject -GFile GObject PangoCairoFontMap PangoFontMap +GFile GObject diff --git a/docs/plugins/gst-plugins-base-plugins.signals b/docs/plugins/gst-plugins-base-plugins.signals index 2f5fab5ae5..621f880b3f 100644 --- a/docs/plugins/gst-plugins-base-plugins.signals +++ b/docs/plugins/gst-plugins-base-plugins.signals @@ -471,3 +471,11 @@ GstPlaySink *gstplaysink GstCaps *arg1 + +GstEncodeBin::request-pad +GstPad* +la +GstEncodeBin *gstencodebin +GstCaps *arg1 + + diff --git a/docs/plugins/inspect/plugin-adder.xml b/docs/plugins/inspect/plugin-adder.xml index c30794c40a..616e5de3c5 100644 --- a/docs/plugins/inspect/plugin-adder.xml +++ b/docs/plugins/inspect/plugin-adder.xml @@ -3,7 +3,7 @@ Adds multiple streams ../../gst/adder/.libs/libgstadder.so libgstadder.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-alsa.xml b/docs/plugins/inspect/plugin-alsa.xml index 17db83fa40..5cd1f5c6b4 100644 --- a/docs/plugins/inspect/plugin-alsa.xml +++ b/docs/plugins/inspect/plugin-alsa.xml @@ -3,7 +3,7 @@ ALSA plugin library ../../ext/alsa/.libs/libgstalsa.so libgstalsa.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-app.xml b/docs/plugins/inspect/plugin-app.xml index cd3d5aab60..4e63961fd3 100644 --- a/docs/plugins/inspect/plugin-app.xml +++ b/docs/plugins/inspect/plugin-app.xml @@ -3,7 +3,7 @@ Elements used to communicate with applications ../../gst/app/.libs/libgstapp.so libgstapp.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-audioconvert.xml b/docs/plugins/inspect/plugin-audioconvert.xml index 69a5d716a5..215ee94ad1 100644 --- a/docs/plugins/inspect/plugin-audioconvert.xml +++ b/docs/plugins/inspect/plugin-audioconvert.xml @@ -3,7 +3,7 @@ Convert audio to different formats ../../gst/audioconvert/.libs/libgstaudioconvert.so libgstaudioconvert.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-audiorate.xml b/docs/plugins/inspect/plugin-audiorate.xml index 598c2d723f..34ec1315da 100644 --- a/docs/plugins/inspect/plugin-audiorate.xml +++ b/docs/plugins/inspect/plugin-audiorate.xml @@ -3,7 +3,7 @@ Adjusts audio frames ../../gst/audiorate/.libs/libgstaudiorate.so libgstaudiorate.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-audioresample.xml b/docs/plugins/inspect/plugin-audioresample.xml index 91552c331b..9099a3ef22 100644 --- a/docs/plugins/inspect/plugin-audioresample.xml +++ b/docs/plugins/inspect/plugin-audioresample.xml @@ -3,7 +3,7 @@ Resamples audio ../../gst/audioresample/.libs/libgstaudioresample.so libgstaudioresample.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-audiotestsrc.xml b/docs/plugins/inspect/plugin-audiotestsrc.xml index 7d973f04fd..4ec1d419a4 100644 --- a/docs/plugins/inspect/plugin-audiotestsrc.xml +++ b/docs/plugins/inspect/plugin-audiotestsrc.xml @@ -3,7 +3,7 @@ Creates audio test signals of given frequency and volume ../../gst/audiotestsrc/.libs/libgstaudiotestsrc.so libgstaudiotestsrc.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-cdparanoia.xml b/docs/plugins/inspect/plugin-cdparanoia.xml index 708cddc72c..095498dc43 100644 --- a/docs/plugins/inspect/plugin-cdparanoia.xml +++ b/docs/plugins/inspect/plugin-cdparanoia.xml @@ -3,7 +3,7 @@ Read audio from CD in paranoid mode ../../ext/cdparanoia/.libs/libgstcdparanoia.so libgstcdparanoia.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-decodebin.xml b/docs/plugins/inspect/plugin-decodebin.xml index 7b77239847..63292f82d0 100644 --- a/docs/plugins/inspect/plugin-decodebin.xml +++ b/docs/plugins/inspect/plugin-decodebin.xml @@ -3,7 +3,7 @@ decoder bin ../../gst/playback/.libs/libgstdecodebin.so libgstdecodebin.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-encoding.xml b/docs/plugins/inspect/plugin-encoding.xml new file mode 100644 index 0000000000..fa94055f54 --- /dev/null +++ b/docs/plugins/inspect/plugin-encoding.xml @@ -0,0 +1,46 @@ + + encoding + various encoding-related elements + ../../gst/encoding/.libs/libgstencodebin.so + libgstencodebin.so + 0.10.32.1 + LGPL + gst-plugins-base + GStreamer Base Plug-ins git + Unknown package origin + + + encodebin + Encoder Bin + Generic/Bin/Encoder + Convenience encoding/muxing element + Edward Hervey <edward.hervey@collabora.co.uk> + + + audio_%d + sink + request +
ANY
+
+ + private_%d + sink + request +
ANY
+
+ + video_%d + sink + request +
ANY
+
+ + src + source + always +
ANY
+
+
+
+
+
\ No newline at end of file diff --git a/docs/plugins/inspect/plugin-ffmpegcolorspace.xml b/docs/plugins/inspect/plugin-ffmpegcolorspace.xml index 229a21a163..e30774010b 100644 --- a/docs/plugins/inspect/plugin-ffmpegcolorspace.xml +++ b/docs/plugins/inspect/plugin-ffmpegcolorspace.xml @@ -3,7 +3,7 @@ colorspace conversion copied from FFMpeg 0.4.9-pre1 ../../gst/ffmpegcolorspace/.libs/libgstffmpegcolorspace.so libgstffmpegcolorspace.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base FFMpeg diff --git a/docs/plugins/inspect/plugin-gdp.xml b/docs/plugins/inspect/plugin-gdp.xml index 40af444fbe..9c563d88ad 100644 --- a/docs/plugins/inspect/plugin-gdp.xml +++ b/docs/plugins/inspect/plugin-gdp.xml @@ -3,7 +3,7 @@ Payload/depayload GDP packets ../../gst/gdp/.libs/libgstgdp.so libgstgdp.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-gio.xml b/docs/plugins/inspect/plugin-gio.xml index 578df73a75..82dde14b8e 100644 --- a/docs/plugins/inspect/plugin-gio.xml +++ b/docs/plugins/inspect/plugin-gio.xml @@ -3,7 +3,7 @@ GIO elements ../../ext/gio/.libs/libgstgio.so libgstgio.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-gnomevfs.xml b/docs/plugins/inspect/plugin-gnomevfs.xml index bbc828b085..44a4a68cab 100644 --- a/docs/plugins/inspect/plugin-gnomevfs.xml +++ b/docs/plugins/inspect/plugin-gnomevfs.xml @@ -3,7 +3,7 @@ elements to read from and write to Gnome-VFS uri's ../../ext/gnomevfs/.libs/libgstgnomevfs.so libgstgnomevfs.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-libvisual.xml b/docs/plugins/inspect/plugin-libvisual.xml index 22f4e8f048..2f20d8b1b4 100644 --- a/docs/plugins/inspect/plugin-libvisual.xml +++ b/docs/plugins/inspect/plugin-libvisual.xml @@ -3,7 +3,7 @@ libvisual visualization plugins ../../ext/libvisual/.libs/libgstlibvisual.so libgstlibvisual.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-ogg.xml b/docs/plugins/inspect/plugin-ogg.xml index 33477006ab..a1d0462e60 100644 --- a/docs/plugins/inspect/plugin-ogg.xml +++ b/docs/plugins/inspect/plugin-ogg.xml @@ -3,7 +3,7 @@ ogg stream manipulation (info about ogg: http://xiph.org) ../../ext/ogg/.libs/libgstogg.so libgstogg.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-pango.xml b/docs/plugins/inspect/plugin-pango.xml index 411e39263e..aad3c8c3b7 100644 --- a/docs/plugins/inspect/plugin-pango.xml +++ b/docs/plugins/inspect/plugin-pango.xml @@ -3,7 +3,7 @@ Pango-based text rendering and overlay ../../ext/pango/.libs/libgstpango.so libgstpango.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-playback.xml b/docs/plugins/inspect/plugin-playback.xml index 55a711e3ec..19fe577faa 100644 --- a/docs/plugins/inspect/plugin-playback.xml +++ b/docs/plugins/inspect/plugin-playback.xml @@ -3,7 +3,7 @@ various playback elements ../../gst/playback/.libs/libgstplaybin.so libgstplaybin.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-subparse.xml b/docs/plugins/inspect/plugin-subparse.xml index fec053023f..69c61511cc 100644 --- a/docs/plugins/inspect/plugin-subparse.xml +++ b/docs/plugins/inspect/plugin-subparse.xml @@ -3,7 +3,7 @@ Subtitle parsing ../../gst/subparse/.libs/libgstsubparse.so libgstsubparse.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-tcp.xml b/docs/plugins/inspect/plugin-tcp.xml index 2397dd0fe6..cb539aab7b 100644 --- a/docs/plugins/inspect/plugin-tcp.xml +++ b/docs/plugins/inspect/plugin-tcp.xml @@ -3,7 +3,7 @@ transfer data over the network via TCP ../../gst/tcp/.libs/libgsttcp.so libgsttcp.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-theora.xml b/docs/plugins/inspect/plugin-theora.xml index 2c6c2b5de7..989e1420f4 100644 --- a/docs/plugins/inspect/plugin-theora.xml +++ b/docs/plugins/inspect/plugin-theora.xml @@ -3,7 +3,7 @@ Theora plugin library ../../ext/theora/.libs/libgsttheora.so libgsttheora.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-typefindfunctions.xml b/docs/plugins/inspect/plugin-typefindfunctions.xml index 413e99d347..d63b20de1c 100644 --- a/docs/plugins/inspect/plugin-typefindfunctions.xml +++ b/docs/plugins/inspect/plugin-typefindfunctions.xml @@ -3,7 +3,7 @@ default typefind functions ../../gst/typefind/.libs/libgsttypefindfunctions.so libgsttypefindfunctions.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-uridecodebin.xml b/docs/plugins/inspect/plugin-uridecodebin.xml index bafb780f33..26f211d384 100644 --- a/docs/plugins/inspect/plugin-uridecodebin.xml +++ b/docs/plugins/inspect/plugin-uridecodebin.xml @@ -3,7 +3,7 @@ URI Decoder bin ../../gst/playback/.libs/libgstdecodebin2.so libgstdecodebin2.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-video4linux.xml b/docs/plugins/inspect/plugin-video4linux.xml index c184dce9b4..eddf4b1ef6 100644 --- a/docs/plugins/inspect/plugin-video4linux.xml +++ b/docs/plugins/inspect/plugin-video4linux.xml @@ -3,7 +3,7 @@ elements for Video 4 Linux ../../sys/v4l/.libs/libgstvideo4linux.so libgstvideo4linux.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-videorate.xml b/docs/plugins/inspect/plugin-videorate.xml index b7c7dbda42..7734630884 100644 --- a/docs/plugins/inspect/plugin-videorate.xml +++ b/docs/plugins/inspect/plugin-videorate.xml @@ -3,7 +3,7 @@ Adjusts video frames ../../gst/videorate/.libs/libgstvideorate.so libgstvideorate.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-videoscale.xml b/docs/plugins/inspect/plugin-videoscale.xml index ce92c33f6b..101eef0243 100644 --- a/docs/plugins/inspect/plugin-videoscale.xml +++ b/docs/plugins/inspect/plugin-videoscale.xml @@ -3,7 +3,7 @@ Resizes video ../../gst/videoscale/.libs/libgstvideoscale.so libgstvideoscale.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git @@ -12,7 +12,7 @@ videoscale Video scaler - Filter/Effect/Video + Filter/Converter/Video/Scaler Resizes video Wim Taymans <wim.taymans@chello.be> diff --git a/docs/plugins/inspect/plugin-videotestsrc.xml b/docs/plugins/inspect/plugin-videotestsrc.xml index c8ccb14a9d..bc2f747d2f 100644 --- a/docs/plugins/inspect/plugin-videotestsrc.xml +++ b/docs/plugins/inspect/plugin-videotestsrc.xml @@ -3,7 +3,7 @@ Creates a test video stream ../../gst/videotestsrc/.libs/libgstvideotestsrc.so libgstvideotestsrc.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-volume.xml b/docs/plugins/inspect/plugin-volume.xml index 331d20a279..0978f0644e 100644 --- a/docs/plugins/inspect/plugin-volume.xml +++ b/docs/plugins/inspect/plugin-volume.xml @@ -3,7 +3,7 @@ plugin for controlling audio volume ../../gst/volume/.libs/libgstvolume.so libgstvolume.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-vorbis.xml b/docs/plugins/inspect/plugin-vorbis.xml index a00b815ecb..7f92050210 100644 --- a/docs/plugins/inspect/plugin-vorbis.xml +++ b/docs/plugins/inspect/plugin-vorbis.xml @@ -3,7 +3,7 @@ Vorbis plugin library ../../ext/vorbis/.libs/libgstvorbis.so libgstvorbis.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-ximagesink.xml b/docs/plugins/inspect/plugin-ximagesink.xml index be3996315c..3e81de1d64 100644 --- a/docs/plugins/inspect/plugin-ximagesink.xml +++ b/docs/plugins/inspect/plugin-ximagesink.xml @@ -3,7 +3,7 @@ X11 video output element based on standard Xlib calls ../../sys/ximage/.libs/libgstximagesink.so libgstximagesink.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/docs/plugins/inspect/plugin-xvimagesink.xml b/docs/plugins/inspect/plugin-xvimagesink.xml index 33280649ef..2d7943ce7b 100644 --- a/docs/plugins/inspect/plugin-xvimagesink.xml +++ b/docs/plugins/inspect/plugin-xvimagesink.xml @@ -3,7 +3,7 @@ XFree86 video output plugin using Xv extension ../../sys/xvimage/.libs/libgstxvimagesink.so libgstxvimagesink.so - 0.10.31.1 + 0.10.32.1 LGPL gst-plugins-base GStreamer Base Plug-ins git diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index a7ee8ae5b1..b27a9a7486 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -122,6 +122,9 @@ static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad, GstFlowReturn ret); static void gst_ogg_demux_sync_streams (GstOggDemux * ogg); +GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, + GstCaps * caps, GList * headers); + GType gst_ogg_pad_get_type (void); G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD); @@ -449,6 +452,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, guint64 out_offset, out_offset_end; gboolean delta_unit = FALSE; + cret = GST_FLOW_OK; + GST_DEBUG_OBJECT (ogg, "%p streaming to peer serial %08lx", pad, pad->map.serialno); @@ -464,7 +469,6 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) { /* We don't push header packets for OGM */ - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); goto done; } @@ -485,7 +489,6 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, packet->b_o_s || (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) { /* We don't push header packets for VP8 */ - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); goto done; } offset = 0; @@ -540,7 +543,12 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, } else if (pad->map.is_sparse) { out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule); - out_duration = GST_CLOCK_TIME_NONE; + if (duration == GST_CLOCK_TIME_NONE) { + out_duration = GST_CLOCK_TIME_NONE; + } else { + out_duration = gst_util_uint64_scale (duration, + GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n); + } } else { out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule - duration); @@ -562,15 +570,11 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, goto empty_packet; } - ret = - gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad), - GST_BUFFER_OFFSET_NONE, packet->bytes - offset - trim, - GST_PAD_CAPS (pad), &buf); + if (!pad->added) + goto not_added; - /* combine flows */ - cret = gst_ogg_demux_combine_flows (ogg, pad, ret); - if (ret != GST_FLOW_OK) - goto no_buffer; + buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim); + gst_buffer_set_caps (buf, GST_PAD_CAPS (pad)); /* set delta flag for OGM content */ if (delta_unit) @@ -651,23 +655,17 @@ done: empty_packet: { GST_DEBUG_OBJECT (ogg, "Skipping empty packet"); - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); goto done; } no_timestamp: { GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet"); - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); goto done; } - -no_buffer: +not_added: { - GST_DEBUG_OBJECT (ogg, - "%p could not get buffer from peer %08lx, %d (%s), combined %d (%s)", - pad, pad->map.serialno, ret, gst_flow_get_name (ret), - cret, gst_flow_get_name (cret)); + GST_DEBUG_OBJECT (ogg, "pad not added yet"); goto done; } } @@ -1398,10 +1396,7 @@ gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event) return res; } -/* submit the given buffer to the ogg sync. - * - * Returns the number of bytes submited. - */ +/* submit the given buffer to the ogg sync */ static GstFlowReturn gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer) { @@ -1705,6 +1700,47 @@ gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg) return TRUE; } +GstCaps * +gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps, + GList * headers) +{ + GstStructure *structure; + GValue array = { 0 }; + + GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps); + + if (G_UNLIKELY (!caps)) + return NULL; + if (G_UNLIKELY (!headers)) + return NULL; + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&array, GST_TYPE_ARRAY); + + while (headers) { + GValue value = { 0 }; + GstBuffer *buffer; + ogg_packet *op = headers->data; + g_assert (op); + buffer = gst_buffer_new_and_alloc (op->bytes); + memcpy (GST_BUFFER_DATA (buffer), op->packet, op->bytes); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS); + g_value_init (&value, GST_TYPE_BUFFER); + gst_value_take_buffer (&value, buffer); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + headers = headers->next; + } + + gst_structure_set_value (structure, "streamheader", &array); + g_value_unset (&array); + GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps); + + return caps; +} + static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, GstEvent * event) @@ -1728,7 +1764,6 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, /* first add the pads */ for (i = 0; i < chain->streams->len; i++) { GstOggPad *pad; - GstStructure *structure; pad = g_array_index (chain->streams, GstOggPad *, i); @@ -1746,8 +1781,6 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad); - structure = gst_caps_get_structure (GST_PAD_CAPS (pad), 0); - /* activate first */ gst_pad_set_active (GST_PAD_CAST (pad), TRUE); @@ -1790,6 +1823,11 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, pad->map.taglist = NULL; } + /* Set headers on caps */ + pad->map.caps = + gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers); + gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps); + GST_DEBUG_OBJECT (ogg, "pushing headers"); /* push headers */ for (walk = pad->map.headers; walk; walk = g_list_next (walk)) { @@ -2068,7 +2106,7 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, goto next; granulepos = ogg_page_granulepos (&og); - if (granulepos == -1) { + if (granulepos == -1 || granulepos == 0) { GST_LOG_OBJECT (ogg, "granulepos of next page is -1"); continue; } diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index bab9442a12..9016102d2e 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -282,7 +282,8 @@ gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData * data) GstOggPadData *oggpad = (GstOggPadData *) data; GstBuffer *buf; - ogg_stream_clear (&oggpad->stream); + ogg_stream_clear (&oggpad->map.stream); + gst_caps_replace (&oggpad->map.caps, NULL); if (oggpad->pagebuffers) { while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) { @@ -336,6 +337,34 @@ gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event) return ret; } +static gboolean +gst_ogg_mux_is_serialno_present (GstOggMux * ogg_mux, guint32 serialno) +{ + GSList *walk; + + walk = ogg_mux->collect->data; + while (walk) { + GstOggPadData *pad = (GstOggPadData *) walk->data; + if (pad->map.serialno == serialno) + return TRUE; + walk = walk->next; + } + + return FALSE; +} + +static guint32 +gst_ogg_mux_generate_serialno (GstOggMux * ogg_mux) +{ + guint32 serialno; + + do { + serialno = g_random_int_range (0, G_MAXINT32); + } while (gst_ogg_mux_is_serialno_present (ogg_mux, serialno)); + + return serialno; +} + static GstPad * gst_ogg_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * req_name) @@ -363,7 +392,7 @@ gst_ogg_mux_request_new_pad (GstElement * element, if (req_name == NULL || strlen (req_name) < 6) { /* no name given when requesting the pad, use random serial number */ - serial = rand (); + serial = gst_ogg_mux_generate_serialno (ogg_mux); } else { /* parse serial number from requested padname */ serial = atoi (&req_name[5]); @@ -384,8 +413,8 @@ gst_ogg_mux_request_new_pad (GstElement * element, sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify); ogg_mux->active_pads++; - oggpad->serial = serial; - ogg_stream_init (&oggpad->stream, serial); + oggpad->map.serialno = serial; + ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno); oggpad->packetno = 0; oggpad->pageno = 0; oggpad->eos = FALSE; @@ -394,7 +423,10 @@ gst_ogg_mux_request_new_pad (GstElement * element, oggpad->new_page = TRUE; oggpad->first_delta = FALSE; oggpad->prev_delta = FALSE; + oggpad->data_pushed = FALSE; oggpad->pagebuffers = g_queue_new (); + oggpad->map.headers = NULL; + oggpad->map.queued = NULL; oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); gst_pad_set_event_function (newpad, @@ -750,7 +782,6 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux) /* try to get a new buffer for this pad if needed and possible */ if (pad->buffer == NULL) { GstBuffer *buf; - gboolean incaps; /* shift the buffer along if needed (it's okay if next_buffer is NULL) */ if (pad->buffer == NULL) { @@ -769,19 +800,46 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux) GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) ogg_mux->delta_pad = pad; - incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS); /* if we need headers */ if (pad->state == GST_OGG_PAD_STATE_CONTROL) { /* and we have one */ - if (incaps) { + ogg_packet packet; + gboolean is_header; + + packet.packet = GST_BUFFER_DATA (buf); + packet.bytes = GST_BUFFER_SIZE (buf); + + if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) + packet.granulepos = GST_BUFFER_OFFSET_END (buf); + else + packet.granulepos = 0; + + /* if we're not yet in data mode, ensure we're setup on the first packet */ + if (!pad->have_type) { + pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet); + if (!pad->have_type) { + GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream " + "(pad caps: %" GST_PTR_FORMAT ")", GST_PAD_CAPS (pad)); + } else { + GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT, + pad->map.caps); + } + } + + if (pad->have_type) + is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet); + else /* fallback (FIXME 0.11: remove IN_CAPS hack) */ + is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + + if (is_header) { GST_DEBUG_OBJECT (ogg_mux, - "got incaps buffer in control state, ignoring"); + "got header buffer in control state, ignoring"); /* just ignore */ gst_buffer_unref (buf); buf = NULL; } else { GST_DEBUG_OBJECT (ogg_mux, - "got data buffer in control state, switching " "to data mode"); + "got data buffer in control state, switching to data mode"); /* this is a data buffer so switch to data state */ pad->state = GST_OGG_PAD_STATE_DATA; } @@ -798,7 +856,7 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux) /* Just gone to EOS. Flush existing page(s) */ pad->eos = TRUE; - while (ogg_stream_flush (&pad->stream, &page)) { + while (ogg_stream_flush (&pad->map.stream, &page)) { /* Place page into the per-pad queue */ ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta); @@ -973,7 +1031,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) continue; /* now figure out the headers */ - pad->headers = gst_ogg_mux_get_headers (pad); + pad->map.headers = gst_ogg_mux_get_headers (pad); } GST_LOG_OBJECT (mux, "creating BOS pages"); @@ -999,9 +1057,9 @@ gst_ogg_mux_send_headers (GstOggMux * mux) GST_LOG_OBJECT (thepad, "looping over headers"); - if (pad->headers) { - buf = GST_BUFFER (pad->headers->data); - pad->headers = g_list_remove (pad->headers, buf); + if (pad->map.headers) { + buf = GST_BUFFER (pad->map.headers->data); + pad->map.headers = g_list_remove (pad->map.headers, buf); } else if (pad->buffer) { buf = pad->buffer; gst_buffer_ref (buf); @@ -1010,10 +1068,10 @@ gst_ogg_mux_send_headers (GstOggMux * mux) gst_buffer_ref (buf); } else { /* fixme -- should be caught in the previous list traversal. */ - GST_OBJECT_LOCK (pad); + GST_OBJECT_LOCK (thepad); g_critical ("No headers or buffers on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_OBJECT_UNLOCK (pad); + GST_DEBUG_PAD_NAME (thepad)); + GST_OBJECT_UNLOCK (thepad); continue; } @@ -1030,11 +1088,11 @@ gst_ogg_mux_send_headers (GstOggMux * mux) packet.e_o_s = 0; /* swap the packet in */ - ogg_stream_packetin (&pad->stream, &packet); + ogg_stream_packetin (&pad->map.stream, &packet); gst_buffer_unref (buf); GST_LOG_OBJECT (thepad, "flushing out BOS page"); - if (!ogg_stream_flush (&pad->stream, &page)) + if (!ogg_stream_flush (&pad->map.stream, &page)) g_critical ("Could not flush BOS page"); hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); @@ -1078,7 +1136,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s", GST_DEBUG_PAD_NAME (thepad)); - hwalk = pad->headers; + hwalk = pad->map.headers; while (hwalk) { GstBuffer *buf = GST_BUFFER (hwalk->data); ogg_packet packet; @@ -1099,15 +1157,15 @@ gst_ogg_mux_send_headers (GstOggMux * mux) packet.e_o_s = 0; /* swap the packet in */ - ogg_stream_packetin (&pad->stream, &packet); + ogg_stream_packetin (&pad->map.stream, &packet); gst_buffer_unref (buf); /* if last header, flush page */ if (hwalk == NULL) { GST_LOG_OBJECT (mux, "flushing page as packet %" G_GUINT64_FORMAT " is first or " - "last packet", pad->packetno); - while (ogg_stream_flush (&pad->stream, &page)) { + "last packet", packet.packetno); + while (ogg_stream_flush (&pad->map.stream, &page)) { GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); GST_LOG_OBJECT (mux, "swapped out page"); @@ -1116,7 +1174,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) } else { GST_LOG_OBJECT (mux, "try to swap out page"); /* just try to swap out a page then */ - while (ogg_stream_pageout (&pad->stream, &page) > 0) { + while (ogg_stream_pageout (&pad->map.stream, &page) > 0) { GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); GST_LOG_OBJECT (mux, "swapped out page"); @@ -1124,8 +1182,8 @@ gst_ogg_mux_send_headers (GstOggMux * mux) } } } - g_list_free (pad->headers); - pad->headers = NULL; + g_list_free (pad->map.headers); + pad->map.headers = NULL; } /* hbufs holds all buffers for the headers now */ @@ -1206,9 +1264,10 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT " will make page too long, flushing", - GST_BUFFER_OFFSET_END (pad->buffer), (gint64) pad->stream.packetno); + GST_BUFFER_OFFSET_END (pad->buffer), + (gint64) pad->map.stream.packetno); - while (ogg_stream_flush (&pad->stream, &page)) { + while (ogg_stream_flush (&pad->map.stream, &page)) { /* end time of this page is the timestamp of the next buffer */ ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer); /* Place page into the per-pad queue */ @@ -1306,11 +1365,15 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) } if (GST_BUFFER_IS_DISCONT (buf)) { - GST_LOG_OBJECT (pad->collect.pad, "got discont"); - packet.packetno++; - /* No public API for this; hack things in */ - pad->stream.pageno++; - force_flush = TRUE; + if (pad->data_pushed) { + GST_LOG_OBJECT (pad->collect.pad, "got discont"); + packet.packetno++; + /* No public API for this; hack things in */ + pad->map.stream.pageno++; + force_flush = TRUE; + } else { + GST_LOG_OBJECT (pad->collect.pad, "discont at stream start"); + } } /* flush the currently built page if necessary */ @@ -1318,7 +1381,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT " forced flush of page before this packet", GST_BUFFER_OFFSET_END (pad->buffer)); - while (ogg_stream_flush (&pad->stream, &page)) { + while (ogg_stream_flush (&pad->map.stream, &page)) { /* end time of this page is the timestamp of the next buffer */ ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer); ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, @@ -1363,7 +1426,8 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) if (packet.b_o_s == 1) GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet"); - ogg_stream_packetin (&pad->stream, &packet); + ogg_stream_packetin (&pad->map.stream, &packet); + pad->data_pushed = TRUE; gp_time = GST_BUFFER_OFFSET (pad->buffer); granulepos = GST_BUFFER_OFFSET_END (pad->buffer); @@ -1381,7 +1445,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) /* let ogg write out the pages now. The packet we got could end * up in more than one page so we need to write them all */ - if (ogg_stream_pageout (&pad->stream, &page) > 0) { + if (ogg_stream_pageout (&pad->map.stream, &page) > 0) { /* we have a new page, so we need to timestamp it correctly. * if this fresh packet ends on this page, then the page's granulepos * comes from that packet, and we should set this buffer's timestamp */ @@ -1392,7 +1456,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp)); GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT " new page %ld", - GST_GP_CAST (ogg_page_granulepos (&page)), pad->stream.pageno); + GST_GP_CAST (ogg_page_granulepos (&page)), pad->map.stream.pageno); if (ogg_page_granulepos (&page) == granulepos) { /* the packet we streamed in finishes on the current page, @@ -1421,7 +1485,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) /* use an inner loop here to flush the remaining pages and * mark them as delta frames as well */ - while (ogg_stream_pageout (&pad->stream, &page) > 0) { + while (ogg_stream_pageout (&pad->map.stream, &page) > 0) { if (ogg_page_granulepos (&page) == granulepos) { /* the page has taken up the new packet completely, which means * the packet ends the page and we can update the gp time @@ -1598,7 +1662,7 @@ gst_ogg_mux_init_collectpads (GstCollectPads * collect) while (walk) { GstOggPadData *oggpad = (GstOggPadData *) walk->data; - ogg_stream_init (&oggpad->stream, oggpad->serial); + ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno); oggpad->packetno = 0; oggpad->pageno = 0; oggpad->eos = FALSE; @@ -1607,6 +1671,7 @@ gst_ogg_mux_init_collectpads (GstCollectPads * collect) oggpad->new_page = TRUE; oggpad->first_delta = FALSE; oggpad->prev_delta = FALSE; + oggpad->data_pushed = FALSE; oggpad->pagebuffers = g_queue_new (); walk = g_slist_next (walk); @@ -1623,7 +1688,7 @@ gst_ogg_mux_clear_collectpads (GstCollectPads * collect) GstOggPadData *oggpad = (GstOggPadData *) walk->data; GstBuffer *buf; - ogg_stream_clear (&oggpad->stream); + ogg_stream_clear (&oggpad->map.stream); while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) { gst_buffer_unref (buf); diff --git a/ext/ogg/gstoggmux.h b/ext/ogg/gstoggmux.h index 6134d5ed2f..6812f82ca2 100644 --- a/ext/ogg/gstoggmux.h +++ b/ext/ogg/gstoggmux.h @@ -25,6 +25,7 @@ #include #include +#include "gstoggstream.h" G_BEGIN_DECLS @@ -49,13 +50,14 @@ typedef struct { GstCollectData collect; /* we extend the CollectData */ + GstOggStream map; + gboolean have_type; + /* These two buffers make a very simple queue - they enter as 'next_buffer' * and (usually) leave as 'buffer', except at EOS, when buffer will be NULL */ GstBuffer *buffer; /* the first waiting buffer for the pad */ GstBuffer *next_buffer; /* the second waiting buffer for the pad */ - gint serial; - ogg_stream_state stream; gint64 packetno; /* number of next packet */ gint64 pageno; /* number of next page */ guint64 duration; /* duration of current page */ @@ -71,13 +73,12 @@ typedef struct GstOggPadState state; /* state of the pad */ - GList *headers; - GQueue *pagebuffers; /* List of pages in buffers ready for pushing */ gboolean new_page; /* starting a new page */ gboolean first_delta; /* was the first packet in the page a delta */ gboolean prev_delta; /* was the previous buffer a delta frame */ + gboolean data_pushed; /* whether we pushed data already */ GstPadEventFunction collect_event; diff --git a/ext/ogg/gstoggparse.c b/ext/ogg/gstoggparse.c index e05c0543ed..ffbba865a1 100644 --- a/ext/ogg/gstoggparse.c +++ b/ext/ogg/gstoggparse.c @@ -115,6 +115,7 @@ free_stream (GstOggStream * stream) { g_list_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL); g_list_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL); + g_list_foreach (stream->stored_buffers, (GFunc) gst_mini_object_unref, NULL); g_free (stream); } @@ -281,29 +282,41 @@ gst_ogg_parse_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } -/* submit the given buffer to the ogg sync. - * - * Returns the number of bytes submited. - */ -static gint +/* submit the given buffer to the ogg sync */ +static GstFlowReturn gst_ogg_parse_submit_buffer (GstOggParse * ogg, GstBuffer * buffer) { guint size; guint8 *data; gchar *oggbuffer; + GstFlowReturn ret = GST_FLOW_OK; size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); - /* We now have a buffer, submit it to the ogg sync layer */ - oggbuffer = ogg_sync_buffer (&ogg->sync, size); - memcpy (oggbuffer, data, size); - ogg_sync_wrote (&ogg->sync, size); + GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size); + if (G_UNLIKELY (size == 0)) + goto done; - /* We've copied all the neccesary data, so we're done with the buffer */ + oggbuffer = ogg_sync_buffer (&ogg->sync, size); + if (G_UNLIKELY (oggbuffer == NULL)) { + GST_ELEMENT_ERROR (ogg, STREAM, DECODE, + (NULL), ("failed to get ogg sync buffer")); + ret = GST_FLOW_ERROR; + goto done; + } + + memcpy (oggbuffer, data, size); + if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0)) { + GST_ELEMENT_ERROR (ogg, STREAM, DECODE, + (NULL), ("failed to write %d bytes to the sync buffer", size)); + ret = GST_FLOW_ERROR; + } + +done: gst_buffer_unref (buffer); - return size; + return ret; } static void @@ -350,7 +363,7 @@ gst_ogg_parse_is_header (GstOggParse * ogg, GstOggStream * stream, static GstBuffer * gst_ogg_parse_buffer_from_page (ogg_page * page, - guint64 offset, gboolean keyframe, GstClockTime timestamp) + guint64 offset, GstClockTime timestamp) { int size = page->header_len + page->body_len; GstBuffer *buf = gst_buffer_new_and_alloc (size); @@ -361,9 +374,6 @@ gst_ogg_parse_buffer_from_page (ogg_page * page, GST_BUFFER_TIMESTAMP (buf) = timestamp; GST_BUFFER_OFFSET (buf) = offset; GST_BUFFER_OFFSET_END (buf) = offset + size; - if (!keyframe) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - } return buf; } @@ -435,7 +445,7 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) keyframe = TRUE; } pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, - keyframe, buffertimestamp); + buffertimestamp); /* We read out 'ret' bytes, so we set the next offset appropriately */ ogg->offset += ret; @@ -647,15 +657,42 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) g_list_free (stream->unknown_pages); stream->unknown_pages = NULL; } + } - gst_buffer_set_caps (pagebuffer, caps); - - result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); - if (result != GST_FLOW_OK) - return result; + if (granule == -1) { + stream->stored_buffers = g_list_append (stream->stored_buffers, + pagebuffer); } else { - /* Normal data page, submit buffer */ + if (stream->stored_buffers) { + int j; + + for (j = 0; j < g_list_length (stream->stored_buffers); j++) { + GstBuffer *buf = + GST_BUFFER (g_list_nth_data (stream->stored_buffers, j)); + + gst_buffer_set_caps (buf, ogg->caps); + GST_BUFFER_TIMESTAMP (buf) = buffertimestamp; + if (!keyframe) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + keyframe = FALSE; + } + + result = gst_pad_push (ogg->srcpad, buf); + if (result != GST_FLOW_OK) + return result; + } + g_list_free (stream->stored_buffers); + stream->stored_buffers = NULL; + } + gst_buffer_set_caps (pagebuffer, ogg->caps); + if (!keyframe) { + GST_BUFFER_FLAG_SET (pagebuffer, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + keyframe = FALSE; + } + result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); if (result != GST_FLOW_OK) return result; diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index eeaf4b9b50..caa986513e 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -255,6 +255,11 @@ granule_to_granulepos_default (GstOggStream * pad, gint64 granule, gint64 keyoffset; if (pad->granuleshift != 0) { + /* If we don't know where the previous keyframe is yet, assume it is + at 0 or 1, depending on bitstream version. If nothing else, this + avoids getting negative granpos back. */ + if (keyframe_granule < 0) + keyframe_granule = pad->theora_has_zero_keyoffset ? 0 : 1; keyoffset = granule - keyframe_granule; return (keyframe_granule << pad->granuleshift) | keyoffset; } else { @@ -342,9 +347,14 @@ setup_theora_mapper (GstOggStream * pad, ogg_packet * packet) { guint8 *data = packet->packet; guint w, h, par_d, par_n; + guint8 vmaj, vmin, vrev; - w = GST_READ_UINT24_BE (data + 14) & 0xFFFFF0; - h = GST_READ_UINT24_BE (data + 17) & 0xFFFFF0; + vmaj = data[7]; + vmin = data[8]; + vrev = data[9]; + + w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF; + h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF; pad->granulerate_n = GST_READ_UINT32_BE (data + 22); pad->granulerate_d = GST_READ_UINT32_BE (data + 26); @@ -371,6 +381,12 @@ setup_theora_mapper (GstOggStream * pad, ogg_packet * packet) return FALSE; } + /* The interpretation of the granule position has changed with 3.2.1. + The granule is now made from the number of frames encoded, rather than + the index of the frame being encoded - so there is a difference of 1. */ + pad->theora_has_zero_keyoffset = + ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201; + pad->caps = gst_caps_new_simple ("video/x-theora", NULL); if (w > 0 && h > 0) { @@ -398,9 +414,6 @@ granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos) if (pad->granuleshift != 0) { keyindex = granulepos >> pad->granuleshift; keyoffset = granulepos - (keyindex << pad->granuleshift); - if (keyoffset == 0) { - pad->theora_has_zero_keyoffset = TRUE; - } if (pad->theora_has_zero_keyoffset) { keyoffset++; } @@ -1732,6 +1745,83 @@ setup_kate_mapper (GstOggStream * pad, ogg_packet * packet) return TRUE; } +static gint64 +packet_duration_kate (GstOggStream * pad, ogg_packet * packet) +{ + gint64 duration; + + if (packet->bytes < 1) + return 0; + + switch (packet->packet[0]) { + case 0x00: /* text data */ + if (packet->bytes < 1 + 8 * 2) { + duration = 0; + } else { + duration = GST_READ_UINT64_LE (packet->packet + 1 + 8); + if (duration < 0) + duration = 0; + } + break; + default: + duration = GST_CLOCK_TIME_NONE; + break; + } + + return duration; +} + +static void +extract_tags_kate (GstOggStream * pad, ogg_packet * packet) +{ + GstTagList *list = NULL; + + if (packet->bytes <= 0) + return; + + switch (packet->packet[0]) { + case 0x80:{ + const gchar *canonical; + char language[16]; + + if (packet->bytes < 64) { + GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored"); + break; + } + + /* the language tag is 16 bytes at offset 32, ensure NUL terminator */ + memcpy (language, packet->packet + 32, 16); + language[15] = 0; + + /* language is an ISO 639-1 code or RFC 3066 language code, we + * truncate to ISO 639-1 */ + g_strdelimit (language, NULL, '\0'); + canonical = gst_tag_get_language_code_iso_639_1 (language); + if (canonical) { + list = gst_tag_list_new_full (GST_TAG_LANGUAGE_CODE, canonical, NULL); + } else { + GST_WARNING ("Unknown or invalid language code %s, ignored", language); + } + break; + } + case 0x81: + tag_list_from_vorbiscomment_packet (packet, + (const guint8 *) "\201kate\0\0\0\0", 9, &list); + break; + default: + break; + } + + if (list) { + if (pad->taglist) { + /* ensure the comment packet cannot override the category/language + from the identification header */ + gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL); + } else + pad->taglist = list; + } +} + /* *INDENT-OFF* */ /* indent hates our freedoms */ @@ -1875,9 +1965,9 @@ const GstOggMap mappers[] = { granule_to_granulepos_default, NULL, is_header_count, + packet_duration_kate, NULL, - NULL, - NULL + extract_tags_kate }, { "BBCD\0", 5, 13, diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index 974ad89b50..94c8f05504 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -53,6 +53,7 @@ struct _GstOggStream /* for oggparse */ gboolean in_headers; GList *unknown_pages; + GList *stored_buffers; gint map; gboolean is_skeleton; diff --git a/ext/pango/Makefile.am b/ext/pango/Makefile.am index 197ed33840..2ffea22685 100644 --- a/ext/pango/Makefile.am +++ b/ext/pango/Makefile.am @@ -15,12 +15,14 @@ libgstpango_la_SOURCES = \ libgstpango_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ + $(GST_CONTROLLER_CFLAGS) \ $(GST_CFLAGS) \ $(PANGO_CFLAGS) libgstpango_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ $(top_builddir)/gst-libs/gst/video/libgstvideo-$(GST_MAJORMINOR).la \ $(GST_BASE_LIBS) \ + $(GST_CONTROLLER_LIBS) \ $(GST_LIBS) \ $(PANGO_LIBS) libgstpango_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/ext/pango/gsttextoverlay.c b/ext/pango/gsttextoverlay.c index 0dd8a102d1..c09de831e6 100644 --- a/ext/pango/gsttextoverlay.c +++ b/ext/pango/gsttextoverlay.c @@ -216,6 +216,7 @@ gst_text_overlay_valign_get_type (void) {GST_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"}, {GST_TEXT_OVERLAY_VALIGN_TOP, "top", "top"}, {GST_TEXT_OVERLAY_VALIGN_POS, "position", "position"}, + {GST_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"}, {0, NULL, NULL}, }; @@ -382,7 +383,7 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT, g_param_spec_string ("text", "text", "Text to be display.", DEFAULT_PROP_TEXT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING, g_param_spec_boolean ("shaded-background", "shaded background", "Whether to shade the background under the text area", @@ -430,7 +431,8 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS, g_param_spec_double ("xpos", "horizontal position", "Horizontal position when using position alignment", 0, 1.0, - DEFAULT_PROP_XPOS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + DEFAULT_PROP_XPOS, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); /** * GstTextOverlay:ypos * @@ -441,7 +443,8 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS, g_param_spec_double ("ypos", "vertical position", "Vertical position when using position alignment", 0, 1.0, - DEFAULT_PROP_YPOS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + DEFAULT_PROP_YPOS, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE, g_param_spec_enum ("wrap-mode", "wrap mode", "Whether to wrap the text and if so how.", @@ -463,7 +466,8 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR, g_param_spec_uint ("color", "Color", "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32, - DEFAULT_PROP_COLOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + DEFAULT_PROP_COLOR, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); /** * GstTextOverlay:line-alignment @@ -489,7 +493,8 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, g_param_spec_boolean ("silent", "silent", "Whether to render the text string", - DEFAULT_PROP_SILENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + DEFAULT_PROP_SILENT, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); /** * GstTextOverlay:wait-text * @@ -848,10 +853,9 @@ gst_text_overlay_set_property (GObject * object, guint prop_id, overlay->wait_text = g_value_get_boolean (value); break; case PROP_AUTO_ADJUST_SIZE: - { overlay->auto_adjust_size = g_value_get_boolean (value); overlay->need_render = TRUE; - } + break; case PROP_VERTICAL_RENDER: overlay->use_vertical_render = g_value_get_boolean (value); gst_text_overlay_update_render_mode (overlay); @@ -1795,6 +1799,9 @@ gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) ypos = (gint) (overlay->height * overlay->ypos) - height / 2; ypos = CLAMP (ypos, 0, overlay->height - height); break; + case GST_TEXT_OVERLAY_VALIGN_CENTER: + ypos = (overlay->height - height) / 2; + break; default: ypos = overlay->ypad; break; @@ -2248,6 +2255,8 @@ gst_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer) } } + gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer)); + wait_for_text_buf: GST_OBJECT_LOCK (overlay); @@ -2532,6 +2541,8 @@ gst_text_overlay_change_state (GstElement * element, GstStateChange transition) static gboolean plugin_init (GstPlugin * plugin) { + gst_controller_init (NULL, NULL); + if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE, GST_TYPE_TEXT_OVERLAY) || !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE, diff --git a/ext/pango/gsttextoverlay.h b/ext/pango/gsttextoverlay.h index 8031478b9e..10dd9479e0 100644 --- a/ext/pango/gsttextoverlay.h +++ b/ext/pango/gsttextoverlay.h @@ -3,6 +3,7 @@ #include #include +#include #include G_BEGIN_DECLS @@ -34,7 +35,8 @@ typedef enum { GST_TEXT_OVERLAY_VALIGN_BASELINE, GST_TEXT_OVERLAY_VALIGN_BOTTOM, GST_TEXT_OVERLAY_VALIGN_TOP, - GST_TEXT_OVERLAY_VALIGN_POS + GST_TEXT_OVERLAY_VALIGN_POS, + GST_TEXT_OVERLAY_VALIGN_CENTER } GstTextOverlayVAlign; /** diff --git a/ext/theora/gsttheoraenc.c b/ext/theora/gsttheoraenc.c index 8ccfa93cc0..6a09c2cf3e 100644 --- a/ext/theora/gsttheoraenc.c +++ b/ext/theora/gsttheoraenc.c @@ -175,6 +175,57 @@ granulepos_to_timestamp (GstTheoraEnc * theoraenc, ogg_int64_t granulepos) theoraenc->info.fps_numerator); } +/* Generate a dummy encoder context for use in th_encode_ctl queries + Release with th_encode_free() + This and the next routine from theora/examples/libtheora_info.c */ +static th_enc_ctx * +dummy_encode_ctx (void) +{ + th_enc_ctx *ctx; + th_info info; + + /* set the minimal video parameters */ + th_info_init (&info); + info.frame_width = 320; + info.frame_height = 240; + info.fps_numerator = 1; + info.fps_denominator = 1; + + /* allocate and initialize a context object */ + ctx = th_encode_alloc (&info); + if (!ctx) + GST_WARNING ("Failed to allocate dummy encoder context."); + + /* clear the info struct */ + th_info_clear (&info); + + return ctx; +} + +/* Query the current and maximum values for the 'speed level' setting. + This can be used to ask the encoder to trade off encoding quality + vs. performance cost, for example to adapt to realtime constraints. */ +static int +check_speed_level (th_enc_ctx * ctx, int *current, int *max) +{ + int ret; + + /* query the current speed level */ + ret = th_encode_ctl (ctx, TH_ENCCTL_GET_SPLEVEL, current, sizeof (int)); + if (ret) { + GST_WARNING ("Error %d getting current speed level.", ret); + return ret; + } + /* query the maximum speed level, which varies by encoder version */ + ret = th_encode_ctl (ctx, TH_ENCCTL_GET_SPLEVEL_MAX, max, sizeof (int)); + if (ret) { + GST_WARNING ("Error %d getting maximum speed level.", ret); + return ret; + } + + return 0; +} + static GstStaticPadTemplate theora_enc_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -242,6 +293,21 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass) GObjectClass *gobject_class = (GObjectClass *) klass; GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + /* query runtime encoder properties */ + th_enc_ctx *th_ctx; + int default_speed_level = THEORA_DEF_SPEEDLEVEL; + int max_speed_level = default_speed_level; + + GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder"); + + th_ctx = dummy_encode_ctx (); + if (th_ctx) { + if (!check_speed_level (th_ctx, &default_speed_level, &max_speed_level)) + GST_WARNING + ("Failed to determine settings for the speed-level property."); + th_encode_free (th_ctx); + } + gobject_class->set_property = theora_enc_set_property; gobject_class->get_property = theora_enc_get_property; gobject_class->finalize = theora_enc_finalize; @@ -301,40 +367,38 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass) (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_SPEEDLEVEL, g_param_spec_int ("speed-level", "Speed level", - "Controls the amount of motion vector searching done while " - "encoding. This property requires libtheora version >= 1.0", - 0, 3, THEORA_DEF_SPEEDLEVEL, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Controls the amount of analysis performed when encoding." + " Higher values trade compression quality for speed." + " This property requires libtheora version >= 1.0" + ", and the maximum value may vary based on encoder version.", + 0, max_speed_level, default_speed_level, + (GParamFlags) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_VP3_COMPATIBLE, g_param_spec_boolean ("vp3-compatible", "VP3 Compatible", - "Disables non-VP3 compatible features." - " This property requires libtheora version >= 1.1", + "Disables non-VP3 compatible features", THEORA_DEF_VP3_COMPATIBLE, (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_DROP_FRAMES, g_param_spec_boolean ("drop-frames", "VP3 Compatible", - "Allow or disallow frame dropping." - " This property requires libtheora version >= 1.1", + "Allow or disallow frame dropping", THEORA_DEF_DROP_FRAMES, (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CAP_OVERFLOW, g_param_spec_boolean ("cap-overflow", "VP3 Compatible", - "Enable capping of bit reservoir overflows." - " This property requires libtheora version >= 1.1", + "Enable capping of bit reservoir overflows", THEORA_DEF_CAP_OVERFLOW, (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CAP_UNDERFLOW, g_param_spec_boolean ("cap-underflow", "VP3 Compatible", - "Enable capping of bit reservoir underflows." - " This property requires libtheora version >= 1.1", + "Enable capping of bit reservoir underflows", THEORA_DEF_CAP_UNDERFLOW, (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_RATE_BUFFER, g_param_spec_int ("rate-buffer", "Rate Control Buffer", "Sets the size of the rate control buffer, in units of frames. " "The default value of 0 instructs the encoder to automatically " - "select an appropriate value." - " This property requires libtheora version >= 1.1", + "select an appropriate value", 0, 1000, THEORA_DEF_RATE_BUFFER, (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_MULTIPASS_CACHE_FILE, @@ -348,7 +412,6 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass) (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gstelement_class->change_state = theora_enc_change_state; - GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder"); } static void @@ -377,7 +440,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class) enc->expected_ts = GST_CLOCK_TIME_NONE; - enc->speed_level = THEORA_DEF_SPEEDLEVEL; + /* enc->speed_level is set to the libtheora default by the constructor */ enc->vp3_compatible = THEORA_DEF_VP3_COMPATIBLE; enc->drop_frames = THEORA_DEF_DROP_FRAMES; enc->cap_overflow = THEORA_DEF_CAP_OVERFLOW; @@ -1378,13 +1441,12 @@ theora_enc_set_property (GObject * object, guint prop_id, case PROP_BITRATE: GST_OBJECT_LOCK (enc); enc->video_bitrate = g_value_get_int (value) * 1000; - enc->video_quality = 0; enc->bitrate_changed = TRUE; GST_OBJECT_UNLOCK (enc); break; case PROP_QUALITY: GST_OBJECT_LOCK (enc); - if (GST_STATE (enc) >= GST_STATE_PAUSED && enc->video_quality == 0) { + if (GST_STATE (enc) >= GST_STATE_PAUSED && enc->video_bitrate > 0) { GST_WARNING_OBJECT (object, "Can't change from bitrate to quality mode" " while playing"); } else { @@ -1405,6 +1467,10 @@ theora_enc_set_property (GObject * object, guint prop_id, break; case PROP_SPEEDLEVEL: enc->speed_level = g_value_get_int (value); + if (enc->encoder) { + th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level, + sizeof (enc->speed_level)); + } break; case PROP_VP3_COMPATIBLE: enc->vp3_compatible = g_value_get_boolean (value); diff --git a/ext/vorbis/gstvorbisdec.c b/ext/vorbis/gstvorbisdec.c index efd0ba0557..33f4184d57 100644 --- a/ext/vorbis/gstvorbisdec.c +++ b/ext/vorbis/gstvorbisdec.c @@ -827,7 +827,7 @@ vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse, GstClockTime timestamp, GstClockTime duration) { /* interpolate reverse */ - if (vd->last_timestamp != -1 && reverse) + if (vd->last_timestamp != -1 && duration != -1 && reverse) vd->last_timestamp -= duration; /* take buffer timestamp, use interpolated timestamp otherwise */ @@ -837,11 +837,18 @@ vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse, timestamp = vd->last_timestamp; /* interpolate forwards */ - if (vd->last_timestamp != -1 && !reverse) + if (vd->last_timestamp != -1 && duration != -1 && !reverse) vd->last_timestamp += duration; - GST_BUFFER_TIMESTAMP (buf) = timestamp; - GST_BUFFER_DURATION (buf) = duration; + GST_LOG_OBJECT (vd, + "keeping timestamp %" GST_TIME_FORMAT " ts %" GST_TIME_FORMAT " dur %" + GST_TIME_FORMAT, GST_TIME_ARGS (vd->last_timestamp), + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration)); + + if (buf) { + GST_BUFFER_TIMESTAMP (buf) = timestamp; + GST_BUFFER_DURATION (buf) = duration; + } } static GstFlowReturn @@ -850,7 +857,7 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, { vorbis_sample_t **pcm; guint sample_count; - GstBuffer *out; + GstBuffer *out = NULL; GstFlowReturn result; gint size; @@ -910,6 +917,10 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, result = vorbis_dec_push_reverse (vd, out); done: + if (out == NULL) { + /* no output, still keep track of timestamps */ + vorbis_do_timestamps (vd, NULL, FALSE, timestamp, duration); + } vorbis_synthesis_read (&vd->vd, sample_count); return result; diff --git a/gst-libs/gst/app/Makefile.am b/gst-libs/gst/app/Makefile.am index ad55d008af..106423932e 100644 --- a/gst-libs/gst/app/Makefile.am +++ b/gst-libs/gst/app/Makefile.am @@ -70,7 +70,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-`@GST_MAJORMINOR@ \ diff --git a/gst-libs/gst/app/gstappbuffer.c b/gst-libs/gst/app/gstappbuffer.c index 06f354f869..2fa72bb8ad 100644 --- a/gst-libs/gst/app/gstappbuffer.c +++ b/gst-libs/gst/app/gstappbuffer.c @@ -37,9 +37,9 @@ static GstBufferClass *parent_class; GType gst_app_buffer_get_type (void) { - static GType _gst_app_buffer_type; + static volatile gsize app_buffer_type = 0; - if (G_UNLIKELY (_gst_app_buffer_type == 0)) { + if (g_once_init_enter (&app_buffer_type)) { static const GTypeInfo app_buffer_info = { sizeof (GstBufferClass), NULL, @@ -52,10 +52,12 @@ gst_app_buffer_get_type (void) (GInstanceInitFunc) gst_app_buffer_init, NULL }; - _gst_app_buffer_type = g_type_register_static (GST_TYPE_BUFFER, - "GstAppBuffer", &app_buffer_info, 0); + GType tmp = g_type_register_static (GST_TYPE_BUFFER, "GstAppBuffer", + &app_buffer_info, 0); + g_once_init_leave (&app_buffer_type, tmp); } - return _gst_app_buffer_type; + + return (GType) app_buffer_type; } static void diff --git a/gst-libs/gst/app/gstappsink.c b/gst-libs/gst/app/gstappsink.c index 9bcc91eac2..07a3989c86 100644 --- a/gst-libs/gst/app/gstappsink.c +++ b/gst-libs/gst/app/gstappsink.c @@ -111,6 +111,8 @@ struct _GstAppSinkPrivate GstAppSinkCallbacks callbacks; gpointer user_data; GDestroyNotify notify; + + gboolean buffer_lists_supported; }; GST_DEBUG_CATEGORY_STATIC (app_sink_debug); @@ -271,7 +273,8 @@ gst_app_sink_class_init (GstAppSinkClass * klass) g_object_class_install_property (gobject_class, PROP_EMIT_SIGNALS, g_param_spec_boolean ("emit-signals", "Emit signals", - "Emit new-preroll and new-buffer signals", DEFAULT_PROP_EMIT_SIGNALS, + "Emit new-preroll, new-buffer and new-buffer-list signals", + DEFAULT_PROP_EMIT_SIGNALS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_MAX_BUFFERS, @@ -622,6 +625,21 @@ gst_app_sink_flush_unlocked (GstAppSink * appsink) g_cond_signal (priv->cond); } +#define NEW_BUFFER_LIST_SIGID \ + gst_app_sink_signals[SIGNAL_NEW_BUFFER_LIST] + +static gboolean +gst_app_sink_check_buffer_lists_support (GstAppSink * appsink) +{ + gboolean ret; + + ret = (appsink->priv->callbacks.new_buffer_list != NULL) || + g_signal_has_handler_pending (appsink, NEW_BUFFER_LIST_SIGID, 0, FALSE); + + GST_INFO_OBJECT (appsink, "application supports buffer lists: %d", ret); + return ret; +} + static gboolean gst_app_sink_start (GstBaseSink * psink) { @@ -630,7 +648,10 @@ gst_app_sink_start (GstBaseSink * psink) g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "starting"); + priv->flushing = FALSE; priv->started = TRUE; + priv->buffer_lists_supported = + gst_app_sink_check_buffer_lists_support (appsink); g_mutex_unlock (priv->mutex); return TRUE; @@ -779,6 +800,8 @@ restart: if (is_list) { if (priv->callbacks.new_buffer_list) priv->callbacks.new_buffer_list (appsink, priv->user_data); + else if (emit) + g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_BUFFER_LIST], 0); } else { if (priv->callbacks.new_buffer) priv->callbacks.new_buffer (appsink, priv->user_data); @@ -808,9 +831,46 @@ gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer) } static GstFlowReturn -gst_app_sink_render_list (GstBaseSink * psink, GstBufferList * list) +gst_app_sink_render_list (GstBaseSink * sink, GstBufferList * list) { - return gst_app_sink_render_common (psink, GST_MINI_OBJECT_CAST (list), TRUE); + GstBufferListIterator *it; + GstFlowReturn flow; + GstAppSink *appsink; + GstBuffer *group; + + appsink = GST_APP_SINK_CAST (sink); + + if (appsink->priv->buffer_lists_supported) + return gst_app_sink_render_common (sink, GST_MINI_OBJECT_CAST (list), TRUE); + + /* The application doesn't support buffer lists, extract individual buffers + * then and push them one-by-one */ + GST_INFO_OBJECT (sink, "chaining each group in list as a merged buffer"); + + it = gst_buffer_list_iterate (list); + + if (gst_buffer_list_iterator_next_group (it)) { + do { + group = gst_buffer_list_iterator_merge_group (it); + if (group == NULL) { + group = gst_buffer_new (); + GST_DEBUG_OBJECT (sink, "chaining empty group"); + } else { + GST_DEBUG_OBJECT (sink, "chaining group"); + } + flow = gst_app_sink_render (sink, group); + gst_buffer_unref (group); + } while (flow == GST_FLOW_OK && gst_buffer_list_iterator_next_group (it)); + } else { + GST_DEBUG_OBJECT (sink, "chaining empty group"); + group = gst_buffer_new (); + flow = gst_app_sink_render (sink, group); + gst_buffer_unref (group); + } + + gst_buffer_list_iterator_free (it); + + return flow; } static GstCaps * @@ -1331,6 +1391,8 @@ gst_app_sink_set_callbacks (GstAppSink * appsink, priv->callbacks = *callbacks; priv->user_data = user_data; priv->notify = notify; + priv->buffer_lists_supported = + gst_app_sink_check_buffer_lists_support (appsink); GST_OBJECT_UNLOCK (appsink); } diff --git a/gst-libs/gst/app/gstappsrc.c b/gst-libs/gst/app/gstappsrc.c index b5baefa2f9..f051711e26 100644 --- a/gst-libs/gst/app/gstappsrc.c +++ b/gst-libs/gst/app/gstappsrc.c @@ -198,23 +198,24 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); - -#define GST_TYPE_APP_STREAM_TYPE (stream_type_get_type ()) -static GType -stream_type_get_type (void) +GType +gst_app_stream_type_get_type (void) { - static GType stream_type_type = 0; + static volatile gsize stream_type_type = 0; static const GEnumValue stream_type[] = { - {GST_APP_STREAM_TYPE_STREAM, "Stream", "stream"}, - {GST_APP_STREAM_TYPE_SEEKABLE, "Seekable", "seekable"}, - {GST_APP_STREAM_TYPE_RANDOM_ACCESS, "Random Access", "random-access"}, - {0, NULL, NULL}, + {GST_APP_STREAM_TYPE_STREAM, "GST_APP_STREAM_TYPE_STREAM", "stream"}, + {GST_APP_STREAM_TYPE_SEEKABLE, "GST_APP_STREAM_TYPE_SEEKABLE", "seekable"}, + {GST_APP_STREAM_TYPE_RANDOM_ACCESS, "GST_APP_STREAM_TYPE_RANDOM_ACCESS", + "random-access"}, + {0, NULL, NULL} }; - if (!stream_type_type) { - stream_type_type = g_enum_register_static ("GstAppStreamType", stream_type); + if (g_once_init_enter (&stream_type_type)) { + GType tmp = g_enum_register_static ("GstAppStreamType", stream_type); + g_once_init_leave (&stream_type_type, tmp); } - return stream_type_type; + + return (GType) stream_type_type; } static void gst_app_src_uri_handler_init (gpointer g_iface, diff --git a/gst-libs/gst/app/gstappsrc.h b/gst-libs/gst/app/gstappsrc.h index a1e09f7a50..041cb68215 100644 --- a/gst-libs/gst/app/gstappsrc.h +++ b/gst-libs/gst/app/gstappsrc.h @@ -117,6 +117,10 @@ struct _GstAppSrcClass GType gst_app_src_get_type(void); +/* GType getter for GstAppStreamType, since 0.10.32 */ +#define GST_TYPE_APP_STREAM_TYPE (gst_app_stream_type_get_type ()) +GType gst_app_stream_type_get_type (void); + void gst_app_src_set_caps (GstAppSrc *appsrc, const GstCaps *caps); GstCaps* gst_app_src_get_caps (GstAppSrc *appsrc); diff --git a/gst-libs/gst/audio/Makefile.am b/gst-libs/gst/audio/Makefile.am index 3d5d77093b..0a282171f3 100644 --- a/gst-libs/gst/audio/Makefile.am +++ b/gst-libs/gst/audio/Makefile.am @@ -101,7 +101,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=$(builddir)/../interfaces \ diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 8e7760bfa1..b5c87b9fdf 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -71,15 +71,6 @@ enum LAST_SIGNAL }; -/* we tollerate half a second diff before we start resyncing. This - * should be enough to compensate for various rounding errors in the timestamp - * and sample offset position. - * This is an emergency resync fallback since buffers marked as DISCONT will - * always lock to the correct timestamp immediatly and buffers not marked as - * DISCONT are contiguous by definition. - */ -#define DIFF_TOLERANCE 2 - /* FIXME: 0.11, store the buffer_time and latency_time in nanoseconds */ #define DEFAULT_BUFFER_TIME ((200 * GST_MSECOND) / GST_USECOND) #define DEFAULT_LATENCY_TIME ((10 * GST_MSECOND) / GST_USECOND) @@ -89,7 +80,7 @@ enum /* FIXME, enable pull mode when clock slaving and trick modes are figured out */ #define DEFAULT_CAN_ACTIVATE_PULL FALSE -/* when timestamps or clock slaving drift for more than 20ms we resync. This is +/* when timestamps or clock slaving drift for more than 40ms we resync. This is * a reasonable default */ #define DEFAULT_DRIFT_TOLERANCE ((40 * GST_MSECOND) / GST_USECOND) @@ -267,6 +258,7 @@ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, GstBaseAudioSinkClass * g_class) { GstPluginFeature *feature; + GstBaseSink *basesink; baseaudiosink->priv = GST_BASE_AUDIO_SINK_GET_PRIVATE (baseaudiosink); @@ -274,13 +266,16 @@ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, baseaudiosink->latency_time = DEFAULT_LATENCY_TIME; baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK; baseaudiosink->priv->slave_method = DEFAULT_SLAVE_METHOD; + baseaudiosink->priv->drift_tolerance = DEFAULT_DRIFT_TOLERANCE; baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock", (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink); - GST_BASE_SINK (baseaudiosink)->can_activate_push = TRUE; - GST_BASE_SINK (baseaudiosink)->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL; - baseaudiosink->priv->drift_tolerance = DEFAULT_DRIFT_TOLERANCE; + basesink = GST_BASE_SINK_CAST (baseaudiosink); + basesink->can_activate_push = TRUE; + basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL; + + gst_base_sink_set_last_buffer_enabled (basesink, FALSE); /* install some custom pad_query functions */ gst_pad_set_query_function (GST_BASE_SINK_PAD (baseaudiosink), @@ -417,13 +412,6 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query) GST_DEBUG_OBJECT (basesink, "latency query"); - if (!basesink->ringbuffer || !basesink->ringbuffer->spec.rate) { - GST_DEBUG_OBJECT (basesink, - "we are not yet negotiated, can't report latency yet"); - res = FALSE; - goto done; - } - /* ask parent first, it will do an upstream query for us. */ if ((res = gst_base_sink_query_latency (GST_BASE_SINK_CAST (basesink), &live, @@ -434,6 +422,15 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query) if (live && us_live) { GstRingBufferSpec *spec; + GST_OBJECT_LOCK (basesink); + if (!basesink->ringbuffer || !basesink->ringbuffer->spec.rate) { + GST_OBJECT_UNLOCK (basesink); + + GST_DEBUG_OBJECT (basesink, + "we are not yet negotiated, can't report latency yet"); + res = FALSE; + goto done; + } spec = &basesink->ringbuffer->spec; basesink->priv->us_latency = min_l; @@ -441,6 +438,7 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query) min_latency = gst_util_uint64_scale_int (spec->seglatency * spec->segsize, GST_SECOND, spec->rate * spec->bytes_per_sample); + GST_OBJECT_UNLOCK (basesink); /* we cannot go lower than the buffer size and the min peer latency */ min_latency = min_latency + min_l; diff --git a/gst-libs/gst/cdda/Makefile.am b/gst-libs/gst/cdda/Makefile.am index 8b67dffce7..09f334aae5 100644 --- a/gst-libs/gst/cdda/Makefile.am +++ b/gst-libs/gst/cdda/Makefile.am @@ -54,7 +54,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=$(builddir)/../tag \ diff --git a/gst-libs/gst/fft/Makefile.am b/gst-libs/gst/fft/Makefile.am index 2ab5265834..7e84eaec17 100644 --- a/gst-libs/gst/fft/Makefile.am +++ b/gst-libs/gst/fft/Makefile.am @@ -77,7 +77,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/interfaces/Makefile.am b/gst-libs/gst/interfaces/Makefile.am index 6fd5a30462..f462211fa2 100644 --- a/gst-libs/gst/interfaces/Makefile.am +++ b/gst-libs/gst/interfaces/Makefile.am @@ -108,7 +108,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/netbuffer/Makefile.am b/gst-libs/gst/netbuffer/Makefile.am index ec86c57355..fced57c33c 100644 --- a/gst-libs/gst/netbuffer/Makefile.am +++ b/gst-libs/gst/netbuffer/Makefile.am @@ -42,7 +42,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/pbutils/Makefile.am b/gst-libs/gst/pbutils/Makefile.am index 296289723d..e26ef9b49a 100644 --- a/gst-libs/gst/pbutils/Makefile.am +++ b/gst-libs/gst/pbutils/Makefile.am @@ -4,6 +4,8 @@ headers_pbutils = \ pbutils.h \ codec-utils.h \ descriptions.h \ + encoding-profile.h \ + encoding-target.h \ install-plugins.h \ missing-plugins.h \ gstdiscoverer.h @@ -22,6 +24,8 @@ libgstpbutils_@GST_MAJORMINOR@_la_SOURCES = \ pbutils.c \ codec-utils.c \ descriptions.c \ + encoding-profile.c \ + encoding-target.c \ install-plugins.c \ missing-plugins.c \ gstdiscoverer.c \ @@ -81,11 +85,14 @@ GstPbutils-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstpbutils-@GST_MAJ --add-include-path=$(srcdir)/../video \ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ --library=libgstpbutils-@GST_MAJORMINOR@.la \ + --library-path=`$(PKG_CONFIG) --variable=libdir gstreamer-@GST_MAJORMINOR@` \ + --library=gstreamer-@GST_MAJORMINOR@ \ --include=Gst-@GST_MAJORMINOR@ \ --libtool="$(top_builddir)/libtool" \ --pkg gstreamer-@GST_MAJORMINOR@ \ --pkg gstreamer-video-@GST_MAJORMINOR@ \ --pkg-export gstreamer-pbutils-@GST_MAJORMINOR@ \ + --add-init-section="gst_init(NULL,NULL);" \ --output $@ \ $(gir_headers) \ $(gir_sources) @@ -100,7 +107,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(srcdir)/../video \ --includedir=$(builddir) \ diff --git a/gst-libs/gst/pbutils/codec-utils.c b/gst-libs/gst/pbutils/codec-utils.c index 112d240fce..f885fe1ff1 100644 --- a/gst-libs/gst/pbutils/codec-utils.c +++ b/gst-libs/gst/pbutils/codec-utils.c @@ -25,9 +25,8 @@ * * * - * Provides numerous codec-specific ulility functions such as functions to - * provide the codec profile and level in human-readable string form from - * header data. + * Provides codec-specific ulility functions such as functions to provide the + * codec profile and level in human-readable string form from header data. * * */ @@ -87,7 +86,7 @@ gst_codec_utils_aac_get_sample_rate_from_index (guint sr_idx) * gst_codec_utils_aac_get_profile: * @audio_config: a pointer to the AudioSpecificConfig as specified in the * Elementary Stream Descriptor (esds) in ISO/IEC 14496-1 (see - * below for a more details). + * gst_codec_utils_aac_get_level() for a more details). * @len: Length of @audio_config in bytes * * Returns the profile of the given AAC stream as a string. The profile is @@ -98,7 +97,7 @@ gst_codec_utils_aac_get_sample_rate_from_index (guint sr_idx) * HE-AAC support has not yet been implemented. * * - * Returns: The profile as a const string and NULL if the profile could not be + * Returns: The profile as a const string and %NULL if the profile could not be * determined. * * Since: 0.10.31 @@ -134,8 +133,7 @@ gst_codec_utils_aac_get_profile (const guint8 * audio_config, guint len) /** * gst_codec_utils_aac_get_level: * @audio_config: a pointer to the AudioSpecificConfig as specified in the - * Elementary Stream Descriptor (esds) in ISO/IEC 14496-1 (see - * below for a more detailed description). + * Elementary Stream Descriptor (esds) in ISO/IEC 14496-1. * @len: Length of @audio_config in bytes * * Determines the level of a stream as defined in ISO/IEC 14496-3. For AAC LC @@ -145,17 +143,25 @@ gst_codec_utils_aac_get_profile (const guint8 * audio_config, guint len) * The @audio_config parameter follows the following format, starting from the * most significant bit of the first byte: * - * Bit 0:4 contains the AudioObjectType - * Bit 5:8 contains the sample frequency index (if this is 0xf, then the next - * 24 bits define the actual sample frequency, and subsequent fields - * are appropriately shifted). - * Bit 9:12 contains the channel configuration + * + * + * Bit 0:4 contains the AudioObjectType + * + * + * Bit 5:8 contains the sample frequency index (if this is 0xf, then the + * next 24 bits define the actual sample frequency, and subsequent + * fields are appropriately shifted). + * + * + * Bit 9:12 contains the channel configuration + * + * * * * HE-AAC support has not yet been implemented. * * - * Returns: The level as a const string and NULL if the level could not be + * Returns: The level as a const string and %NULL if the level could not be * determined. * * Since: 0.10.31 @@ -325,12 +331,12 @@ gst_codec_utils_aac_get_level (const guint8 * audio_config, guint len) * @len: Length of @audio_config in bytes * * Sets the level and profile on @caps if it can be determined from - * @audio_config. See #gst_codec_utils_aac_get_level() and + * @audio_config. See gst_codec_utils_aac_get_level() and * gst_codec_utils_aac_get_profile() for more details on the parameters. * @caps must be audio/mpeg caps with an "mpegversion" field of either 2 or 4. * If mpegversion is 4, the "base-profile" field is also set in @caps. * - * Returns: TRUE if the level and profile could be set, FALSE otherwise. + * Returns: %TRUE if the level and profile could be set, %FALSE otherwise. * * Since: 0.10.31 */ @@ -386,16 +392,18 @@ gst_codec_utils_aac_caps_set_level_and_profile (GstCaps * caps, * as a bitstream here, with bit 0 being the most significant bit of the first * byte. * - * Bit 0:7 - Profile indication - * Bit 8 - constraint_set0_flag - * Bit 9 - constraint_set1_flag - * Bit 10 - constraint_set2_flag - * Bit 11 - constraint_set3_flag - * Bit 12 - constraint_set3_flag - * Bit 13:15 - Reserved - * Bit 16:24 - Level indication + * + * Bit 0:7 - Profile indication + * Bit 8 - constraint_set0_flag + * Bit 9 - constraint_set1_flag + * Bit 10 - constraint_set2_flag + * Bit 11 - constraint_set3_flag + * Bit 12 - constraint_set3_flag + * Bit 13:15 - Reserved + * Bit 16:24 - Level indication + * * - * Returns: The profile as a const string, or NULL if there is an error. + * Returns: The profile as a const string, or %NULL if there is an error. * * Since: 0.10.31 */ @@ -466,9 +474,9 @@ gst_codec_utils_h264_get_profile (const guint8 * sps, guint len) * * Converts the level indication (level_idc) in the stream's * sequence parameter set into a string. The SPS is expected to have the - * same format as for @gst_codec_utils_aac_get_profile(). + * same format as for gst_codec_utils_h264_get_profile(). * - * Returns: The level as a const string, or NULL if there is an error. + * Returns: The level as a const string, or %NULL if there is an error. * * Since: 0.10.31 */ @@ -525,10 +533,10 @@ gst_codec_utils_h264_get_level (const guint8 * sps, guint len) * @len: Length of the data available in @sps. * * Sets the level and profile in @caps if it can be determined from @sps. See - * #gst_codec_utils_h264_get_level() and #gst_codec_utils_h264_get_profile() + * gst_codec_utils_h264_get_level() and gst_codec_utils_h264_get_profile() * for more details on the parameters. * - * Returns: TRUE if the level and profile could be set, FALSE otherwise. + * Returns: %TRUE if the level and profile could be set, %FALSE otherwise. * * Since: 0.10.31 */ @@ -724,11 +732,11 @@ gst_codec_utils_mpeg4video_get_level (const guint8 * vis_obj_seq, guint len) * @len: Length of the data available in @sps. * * Sets the level and profile in @caps if it can be determined from - * @vis_obj_seq. See #gst_codec_utils_mpeg4video_get_level() and - * #gst_codec_utils_mpeg4video_get_profile() for more details on the + * @vis_obj_seq. See gst_codec_utils_mpeg4video_get_level() and + * gst_codec_utils_mpeg4video_get_profile() for more details on the * parameters. * - * Returns: TRUE if the level and profile could be set, FALSE otherwise. + * Returns: %TRUE if the level and profile could be set, %FALSE otherwise. * * Since: 0.10.31 */ diff --git a/gst-libs/gst/pbutils/descriptions.c b/gst-libs/gst/pbutils/descriptions.c index 9fa0c6eeb4..c32e3d92cd 100644 --- a/gst-libs/gst/pbutils/descriptions.c +++ b/gst-libs/gst/pbutils/descriptions.c @@ -62,9 +62,11 @@ typedef struct static const FormatInfo formats[] = { /* container/tag formats with static descriptions */ + {"application/gxf", "General Exchange Format (GXF)", FLAG_CONTAINER}, {"application/ogg", "Ogg", FLAG_CONTAINER}, - {"application/mxf", "MXF", FLAG_CONTAINER}, + {"application/mxf", "Material eXchange Format (MXF)", FLAG_CONTAINER}, {"application/vnd.rn-realmedia", "Realmedia", FLAG_CONTAINER}, + {"application/x-annodex", "Ogg", FLAG_CONTAINER}, {"application/x-id3", N_("ID3 tag"), FLAG_CONTAINER}, {"application/x-ape", N_("APE tag"), FLAG_CONTAINER}, {"application/x-apetag", N_("APE tag"), FLAG_CONTAINER}, @@ -78,7 +80,7 @@ static const FormatInfo formats[] = { {"video/x-matroska", "Matroska", FLAG_CONTAINER}, {"video/webm", "WebM", FLAG_CONTAINER}, {"video/x-ms-asf", "Advanced Streaming Format (ASF)", FLAG_CONTAINER}, - {"video/x-msvideo", "AVI", FLAG_CONTAINER}, + {"video/x-msvideo", "Audio Video Interleave (AVI)", FLAG_CONTAINER}, {"video/x-quicktime", "Quicktime", FLAG_CONTAINER}, {"video/quicktime", "Quicktime", FLAG_CONTAINER}, {"video/mj2", "Motion JPEG 2000", FLAG_CONTAINER}, @@ -160,6 +162,7 @@ static const FormatInfo formats[] = { {"video/x-compressed-yuv", N_("CYUV Lossless"), 0}, {"video/x-dirac", "Dirac", 0}, {"video/x-dnxhd", "Digital Nonlinear Extensible High Definition (DNxHD)", 0}, + /* FIXME 0.11: rename to subpicture/x-dvd or so */ {"video/x-dvd-subpicture", "DVD subpicture", 0}, {"video/x-ffv", N_("FFMpeg v1"), 0}, {"video/x-flash-screen", "Flash Screen Video", 0}, @@ -209,17 +212,21 @@ static const FormatInfo formats[] = { {"image/jpeg", "JPEG", 0}, {"image/jng", "JPEG Network Graphics (JNG)", 0}, {"image/png", "PNG", 0}, - {"image/pbm", "PBM", 0}, - {"image/ppm", "PPM", 0}, + {"image/pbm", "Portable BitMap (PBM)", 0}, + {"image/ppm", "Portable PixMap (PPM)", 0}, {"image/svg+xml", "Scalable Vector Graphics (SVG)", 0}, {"image/tiff", "TIFF", 0}, {"image/x-cmu-raster", "CMU Raster Format", 0}, + {"image/x-degas", "DEGAS", 0}, {"image/x-icon", "ICO", 0}, {"image/x-j2c", "JPEG 2000", 0}, {"image/x-jpc", "JPEG 2000", 0}, {"image/jp2", "JPEG 2000", 0}, + {"image/x-pcx", "PCX", 0}, {"image/x-xcf", "XFC", 0}, {"image/x-pixmap", "XPM", 0}, + {"image/x-portable-anymap", "Portable AnyMap (PAM)", 0}, + {"image/x-portable-graymap", "Portable GrayMap (PGM)", 0}, {"image/x-xpixmap", "XPM", 0}, {"image/x-quicktime", "QuickTime Image Format (QTIF)", 0}, {"image/x-sun-raster", "Sun Raster Format (RAS)", 0}, @@ -231,6 +238,7 @@ static const FormatInfo formats[] = { {"application/x-subtitle-tmplayer", N_("TMPlayer subtitle format"), 0}, {"application/x-kate", "Kate", 0}, {"subtitle/x-kate", N_("Kate subtitle format"), 0}, + {"subpicture/x-dvb", "DVB subtitles", 0}, /* add variant field to typefinder? { "application/x-subtitle", N_("subtitle"), 0}, */ /* non-audio/video/container formats */ diff --git a/gst-libs/gst/pbutils/encoding-profile.c b/gst-libs/gst/pbutils/encoding-profile.c new file mode 100644 index 0000000000..b1c8051b6e --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-profile.c @@ -0,0 +1,966 @@ +/* GStreamer encoding profiles library + * Copyright (C) 2009-2010 Edward Hervey + * (C) 2009-2010 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. + */ + +/** + * SECTION:encoding-profile + * @short_description: Encoding profile library + * + * + * + * Functions to create and handle encoding profiles. + * + * + * Encoding profiles describe the media types and settings one wishes to use for + * an encoding process. The top-level profiles are commonly + * #GstEncodingContainerProfile(s) (which contains a user-readable name and + * description along with which container format to use). These, in turn, + * reference one or more #GstEncodingProfile(s) which indicate which encoding + * format should be used on each individual streams. + * + * + * #GstEncodingProfile(s) can be provided to the 'encodebin' element, which will take + * care of selecting and setting up the required elements to produce an output stream + * conforming to the specifications of the profile. + * + * + * Unlike other systems, the encoding profiles do not specify which #GstElement to use + * for the various encoding and muxing steps, but instead relies on specifying the format + * one wishes to use. + * + * + * Encoding profiles can be created at runtime by the application or loaded from (and saved + * to) file using the #GstEncodingTarget API. + * + * + * + * Example: Creating a profile + * + * |[ + * #include + * ... + * GstEncodingProfile * + * create_ogg_theora_profile(void) + *{ + * GstEncodingContainerProfile *prof; + * GstCaps *caps; + * + * caps = gst_caps_from_string("application/ogg"); + * prof = gst_encoding_container_profile_new("Ogg audio/video", + * "Standard OGG/THEORA/VORBIS", + * caps, NULL); + * gst_caps_unref (caps); + * + * caps = gst_caps_from_string("video/x-theora"); + * sprof = gst_encoding_container_profile_add_profile( + * (GstEncodingProfile*) gst_encoding_video_profile_new(caps, NULL, NULL, 0)); + * gst_caps_unref (caps); + * + * caps = gst_caps_from_string("audio/x-vorbis"); + * sprof = gst_encoding_container_profile_add_profile( + * (GstEncodingProfile*) gst_encoding_audio_profile_new(caps, NULL, NULL, 0)); + * gst_caps_unref (caps); + * + * return (GstEncodingProfile*) prof; + *} + * + * + * ]| + * + * + * + * Example: Listing categories, targets and profiles + * + * |[ + * #include + * ... + * GstEncodingProfile *prof; + * GList *categories, *tmpc; + * GList *targets, *tmpt; + * ... + * categories = gst_encoding_target_list_available_categories(); + * + * ... Show available categories to user ... + * + * for (tmpc = categories; tmpc; tmpc = tmpc->next) { + * gchar *category = (gchar *) tmpc->data; + * + * ... and we can list all targets within that category ... + * + * targets = gst_encoding_target_list_all (category); + * + * ... and show a list to our users ... + * + * g_list_foreach (targets, (GFunc) gst_encoding_target_unref, NULL); + * g_list_free (targets); + * } + * + * g_list_foreach (categories, (GFunc) g_free, NULL); + * g_list_free (categories); + * + * ... + * ]| + * + * + * + * Since: 0.10.32 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "encoding-profile.h" +#include "encoding-target.h" + +/* GstEncodingProfile API */ + +struct _GstEncodingProfile +{ + GstMiniObject parent; + + /*< public > */ + gchar *name; + gchar *description; + GstCaps *format; + gchar *preset; + guint presence; + GstCaps *restriction; +}; + +static void string_to_profile_transform (const GValue * src_value, + GValue * dest_value); +static gboolean gst_encoding_profile_deserialize_valfunc (GValue * value, + const gchar * s); + +static void gst_encoding_profile_class_init (GstEncodingProfileClass * klass); +static gpointer gst_encoding_profile_parent_class = NULL; + +static void +gst_encoding_profile_class_intern_init (gpointer klass) +{ + gst_encoding_profile_parent_class = g_type_class_peek_parent (klass); + gst_encoding_profile_class_init ((GstEncodingProfileClass *) klass); +} + +GType +gst_encoding_profile_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) { + GType g_define_type_id = + g_type_register_static_simple (GST_TYPE_MINI_OBJECT, + g_intern_static_string ("GstEncodingProfile"), + sizeof (GstEncodingProfileClass), + (GClassInitFunc) gst_encoding_profile_class_intern_init, + sizeof (GstEncodingProfile), + NULL, + (GTypeFlags) 0); + static GstValueTable gstvtable = { + G_TYPE_NONE, + (GstValueCompareFunc) NULL, + (GstValueSerializeFunc) NULL, + (GstValueDeserializeFunc) gst_encoding_profile_deserialize_valfunc + }; + + gstvtable.type = g_define_type_id; + + /* Register a STRING=>PROFILE GValueTransformFunc */ + g_value_register_transform_func (G_TYPE_STRING, g_define_type_id, + string_to_profile_transform); + /* Register gst-specific GValue functions */ + gst_value_register (&gstvtable); + + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + return g_define_type_id__volatile; +} + +static void +gst_encoding_profile_finalize (GstEncodingProfile * prof) +{ + if (prof->name) + g_free (prof->name); + if (prof->format) + gst_caps_unref (prof->format); + if (prof->preset) + g_free (prof->preset); + if (prof->description) + g_free (prof->description); + if (prof->restriction) + gst_caps_unref (prof->restriction); +} + +static void +gst_encoding_profile_class_init (GstMiniObjectClass * klass) +{ + klass->finalize = + (GstMiniObjectFinalizeFunction) gst_encoding_profile_finalize; +} + +/** + * gst_encoding_profile_get_name: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the name of the profile, can be %NULL. + */ +const gchar * +gst_encoding_profile_get_name (GstEncodingProfile * profile) +{ + return profile->name; +} + +/** + * gst_encoding_profile_get_description: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the description of the profile, can be %NULL. + */ +const gchar * +gst_encoding_profile_get_description (GstEncodingProfile * profile) +{ + return profile->description; +} + +/** + * gst_encoding_profile_get_format: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the #GstCaps corresponding to the media format used in the profile. + */ +const GstCaps * +gst_encoding_profile_get_format (GstEncodingProfile * profile) +{ + return profile->format; +} + +/** + * gst_encoding_profile_get_preset: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the name of the #GstPreset to be used in the profile. + */ +const gchar * +gst_encoding_profile_get_preset (GstEncodingProfile * profile) +{ + return profile->preset; +} + +/** + * gst_encoding_profile_get_presence: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: The number of times the profile is used in its parent + * container profile. If 0, it is not a mandatory stream. + */ +guint +gst_encoding_profile_get_presence (GstEncodingProfile * profile) +{ + return profile->presence; +} + +/** + * gst_encoding_profile_get_restriction: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: The restriction #GstCaps to apply before the encoder + * that will be used in the profile. The fields present in restriction caps are + * properties of the raw stream (that is before encoding), such as height and + * width for video and depth and sampling rate for audio. Does not apply to + * #GstEncodingContainerProfile (since there is no corresponding raw stream). + * Can be %NULL. + */ +const GstCaps * +gst_encoding_profile_get_restriction (GstEncodingProfile * profile) +{ + return profile->restriction; +} + +/** + * gst_encoding_profile_set_name: + * @profile: a #GstEncodingProfile + * @name: the name to set on the profile + * + * Set @name as the given name for the @profile. A copy of @name will be made + * internally. + * + * Since: 0.10.32 + */ +void +gst_encoding_profile_set_name (GstEncodingProfile * profile, const gchar * name) +{ + if (profile->name) + g_free (profile->name); + profile->name = g_strdup (name); +} + +/** + * gst_encoding_profile_set_description: + * @profile: a #GstEncodingProfile + * @description: the description to set on the profile + * + * Set @description as the given description for the @profile. A copy of @description will be made + * internally. + * + * Since: 0.10.32 + */ +void +gst_encoding_profile_set_description (GstEncodingProfile * profile, + const gchar * description) +{ + if (profile->description) + g_free (profile->description); + profile->description = g_strdup (description); +} + +/** + * gst_encoding_profile_set_format: + * @profile: a #GstEncodingProfile + * @format: the media format to use in the profile. + * + * Sets the media format used in the profile. + * + * Since: 0.10.32 + */ +void +gst_encoding_profile_set_format (GstEncodingProfile * profile, GstCaps * format) +{ + if (profile->format) + gst_caps_unref (profile->format); + profile->format = gst_caps_ref (format); +} + +/** + * gst_encoding_profile_set_preset: + * @profile: a #GstEncodingProfile + * @preset: the element preset to use + * + * Sets the preset to use for the profile. + * + * Since: 0.10.32 + */ +void +gst_encoding_profile_set_preset (GstEncodingProfile * profile, + const gchar * preset) +{ + if (profile->preset) + g_free (profile->preset); + profile->preset = g_strdup (preset); +} + +/** + * gst_encoding_profile_set_presence: + * @profile: a #GstEncodingProfile + * @presence: the number of time the profile can be used + * + * Set the number of time the profile is used in its parent + * container profile. If 0, it is not a mandatory stream + * + * Since: 0.10.32 + */ +void +gst_encoding_profile_set_presence (GstEncodingProfile * profile, guint presence) +{ + profile->presence = presence; +} + +/** + * gst_encoding_profile_set_restriction: + * @profile: a #GstEncodingProfile + * @restriction: the restriction to apply + * + * Set the restriction #GstCaps to apply before the encoder + * that will be used in the profile. See gst_encoding_profile_set_restriction() + * for more about restrictions. Does not apply to #GstEncodingContainerProfile. + * + * Since: 0.10.32 + */ +void +gst_encoding_profile_set_restriction (GstEncodingProfile * profile, + GstCaps * restriction) +{ + if (profile->restriction) + gst_caps_unref (profile->restriction); + profile->restriction = restriction; +} + +/* Container profiles */ + +struct _GstEncodingContainerProfile +{ + GstEncodingProfile parent; + + GList *encodingprofiles; +}; + +G_DEFINE_TYPE (GstEncodingContainerProfile, gst_encoding_container_profile, + GST_TYPE_ENCODING_PROFILE); + +static void +gst_encoding_container_profile_init (GstEncodingContainerProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_container_profile_finalize (GstEncodingContainerProfile * prof) +{ + g_list_foreach (prof->encodingprofiles, (GFunc) gst_mini_object_unref, NULL); + g_list_free (prof->encodingprofiles); + + GST_MINI_OBJECT_CLASS (gst_encoding_container_profile_parent_class)->finalize + ((GstMiniObject *) prof); +} + +static void +gst_encoding_container_profile_class_init (GstMiniObjectClass * klass) +{ + klass->finalize = + (GstMiniObjectFinalizeFunction) gst_encoding_container_profile_finalize; +} + +const GList * +gst_encoding_container_profile_get_profiles (GstEncodingContainerProfile * + profile) +{ + return profile->encodingprofiles; +} + +/* Video profiles */ + +struct _GstEncodingVideoProfile +{ + GstEncodingProfile parent; + + guint pass; + gboolean variableframerate; +}; + +G_DEFINE_TYPE (GstEncodingVideoProfile, gst_encoding_video_profile, + GST_TYPE_ENCODING_PROFILE); + +static void +gst_encoding_video_profile_init (GstEncodingVideoProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_video_profile_class_init (GstMiniObjectClass * klass) +{ +} + +/** + * gst_encoding_video_profile_get_pass: + * @prof: a #GstEncodingVideoProfile + * + * Since: 0.10.32 + * + * Returns: The pass number if this is part of a multi-pass profile. Starts at + * 1 for multi-pass. 0 if this is not a multi-pass profile + **/ +guint +gst_encoding_video_profile_get_pass (GstEncodingVideoProfile * prof) +{ + return prof->pass; +} + +/** + * gst_encoding_video_profile_get_variableframerate: + * @prof: a #GstEncodingVideoProfile + * + * Since: 0.10.32 + * + * Returns: Whether non-constant video framerate is allowed for encoding. + */ +gboolean +gst_encoding_video_profile_get_variableframerate (GstEncodingVideoProfile * + prof) +{ + return prof->variableframerate; +} + +/** + * gst_encoding_video_profile_set_pass: + * @prof: a #GstEncodingVideoProfile + * @pass: the pass number for this profile + * + * Sets the pass number of this video profile. The first pass profile should have + * this value set to 1. If this video profile isn't part of a multi-pass profile, + * you may set it to 0 (the default value). + * + * Since: 0.10.32 + */ +void +gst_encoding_video_profile_set_pass (GstEncodingVideoProfile * prof, guint pass) +{ + prof->pass = pass; +} + +/** + * gst_encoding_video_profile_set_variableframerate: + * @prof: a #GstEncodingVideoProfile + * @variableframerate: a boolean + * + * If set to %TRUE, then the incoming streamm will be allowed to have non-constant + * framerate. If set to %FALSE (default value), then the incoming stream will + * be normalized by dropping/duplicating frames in order to produce a + * constance framerate. + * + * Since: 0.10.32 + */ +void +gst_encoding_video_profile_set_variableframerate (GstEncodingVideoProfile * + prof, gboolean variableframerate) +{ + prof->variableframerate = variableframerate; +} + +/* Audio profiles */ + +struct _GstEncodingAudioProfile +{ + GstEncodingProfile parent; +}; + +G_DEFINE_TYPE (GstEncodingAudioProfile, gst_encoding_audio_profile, + GST_TYPE_ENCODING_PROFILE); + +static void +gst_encoding_audio_profile_init (GstEncodingAudioProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_audio_profile_class_init (GstMiniObjectClass * klass) +{ +} + +static inline gboolean +_gst_caps_is_equal_safe (GstCaps * a, GstCaps * b) +{ + if (a == b) + return TRUE; + if ((a == NULL) || (b == NULL)) + return FALSE; + return gst_caps_is_equal (a, b); +} + +static gint +_compare_container_encoding_profiles (GstEncodingContainerProfile * ca, + GstEncodingContainerProfile * cb) +{ + GList *tmp; + + if (g_list_length (ca->encodingprofiles) != + g_list_length (cb->encodingprofiles)) + return -1; + + for (tmp = ca->encodingprofiles; tmp; tmp = tmp->next) { + GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; + if (!gst_encoding_container_profile_contains_profile (ca, prof)) + return -1; + } + + return 0; +} + +static gint +_compare_encoding_profiles (const GstEncodingProfile * a, + const GstEncodingProfile * b) +{ + if ((G_TYPE_FROM_INSTANCE (a) != G_TYPE_FROM_INSTANCE (b)) || + !_gst_caps_is_equal_safe (a->format, b->format) || + (g_strcmp0 (a->preset, b->preset) != 0) || + (g_strcmp0 (a->name, b->name) != 0) || + (g_strcmp0 (a->description, b->description) != 0)) + return -1; + + if (GST_IS_ENCODING_CONTAINER_PROFILE (a)) + return + _compare_container_encoding_profiles (GST_ENCODING_CONTAINER_PROFILE + (a), GST_ENCODING_CONTAINER_PROFILE (b)); + + if (GST_IS_ENCODING_VIDEO_PROFILE (a)) { + GstEncodingVideoProfile *va = (GstEncodingVideoProfile *) a; + GstEncodingVideoProfile *vb = (GstEncodingVideoProfile *) b; + + if ((va->pass != vb->pass) + || (va->variableframerate != vb->variableframerate)) + return -1; + } + + return 0; +} + +/** + * gst_encoding_container_profile_contains_profile: + * @container: a #GstEncodingContainerProfile + * @profile: a #GstEncodingProfile + * + * Checks if @container contains a #GstEncodingProfile identical to + * @profile. + * + * Since: 0.10.32 + * + * Returns: %TRUE if @container contains a #GstEncodingProfile identical + * to @profile, else %FALSE. + */ +gboolean +gst_encoding_container_profile_contains_profile (GstEncodingContainerProfile * + container, GstEncodingProfile * profile) +{ + g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + return (g_list_find_custom (container->encodingprofiles, profile, + (GCompareFunc) _compare_encoding_profiles) != NULL); +} + +/** + * gst_encoding_container_profile_add_profile: + * @container: the #GstEncodingContainerProfile to use + * @profile: the #GstEncodingProfile to add. + * + * Add a #GstEncodingProfile to the list of profiles handled by @container. + * + * No copy of @profile will be made, if you wish to use it elsewhere after this + * method you should increment its reference count. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the @stream was properly added, else %FALSE. + */ +gboolean +gst_encoding_container_profile_add_profile (GstEncodingContainerProfile * + container, GstEncodingProfile * profile) +{ + g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + if (g_list_find_custom (container->encodingprofiles, profile, + (GCompareFunc) _compare_encoding_profiles)) { + GST_ERROR + ("Encoding profile already contains an identical GstEncodingProfile"); + return FALSE; + } + + container->encodingprofiles = + g_list_append (container->encodingprofiles, profile); + + return TRUE; +} + +static GstEncodingProfile * +common_creation (GType objtype, GstCaps * format, const gchar * preset, + const gchar * name, const gchar * description, GstCaps * restriction, + guint presence) +{ + GstEncodingProfile *prof; + + prof = (GstEncodingProfile *) gst_mini_object_new (objtype); + + if (name) + prof->name = g_strdup (name); + if (description) + prof->description = g_strdup (description); + if (preset) + prof->preset = g_strdup (preset); + if (format) + prof->format = gst_caps_ref (format); + if (restriction) + prof->restriction = gst_caps_ref (restriction); + prof->presence = presence; + + return prof; +} + +/** + * gst_encoding_container_profile_new: + * @name: The name of the container profile, can be %NULL + * @description: The description of the container profile, can be %NULL + * @format: The format to use for this profile + * @preset: The preset to use for this profile + * + * Creates a new #GstEncodingContainerProfile. + * + * Since: 0.10.32 + * + * Returns: The newly created #GstEncodingContainerProfile. + */ +GstEncodingContainerProfile * +gst_encoding_container_profile_new (const gchar * name, + const gchar * description, GstCaps * format, const gchar * preset) +{ + g_return_val_if_fail (GST_IS_CAPS (format), NULL); + + return (GstEncodingContainerProfile *) + common_creation (GST_TYPE_ENCODING_CONTAINER_PROFILE, format, preset, + name, description, NULL, 0); +} + +/** + * gst_encoding_video_profile_new: + * @format: the #GstCaps + * @preset: the preset(s) to use on the encoder, can be #NULL + * @restriction: the #GstCaps used to restrict the input to the encoder, can be + * NULL. See gst_encoding_profile_get_restriction() for more details. + * @presence: the number of time this stream must be used. 0 means any number of + * times (including never) + * + * Creates a new #GstEncodingVideoProfile + * + * All provided allocatable arguments will be internally copied, so can be + * safely freed/unreferenced after calling this method. + * + * If you wish to control the pass number (in case of multi-pass scenarios), + * please refer to the gst_encoding_video_profile_set_pass() documentation. + * + * If you wish to use/force a constant framerate please refer to the + * gst_encoding_video_profile_set_variableframerate() documentation. + * + * Since: 0.10.32 + * + * Returns: the newly created #GstEncodingVideoProfile. + */ +GstEncodingVideoProfile * +gst_encoding_video_profile_new (GstCaps * format, const gchar * preset, + GstCaps * restriction, guint presence) +{ + return (GstEncodingVideoProfile *) + common_creation (GST_TYPE_ENCODING_VIDEO_PROFILE, format, preset, NULL, + NULL, restriction, presence); +} + +/** + * gst_encoding_audio_profile_new: + * @format: the #GstCaps + * @preset: the preset(s) to use on the encoder, can be #NULL + * @restriction: the #GstCaps used to restrict the input to the encoder, can be + * NULL. See gst_encoding_profile_get_restriction() for more details. + * @presence: the number of time this stream must be used. 0 means any number of + * times (including never) + * + * Creates a new #GstEncodingAudioProfile + * + * All provided allocatable arguments will be internally copied, so can be + * safely freed/unreferenced after calling this method. + * + * Since: 0.10.32 + * + * Returns: the newly created #GstEncodingAudioProfile. + */ +GstEncodingAudioProfile * +gst_encoding_audio_profile_new (GstCaps * format, const gchar * preset, + GstCaps * restriction, guint presence) +{ + return (GstEncodingAudioProfile *) + common_creation (GST_TYPE_ENCODING_AUDIO_PROFILE, format, preset, NULL, + NULL, restriction, presence); +} + + +/** + * gst_encoding_profile_is_equal: + * @a: a #GstEncodingProfile + * @b: a #GstEncodingProfile + * + * Checks whether the two #GstEncodingProfile are equal + * + * Since: 0.10.32 + * + * Returns: %TRUE if @a and @b are equal, else %FALSE. + */ +gboolean +gst_encoding_profile_is_equal (GstEncodingProfile * a, GstEncodingProfile * b) +{ + return (_compare_encoding_profiles (a, b) == 0); +} + + +/** + * gst_encoding_profile_get_input_caps: + * @profile: a #GstEncodingProfile + * + * Computes the full output caps that this @profile will be able to consume. + * + * Since: 0.10.32 + * + * Returns: The full caps the given @profile can consume. Call gst_caps_unref() + * when you are done with the caps. + */ +GstCaps * +gst_encoding_profile_get_input_caps (GstEncodingProfile * profile) +{ + GstCaps *out, *tmp; + GList *ltmp; + GstStructure *st, *outst; + GQuark out_name; + guint i, len; + const GstCaps *fcaps; + + if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { + GstCaps *res = gst_caps_new_empty (); + + for (ltmp = GST_ENCODING_CONTAINER_PROFILE (profile)->encodingprofiles; + ltmp; ltmp = ltmp->next) { + GstEncodingProfile *sprof = (GstEncodingProfile *) ltmp->data; + gst_caps_merge (res, gst_encoding_profile_get_input_caps (sprof)); + } + return res; + } + + fcaps = profile->format; + + /* fast-path */ + if ((profile->restriction == NULL) || gst_caps_is_any (profile->restriction)) + return gst_caps_copy (fcaps); + + /* Combine the format with the restriction caps */ + outst = gst_caps_get_structure (fcaps, 0); + out_name = gst_structure_get_name_id (outst); + tmp = gst_caps_new_empty (); + len = gst_caps_get_size (profile->restriction); + + for (i = 0; i < len; i++) { + st = gst_structure_copy (gst_caps_get_structure (profile->restriction, i)); + st->name = out_name; + gst_caps_append_structure (tmp, st); + } + + out = gst_caps_intersect (tmp, fcaps); + gst_caps_unref (tmp); + + return out; +} + +/** + * gst_encoding_profile_get_type_nick: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the human-readable name of the type of @profile. + */ +const gchar * +gst_encoding_profile_get_type_nick (GstEncodingProfile * profile) +{ + if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) + return "container"; + if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) + return "video"; + if (GST_IS_ENCODING_AUDIO_PROFILE (profile)) + return "audio"; + return NULL; +} + +/** + * gst_encoding_profile_find: + * @targetname: (transfer none): The name of the target + * @profilename: (transfer none): The name of the profile + * @category: (transfer none) (allow-none): The target category. Can be %NULL + * + * Find the #GstEncodingProfile with the specified name and category. + * + * Returns: (transfer full): The matching #GstEncodingProfile or %NULL. + * + * Since: 0.10.32 + */ +GstEncodingProfile * +gst_encoding_profile_find (const gchar * targetname, const gchar * profilename, + const gchar * category) +{ + GstEncodingProfile *res = NULL; + GstEncodingTarget *target; + + g_return_val_if_fail (targetname != NULL, NULL); + g_return_val_if_fail (profilename != NULL, NULL); + + /* FIXME : how do we handle profiles named the same in several + * categories but of which only one has the required profile ? */ + target = gst_encoding_target_load (targetname, category, NULL); + if (target) { + res = gst_encoding_target_get_profile (target, profilename); + gst_encoding_target_unref (target); + } + + return res; +} + +static GstEncodingProfile * +combo_search (const gchar * pname) +{ + GstEncodingProfile *res; + gchar **split; + + /* Splitup */ + split = g_strsplit (pname, "/", 2); + if (g_strv_length (split) != 2) + return NULL; + + res = gst_encoding_profile_find (split[0], split[1], NULL); + + g_strfreev (split); + + return res; +} + +/* GValue transform function */ +static void +string_to_profile_transform (const GValue * src_value, GValue * dest_value) +{ + const gchar *profilename; + GstEncodingProfile *profile; + + profilename = g_value_get_string (src_value); + + profile = combo_search (profilename); + + if (profile) + gst_value_take_mini_object (dest_value, (GstMiniObject *) profile); +} + +static gboolean +gst_encoding_profile_deserialize_valfunc (GValue * value, const gchar * s) +{ + GstEncodingProfile *profile; + + profile = combo_search (s); + + if (profile) { + gst_value_take_mini_object (value, (GstMiniObject *) profile); + return TRUE; + } + + return FALSE; +} diff --git a/gst-libs/gst/pbutils/encoding-profile.h b/gst-libs/gst/pbutils/encoding-profile.h new file mode 100644 index 0000000000..86beccaf86 --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-profile.h @@ -0,0 +1,187 @@ +/* GStreamer encoding profiles library + * Copyright (C) 2009-2010 Edward Hervey + * (C) 2009-2010 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_PROFILE_H__ +#define __GST_PROFILE_H__ + +#include + +G_BEGIN_DECLS + +#include + +/** + * GstEncodingProfile: + * + * The opaque base class object for all encoding profiles. This contains generic + * information like name, description, format and preset. + * + * Since: 0.10.32 + */ + +#define GST_TYPE_ENCODING_PROFILE \ + (gst_encoding_profile_get_type ()) +#define GST_ENCODING_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_PROFILE, GstEncodingProfile)) +#define GST_IS_ENCODING_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_PROFILE)) +typedef struct _GstEncodingProfile GstEncodingProfile; +typedef GstMiniObjectClass GstEncodingProfileClass; +GType gst_encoding_profile_get_type (void); + + + +/** + * GstEncodingContainerProfile: + * + * Encoding profiles for containers. Keeps track of a list of #GstEncodingProfile + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_CONTAINER_PROFILE \ + (gst_encoding_container_profile_get_type ()) +#define GST_ENCODING_CONTAINER_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_CONTAINER_PROFILE, GstEncodingContainerProfile)) +#define GST_IS_ENCODING_CONTAINER_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_CONTAINER_PROFILE)) +typedef struct _GstEncodingContainerProfile GstEncodingContainerProfile; +typedef GstEncodingProfileClass GstEncodingContainerProfileClass; +GType gst_encoding_container_profile_get_type (void); + + + +/** + * GstEncodingVideoProfile: + * + * Variant of #GstEncodingProfile for video streams, allows specifying the @pass. + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_VIDEO_PROFILE \ + (gst_encoding_video_profile_get_type ()) +#define GST_ENCODING_VIDEO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_VIDEO_PROFILE, GstEncodingVideoProfile)) +#define GST_IS_ENCODING_VIDEO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_VIDEO_PROFILE)) +typedef struct _GstEncodingVideoProfile GstEncodingVideoProfile; +typedef GstEncodingProfileClass GstEncodingVideoProfileClass; +GType gst_encoding_video_profile_get_type (void); + + + +/** + * GstEncodingAudioProfile: + * + * Variant of #GstEncodingProfile for audio streams. + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_AUDIO_PROFILE \ + (gst_encoding_audio_profile_get_type ()) +#define GST_ENCODING_AUDIO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_AUDIO_PROFILE, GstEncodingAudioProfile)) +#define GST_IS_ENCODING_AUDIO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_AUDIO_PROFILE)) +typedef struct _GstEncodingAudioProfile GstEncodingAudioProfile; +typedef GstEncodingProfileClass GstEncodingAudioProfileClass; +GType gst_encoding_audio_profile_get_type (void); + + + +/* GstEncodingProfile API */ + +/** + * gst_encoding_profile_unref: + * @profile: a #GstEncodingProfile + * + * Decreases the reference count of the @profile, possibly freeing the @profile. + * + * Since: 0.10.32 + */ +#define gst_encoding_profile_unref(profile) (gst_mini_object_unref ((GstMiniObject*) profile)) + +/** + * gst_encoding_profile_ref: + * @profile: a #GstEncodingProfile + * + * Increases the reference count of the @profile. + * + * Since: 0.10.32 + */ +#define gst_encoding_profile_ref(profile) (gst_mini_object_ref ((GstMiniObject*) profile)) + +const gchar * gst_encoding_profile_get_name(GstEncodingProfile *profile); +const gchar * gst_encoding_profile_get_description(GstEncodingProfile *profile); +const GstCaps * gst_encoding_profile_get_format(GstEncodingProfile *profile); +const gchar * gst_encoding_profile_get_preset(GstEncodingProfile *profile); +guint gst_encoding_profile_get_presence(GstEncodingProfile *profile); +const GstCaps * gst_encoding_profile_get_restriction(GstEncodingProfile *profile); + +void gst_encoding_profile_set_name(GstEncodingProfile *profile, const gchar *name); +void gst_encoding_profile_set_description(GstEncodingProfile *profile, const gchar *description); +void gst_encoding_profile_set_format(GstEncodingProfile *profile, GstCaps *format); +void gst_encoding_profile_set_preset(GstEncodingProfile *profile, const gchar *preset); +void gst_encoding_profile_set_restriction(GstEncodingProfile *profile, GstCaps *restriction); +void gst_encoding_profile_set_presence(GstEncodingProfile *profile, guint presence); + +gboolean gst_encoding_profile_is_equal (GstEncodingProfile *a, + GstEncodingProfile *b); +GstCaps * gst_encoding_profile_get_input_caps (GstEncodingProfile *profile); + +const gchar *gst_encoding_profile_get_type_nick (GstEncodingProfile *profile); + +GstEncodingProfile * gst_encoding_profile_find (const gchar *targetname, + const gchar *profilename, + const gchar *category); + +/* GstEncodingContainerProfile API */ +gboolean gst_encoding_container_profile_add_profile (GstEncodingContainerProfile *container, + GstEncodingProfile *profile); +gboolean gst_encoding_container_profile_contains_profile (GstEncodingContainerProfile * container, + GstEncodingProfile *profile); +const GList *gst_encoding_container_profile_get_profiles (GstEncodingContainerProfile *profile); + + +GstEncodingContainerProfile * gst_encoding_container_profile_new (const gchar *name, + const gchar *description, + GstCaps *format, + const gchar *preset); + + +/* Invidual stream encodingprofile API */ +GstEncodingVideoProfile * gst_encoding_video_profile_new (GstCaps *format, + const gchar *preset, + GstCaps *restriction, + guint presence); +GstEncodingAudioProfile * gst_encoding_audio_profile_new (GstCaps *format, + const gchar *preset, + GstCaps *restriction, + guint presence); + +guint gst_encoding_video_profile_get_pass (GstEncodingVideoProfile *prof); +gboolean gst_encoding_video_profile_get_variableframerate (GstEncodingVideoProfile *prof); + +void gst_encoding_video_profile_set_pass (GstEncodingVideoProfile *prof, + guint pass); +void gst_encoding_video_profile_set_variableframerate (GstEncodingVideoProfile *prof, + gboolean variableframerate); +G_END_DECLS + +#endif /* __GST_PROFILE_H__ */ diff --git a/gst-libs/gst/pbutils/encoding-target.c b/gst-libs/gst/pbutils/encoding-target.c new file mode 100644 index 0000000000..b7e7ad545b --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-target.c @@ -0,0 +1,1204 @@ +/* GStreamer encoding profile registry + * Copyright (C) 2010 Edward Hervey + * (C) 2010 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 +#include +#include "encoding-target.h" + +/* + * File format + * + * GKeyFile style. + * + * [GStreamer Encoding Target] + * name : + * category : + * description : #translatable + * + * [profile-] + * name : + * description : #optional + * format : + * preset : + * + * [streamprofile-] + * parent : [,..] + * type : # "audio", "video", "text" + * format : + * preset : + * restriction : + * presence : + * pass : + * variableframerate : + * */ + +/* + * Location of profile files + * + * $GST_DATADIR/gstreamer-GST_MAJORMINOR/encoding-profile + * $HOME/gstreamer-GST_MAJORMINOR/encoding-profile + * + * Naming convention + * $(target.category)/$(target.name).gep + * + * Naming restrictions: + * lowercase ASCII letter for the first character + * Same for all other characters + numerics + hyphens + */ + + +#define GST_ENCODING_TARGET_HEADER "GStreamer Encoding Target" +#define GST_ENCODING_TARGET_DIRECTORY "encoding-profiles" +#define GST_ENCODING_TARGET_SUFFIX ".gep" + +struct _GstEncodingTarget +{ + GstMiniObject parent; + + gchar *name; + gchar *category; + gchar *description; + GList *profiles; + + /*< private > */ + gchar *keyfile; +}; + +G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, GST_TYPE_MINI_OBJECT); + +static void +gst_encoding_target_init (GstEncodingTarget * target) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_target_finalize (GstEncodingTarget * target) +{ + GST_DEBUG ("Finalizing"); + + if (target->name) + g_free (target->name); + if (target->category) + g_free (target->category); + if (target->description) + g_free (target->description); + + g_list_foreach (target->profiles, (GFunc) gst_mini_object_unref, NULL); + g_list_free (target->profiles); +} + +static void +gst_encoding_target_class_init (GstMiniObjectClass * klass) +{ + klass->finalize = + (GstMiniObjectFinalizeFunction) gst_encoding_target_finalize; +} + +/** + * gst_encoding_target_get_name: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: (transfer none): The name of the @target. + */ +const gchar * +gst_encoding_target_get_name (GstEncodingTarget * target) +{ + return target->name; +} + +/** + * gst_encoding_target_get_category: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: (transfer none): The category of the @target. For example: + * #GST_ENCODING_CATEGORY_DEVICE. + */ +const gchar * +gst_encoding_target_get_category (GstEncodingTarget * target) +{ + return target->category; +} + +/** + * gst_encoding_target_get_description: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: (transfer none): The description of the @target. + */ +const gchar * +gst_encoding_target_get_description (GstEncodingTarget * target) +{ + return target->description; +} + +/** + * gst_encoding_target_get_profiles: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: (transfer none) (element-type Gst.EncodingProfile): A list of + * #GstEncodingProfile(s) this @target handles. + */ +const GList * +gst_encoding_target_get_profiles (GstEncodingTarget * target) +{ + return target->profiles; +} + +/** + * gst_encoding_target_get_profile: + * @target: a #GstEncodingTarget + * @name: the name of the profile to retrieve + * + * Since: 0.10.32 + * + * Returns: (transfer full): The matching #GstEncodingProfile, or %NULL. + */ +GstEncodingProfile * +gst_encoding_target_get_profile (GstEncodingTarget * target, const gchar * name) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), NULL); + g_return_val_if_fail (name != NULL, NULL); + + for (tmp = target->profiles; tmp; tmp = tmp->next) { + GstEncodingProfile *tprof = (GstEncodingProfile *) tmp->data; + + if (!g_strcmp0 (gst_encoding_profile_get_name (tprof), name)) { + gst_encoding_profile_ref (tprof); + return tprof; + } + } + + return NULL; +} + +static inline gboolean +validate_name (const gchar * name) +{ + guint i, len; + + len = strlen (name); + if (len == 0) + return FALSE; + + /* First character can only be a lower case ASCII character */ + if (!g_ascii_isalpha (name[0]) || !g_ascii_islower (name[0])) + return FALSE; + + /* All following characters can only by: + * either a lower case ASCII character + * or an hyphen + * or a numeric */ + for (i = 1; i < len; i++) { + /* if uppercase ASCII letter, return */ + if (g_ascii_isupper (name[i])) + return FALSE; + /* if a digit, continue */ + if (g_ascii_isdigit (name[i])) + continue; + /* if an hyphen, continue */ + if (name[i] == '-') + continue; + /* remaining should only be ascii letters */ + if (!g_ascii_isalpha (name[i])) + return FALSE; + } + + return TRUE; +} + +/** + * gst_encoding_target_new: + * @name: The name of the target. + * @category: (transfer none): The name of the category to which this @target + * belongs. For example: #GST_ENCODING_CATEGORY_DEVICE. + * @description: (transfer none): A description of #GstEncodingTarget in the + * current locale. + * @profiles: (transfer none) (element-type Gst.EncodingProfile): A #GList of + * #GstEncodingProfile. + * + * Creates a new #GstEncodingTarget. + * + * The name and category can only consist of lowercase ASCII letters for the + * first character, followed by either lowercase ASCII letters, digits or + * hyphens ('-'). + * + * The @category should be one of the existing + * well-defined categories, like #GST_ENCODING_CATEGORY_DEVICE, but it + * can be a application or user specific category if + * needed. + * + * Since: 0.10.32 + * + * Returns: (transfer full): The newly created #GstEncodingTarget or %NULL if + * there was an error. + */ + +GstEncodingTarget * +gst_encoding_target_new (const gchar * name, const gchar * category, + const gchar * description, const GList * profiles) +{ + GstEncodingTarget *res; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (category != NULL, NULL); + g_return_val_if_fail (description != NULL, NULL); + + /* Validate name */ + if (!validate_name (name)) + goto invalid_name; + if (!validate_name (category)) + goto invalid_category; + + res = (GstEncodingTarget *) gst_mini_object_new (GST_TYPE_ENCODING_TARGET); + res->name = g_strdup (name); + res->category = g_strdup (category); + res->description = g_strdup (description); + + while (profiles) { + GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data; + + res->profiles = + g_list_append (res->profiles, gst_encoding_profile_ref (prof)); + profiles = profiles->next; + } + + return res; + +invalid_name: + { + GST_ERROR ("Invalid name for encoding target : '%s'", name); + return NULL; + } + +invalid_category: + { + GST_ERROR ("Invalid name for encoding category : '%s'", category); + return NULL; + } +} + +/** + * gst_encoding_target_add_profile: + * @target: the #GstEncodingTarget to add a profile to + * @profile: (transfer full): the #GstEncodingProfile to add + * + * Adds the given @profile to the @target. Each added profile must have + * a unique name within the profile. + * + * The @target will steal a reference to the @profile. If you wish to use + * the profile after calling this method, you should increase its reference + * count. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the profile was added, else %FALSE. + **/ + +gboolean +gst_encoding_target_add_profile (GstEncodingTarget * target, + GstEncodingProfile * profile) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE); + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + /* Make sure profile isn't already controlled by this target */ + for (tmp = target->profiles; tmp; tmp = tmp->next) { + GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; + + if (!g_strcmp0 (gst_encoding_profile_get_name (profile), + gst_encoding_profile_get_name (prof))) { + GST_WARNING ("Profile already present in target"); + return FALSE; + } + } + + target->profiles = g_list_append (target->profiles, profile); + + return TRUE; +} + +static gboolean +serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof, + const gchar * profilename, guint id) +{ + gchar *sprofgroupname; + gchar *tmpc; + const GstCaps *format, *restriction; + const gchar *preset, *name, *description; + + sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id); + + /* Write the parent profile */ + g_key_file_set_value (out, sprofgroupname, "parent", profilename); + + g_key_file_set_value (out, sprofgroupname, "type", + gst_encoding_profile_get_type_nick (sprof)); + + format = gst_encoding_profile_get_format (sprof); + if (format) { + tmpc = gst_caps_to_string (format); + g_key_file_set_value (out, sprofgroupname, "format", tmpc); + g_free (tmpc); + } + + name = gst_encoding_profile_get_name (sprof); + if (name) + g_key_file_set_string (out, sprofgroupname, "name", name); + + description = gst_encoding_profile_get_description (sprof); + if (description) + g_key_file_set_string (out, sprofgroupname, "description", description); + + preset = gst_encoding_profile_get_preset (sprof); + if (preset) + g_key_file_set_string (out, sprofgroupname, "preset", preset); + + restriction = gst_encoding_profile_get_restriction (sprof); + if (restriction) { + tmpc = gst_caps_to_string (restriction); + g_key_file_set_value (out, sprofgroupname, "restriction", tmpc); + g_free (tmpc); + } + g_key_file_set_integer (out, sprofgroupname, "presence", + gst_encoding_profile_get_presence (sprof)); + + if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) { + GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof; + + g_key_file_set_integer (out, sprofgroupname, "pass", + gst_encoding_video_profile_get_pass (vp)); + g_key_file_set_boolean (out, sprofgroupname, "variableframerate", + gst_encoding_video_profile_get_variableframerate (vp)); + } + + g_free (sprofgroupname); + return TRUE; +} + +static gchar * +get_locale (void) +{ + const char *loc = NULL; + gchar *ret; + +#ifdef ENABLE_NLS +#if defined(LC_MESSAGES) + loc = setlocale (LC_MESSAGES, NULL); + GST_LOG ("LC_MESSAGES: %s", GST_STR_NULL (loc)); +#elif defined(LC_ALL) + loc = setlocale (LC_ALL, NULL); + GST_LOG ("LC_ALL: %s", GST_STR_NULL (loc)); +#else + GST_LOG ("Neither LC_ALL nor LC_MESSAGES defined"); +#endif +#else /* !ENABLE_NLS */ + GST_LOG ("i18n disabled"); +#endif + + if (loc == NULL || g_ascii_strncasecmp (loc, "en", 2) == 0) + return NULL; + + /* en_GB.UTF-8 => en */ + ret = g_ascii_strdown (loc, -1); + ret = g_strcanon (ret, "abcdefghijklmnopqrstuvwxyz", '\0'); + GST_LOG ("using locale: %s", ret); + return ret; +} + +/* Serialize the top-level profiles + * Note: They don't have to be containerprofiles */ +static gboolean +serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof) +{ + gchar *profgroupname; + const GList *tmp; + guint i; + const gchar *profname, *profdesc, *profpreset, *proftype; + const GstCaps *profformat, *profrestriction; + + profname = gst_encoding_profile_get_name (prof); + profdesc = gst_encoding_profile_get_description (prof); + profformat = gst_encoding_profile_get_format (prof); + profpreset = gst_encoding_profile_get_preset (prof); + proftype = gst_encoding_profile_get_type_nick (prof); + profrestriction = gst_encoding_profile_get_restriction (prof); + + profgroupname = g_strdup_printf ("profile-%s", profname); + + g_key_file_set_string (out, profgroupname, "name", profname); + + g_key_file_set_value (out, profgroupname, "type", + gst_encoding_profile_get_type_nick (prof)); + + if (profdesc) { + gchar *locale; + + locale = get_locale (); + if (locale != NULL) { + g_key_file_set_locale_string (out, profgroupname, "description", + locale, profdesc); + g_free (locale); + } else { + g_key_file_set_string (out, profgroupname, "description", profdesc); + } + } + if (profformat) { + gchar *tmpc = gst_caps_to_string (profformat); + g_key_file_set_string (out, profgroupname, "format", tmpc); + g_free (tmpc); + } + if (profpreset) + g_key_file_set_string (out, profgroupname, "preset", profpreset); + + /* stream profiles */ + if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) { + for (tmp = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (prof)), i = 0; tmp; + tmp = tmp->next, i++) { + GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; + + if (!serialize_stream_profiles (out, sprof, profname, i)) + return FALSE; + } + } + g_free (profgroupname); + return TRUE; +} + +static gboolean +serialize_target (GKeyFile * out, GstEncodingTarget * target) +{ + GList *tmp; + + g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "name", target->name); + g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "category", + target->category); + g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description", + target->description); + + for (tmp = target->profiles; tmp; tmp = tmp->next) { + GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; + if (!serialize_encoding_profile (out, prof)) + return FALSE; + } + + return TRUE; +} + +/** + * parse_encoding_profile: + * @in: a #GKeyFile + * @parentprofilename: the parent profile name (including 'profile-' or 'streamprofile-' header) + * @profilename: the profile name group to parse + * @nbgroups: the number of top-level groups + * @groups: the top-level groups + */ +static GstEncodingProfile * +parse_encoding_profile (GKeyFile * in, gchar * parentprofilename, + gchar * profilename, gsize nbgroups, gchar ** groups) +{ + GstEncodingProfile *sprof = NULL; + gchar **parent; + gchar *proftype, *format, *preset, *restriction, *pname, *description; + GstCaps *formatcaps = NULL; + GstCaps *restrictioncaps = NULL; + gboolean variableframerate; + gint pass, presence; + gsize i, nbencprofiles; + + GST_DEBUG ("parentprofilename : %s , profilename : %s", + parentprofilename, profilename); + + if (parentprofilename) { + gboolean found = FALSE; + + parent = + g_key_file_get_string_list (in, profilename, "parent", + &nbencprofiles, NULL); + if (!parent || !nbencprofiles) { + return NULL; + } + + /* Check if this streamprofile is used in */ + for (i = 0; i < nbencprofiles; i++) { + if (!g_strcmp0 (parent[i], parentprofilename)) { + found = TRUE; + break; + } + } + g_strfreev (parent); + + if (!found) { + GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'", + profilename, parentprofilename); + return NULL; + } + } + + pname = g_key_file_get_value (in, profilename, "name", NULL); + + /* First try to get localized description */ + { + gchar *locale; + + locale = get_locale (); + if (locale != NULL) { + /* will try to fall back to untranslated string if no translation found */ + description = g_key_file_get_locale_string (in, profilename, + "description", locale, NULL); + g_free (locale); + } else { + description = + g_key_file_get_string (in, profilename, "description", NULL); + } + } + + /* Note: a missing description is normal for non-container profiles */ + if (description == NULL) { + GST_LOG ("Missing 'description' field for streamprofile %s", profilename); + } + + /* Parse the remaining fields */ + proftype = g_key_file_get_value (in, profilename, "type", NULL); + if (!proftype) { + GST_WARNING ("Missing 'type' field for streamprofile %s", profilename); + return NULL; + } + + format = g_key_file_get_value (in, profilename, "format", NULL); + if (format) { + formatcaps = gst_caps_from_string (format); + g_free (format); + } + + preset = g_key_file_get_value (in, profilename, "preset", NULL); + + restriction = g_key_file_get_value (in, profilename, "restriction", NULL); + if (restriction) { + restrictioncaps = gst_caps_from_string (restriction); + g_free (restriction); + } + + presence = g_key_file_get_integer (in, profilename, "presence", NULL); + pass = g_key_file_get_integer (in, profilename, "pass", NULL); + variableframerate = + g_key_file_get_boolean (in, profilename, "variableframerate", NULL); + + /* Build the streamprofile ! */ + if (!g_strcmp0 (proftype, "container")) { + GstEncodingProfile *pprof; + + sprof = + (GstEncodingProfile *) gst_encoding_container_profile_new (pname, + description, formatcaps, preset); + /* Now look for the stream profiles */ + for (i = 0; i < nbgroups; i++) { + if (!g_ascii_strncasecmp (groups[i], "streamprofile-", 13)) { + pprof = parse_encoding_profile (in, pname, groups[i], nbgroups, groups); + if (pprof) { + gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) sprof, pprof); + } + } + } + } else if (!g_strcmp0 (proftype, "video")) { + sprof = + (GstEncodingProfile *) gst_encoding_video_profile_new (formatcaps, + preset, restrictioncaps, presence); + gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile + *) sprof, variableframerate); + gst_encoding_video_profile_set_pass ((GstEncodingVideoProfile *) sprof, + pass); + } else if (!g_strcmp0 (proftype, "audio")) { + sprof = + (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps, + preset, restrictioncaps, presence); + } else + GST_ERROR ("Unknown profile format '%s'", proftype); + + if (restrictioncaps) + gst_caps_unref (restrictioncaps); + if (formatcaps) + gst_caps_unref (formatcaps); + + if (pname) + g_free (pname); + if (description) + g_free (description); + if (preset) + g_free (preset); + if (proftype) + g_free (proftype); + + return sprof; +} + +static GstEncodingTarget * +parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname, + gchar * description) +{ + GstEncodingTarget *res = NULL; + GstEncodingProfile *prof; + gchar **groups; + gsize i, nbgroups; + + res = gst_encoding_target_new (targetname, categoryname, description, NULL); + + /* Figure out the various profiles */ + groups = g_key_file_get_groups (in, &nbgroups); + for (i = 0; i < nbgroups; i++) { + if (!g_ascii_strncasecmp (groups[i], "profile-", 8)) { + prof = parse_encoding_profile (in, NULL, groups[i], nbgroups, groups); + if (prof) + gst_encoding_target_add_profile (res, prof); + } + } + + g_strfreev (groups); + + if (targetname) + g_free (targetname); + if (categoryname) + g_free (categoryname); + if (description) + g_free (description); + + return res; +} + +static GKeyFile * +load_file_and_read_header (const gchar * path, gchar ** targetname, + gchar ** categoryname, gchar ** description, GError ** error) +{ + GKeyFile *in; + gboolean res; + GError *key_error = NULL; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + in = g_key_file_new (); + + GST_DEBUG ("path:%s", path); + + res = + g_key_file_load_from_file (in, path, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &key_error); + if (!res || key_error != NULL) + goto load_error; + + key_error = NULL; + *targetname = + g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", &key_error); + if (!*targetname) + goto empty_name; + + *categoryname = + g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL); + *description = + g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description", + NULL); + + return in; + +load_error: + { + GST_WARNING ("Unable to read GstEncodingTarget file %s: %s", + path, key_error->message); + g_propagate_error (error, key_error); + g_key_file_free (in); + return NULL; + } + +empty_name: + { + GST_WARNING ("Wrong header in file %s: %s", path, key_error->message); + g_propagate_error (error, key_error); + g_key_file_free (in); + return NULL; + } +} + +/** + * gst_encoding_target_load_from_file: + * @filepath: The file location to load the #GstEncodingTarget from + * @error: If an error occured, this field will be filled in. + * + * Opens the provided file and returns the contained #GstEncodingTarget. + * + * Since: 0.10.32 + * + * Returns: (transfer full): The #GstEncodingTarget contained in the file, else + * %NULL + */ + +GstEncodingTarget * +gst_encoding_target_load_from_file (const gchar * filepath, GError ** error) +{ + GKeyFile *in; + gchar *targetname, *categoryname, *description; + GstEncodingTarget *res = NULL; + + in = load_file_and_read_header (filepath, &targetname, &categoryname, + &description, error); + if (!in) + goto beach; + + res = parse_keyfile (in, targetname, categoryname, description); + + g_key_file_free (in); + +beach: + return res; +} + +/* + * returned list contents must be freed + */ +static GList * +get_matching_filenames (gchar * path, gchar * filename) +{ + GList *res = NULL; + GDir *topdir; + const gchar *subdirname; + + topdir = g_dir_open (path, 0, NULL); + if (G_UNLIKELY (topdir == NULL)) + return NULL; + + while ((subdirname = g_dir_read_name (topdir))) { + gchar *ltmp = g_build_filename (path, subdirname, NULL); + + if (g_file_test (ltmp, G_FILE_TEST_IS_DIR)) { + gchar *tmp = g_build_filename (path, subdirname, filename, NULL); + /* Test to see if we have a file named like that in that directory */ + if (g_file_test (tmp, G_FILE_TEST_EXISTS)) + res = g_list_append (res, tmp); + else + g_free (tmp); + } + g_free (ltmp); + } + + g_dir_close (topdir); + + return res; +} + +static GstEncodingTarget * +gst_encoding_target_subload (gchar * path, const gchar * category, + gchar * lfilename, GError ** error) +{ + GstEncodingTarget *target = NULL; + + if (category) { + gchar *filename; + + filename = g_build_filename (path, category, lfilename, NULL); + target = gst_encoding_target_load_from_file (filename, error); + g_free (filename); + } else { + GList *tmp, *tries = get_matching_filenames (path, lfilename); + + /* Try to find a file named %s.gstprofile in any subdirectories */ + for (tmp = tries; tmp; tmp = tmp->next) { + target = gst_encoding_target_load_from_file ((gchar *) tmp->data, NULL); + if (target) + break; + } + g_list_foreach (tries, (GFunc) g_free, NULL); + if (tries) + g_list_free (tries); + } + + return target; +} + +/** + * gst_encoding_target_load: + * @name: the name of the #GstEncodingTarget to load. + * @category: (allow-none): the name of the target category, like + * #GST_ENCODING_CATEGORY_DEVICE. Can be %NULL + * @error: If an error occured, this field will be filled in. + * + * Searches for the #GstEncodingTarget with the given name, loads it + * and returns it. + * + * If the category name is specified only targets from that category will be + * searched for. + * + * Since: 0.10.32 + * + * Returns: (transfer full): The #GstEncodingTarget if available, else %NULL. + */ +GstEncodingTarget * +gst_encoding_target_load (const gchar * name, const gchar * category, + GError ** error) +{ + gchar *lfilename, *tldir; + GstEncodingTarget *target = NULL; + + g_return_val_if_fail (name != NULL, NULL); + + if (!validate_name (name)) + goto invalid_name; + + if (category && !validate_name (category)) + goto invalid_category; + + lfilename = g_strdup_printf ("%s" GST_ENCODING_TARGET_SUFFIX, name); + + /* Try from local profiles */ + tldir = + g_build_filename (g_get_home_dir (), ".gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, NULL); + target = gst_encoding_target_subload (tldir, category, lfilename, error); + g_free (tldir); + + if (target == NULL) { + /* Try from system-wide profiles */ + tldir = + g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, NULL); + target = gst_encoding_target_subload (tldir, category, lfilename, error); + g_free (tldir); + } + + g_free (lfilename); + + return target; + +invalid_name: + { + GST_ERROR ("Invalid name for encoding target : '%s'", name); + return NULL; + } +invalid_category: + { + GST_ERROR ("Invalid name for encoding category : '%s'", category); + return NULL; + } +} + +/** + * gst_encoding_target_save_to_file: + * @target: a #GstEncodingTarget + * @filepath: the location to store the @target at. + * @error: If an error occured, this field will be filled in. + * + * Saves the @target to the provided file location. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the target was correctly saved, else %FALSE. + **/ + +gboolean +gst_encoding_target_save_to_file (GstEncodingTarget * target, + const gchar * filepath, GError ** error) +{ + GKeyFile *out; + gchar *data; + gsize data_size; + + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE); + g_return_val_if_fail (filepath != NULL, FALSE); + + /* FIXME : Check filepath is valid and writable + * FIXME : Strip out profiles already present in system target */ + + /* Get unique name... */ + + /* Create output GKeyFile */ + out = g_key_file_new (); + + if (!serialize_target (out, target)) + goto serialize_failure; + + if (!(data = g_key_file_to_data (out, &data_size, error))) + goto convert_failed; + + if (!g_file_set_contents (filepath, data, data_size, error)) + goto write_failed; + + g_key_file_free (out); + g_free (data); + + return TRUE; + +serialize_failure: + { + GST_ERROR ("Failure serializing target"); + g_key_file_free (out); + return FALSE; + } + +convert_failed: + { + GST_ERROR ("Failure converting keyfile: %s", (*error)->message); + g_key_file_free (out); + g_free (data); + return FALSE; + } + +write_failed: + { + GST_ERROR ("Unable to write file %s: %s", filepath, (*error)->message); + g_key_file_free (out); + g_free (data); + return FALSE; + } +} + +/** + * gst_encoding_target_save: + * @target: a #GstEncodingTarget + * @error: If an error occured, this field will be filled in. + * + * Saves the @target to a default user-local directory. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the target was correctly saved, else %FALSE. + **/ + +gboolean +gst_encoding_target_save (GstEncodingTarget * target, GError ** error) +{ + gchar *filename; + gchar *lfilename; + gboolean res; + + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE); + g_return_val_if_fail (target->category != NULL, FALSE); + + lfilename = g_strdup_printf ("%s" GST_ENCODING_TARGET_SUFFIX, target->name); + filename = + g_build_filename (g_get_home_dir (), ".gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, target->category, lfilename, NULL); + g_free (lfilename); + + res = gst_encoding_target_save_to_file (target, filename, error); + g_free (filename); + + return TRUE; +} + +static GList * +get_categories (gchar * path) +{ + GList *res = NULL; + GDir *topdir; + const gchar *subdirname; + + topdir = g_dir_open (path, 0, NULL); + if (G_UNLIKELY (topdir == NULL)) + return NULL; + + while ((subdirname = g_dir_read_name (topdir))) { + gchar *ltmp = g_build_filename (path, subdirname, NULL); + + if (g_file_test (ltmp, G_FILE_TEST_IS_DIR)) { + res = g_list_append (res, (gpointer) g_strdup (subdirname)); + } + g_free (ltmp); + } + + g_dir_close (topdir); + + return res; +} + +/** + * gst_encoding_list_available_categories: + * + * Lists all #GstEncodingTarget categories present on disk. + * + * Returns: (transfer full) (element-type gchar*): A list + * of #GstEncodingTarget categories. + * + * Since: 0.10.32 + */ +GList * +gst_encoding_list_available_categories (void) +{ + GList *res = NULL; + GList *tmp1, *tmp2; + gchar *topdir; + + /* First try user-local categories */ + topdir = g_build_filename (g_get_home_dir (), ".gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, NULL); + res = get_categories (topdir); + g_free (topdir); + + /* Extend with system-wide categories */ + topdir = g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, NULL); + tmp1 = get_categories (topdir); + g_free (topdir); + + for (tmp2 = tmp1; tmp2; tmp2 = tmp2->next) { + gchar *name = (gchar *) tmp2->data; + if (!g_list_find_custom (res, name, (GCompareFunc) g_strcmp0)) + res = g_list_append (res, (gpointer) name); + else + g_free (name); + } + g_free (tmp1); + + return res; +} + +static inline GList * +sub_get_all_targets (gchar * subdir) +{ + GList *res = NULL; + const gchar *filename; + GDir *dir; + GstEncodingTarget *target; + + dir = g_dir_open (subdir, 0, NULL); + if (G_UNLIKELY (dir == NULL)) + return NULL; + + while ((filename = g_dir_read_name (dir))) { + gchar *fullname; + + /* Only try files ending with .gstprofile */ + if (!g_str_has_suffix (filename, GST_ENCODING_TARGET_SUFFIX)) + continue; + + fullname = g_build_filename (subdir, filename, NULL); + target = gst_encoding_target_load_from_file (fullname, NULL); + if (target) { + res = g_list_append (res, target); + } else + GST_WARNING ("Failed to get a target from %s", fullname); + g_free (fullname); + } + g_dir_close (dir); + + return res; +} + +static inline GList * +get_all_targets (gchar * topdir, const gchar * categoryname) +{ + GList *res = NULL; + + if (categoryname) { + gchar *subdir = g_build_filename (topdir, categoryname, NULL); + /* Try to open the directory */ + res = sub_get_all_targets (subdir); + g_free (subdir); + } else { + const gchar *subdirname; + GDir *dir = g_dir_open (topdir, 0, NULL); + + if (G_UNLIKELY (dir == NULL)) + return NULL; + + while ((subdirname = g_dir_read_name (dir))) { + gchar *ltmp = g_build_filename (topdir, subdirname, NULL); + + if (g_file_test (ltmp, G_FILE_TEST_IS_DIR)) { + res = g_list_concat (res, sub_get_all_targets (ltmp)); + } + g_free (ltmp); + } + g_dir_close (dir); + } + + return res; +} + +static guint +compare_targets (const GstEncodingTarget * ta, const GstEncodingTarget * tb) +{ + if (!g_strcmp0 (ta->name, tb->name) + && !g_strcmp0 (ta->category, tb->category)) + return -1; + + return 0; +} + +/** + * gst_encoding_list_all_targets: + * @categoryname: (allow-none): The category, for ex: #GST_ENCODING_CATEGORY_DEVICE. + * Can be %NULL. + * + * List all available #GstEncodingTarget for the specified category, or all categories + * if @categoryname is %NULL. + * + * Returns: (transfer full) (element-type GstEncodingTarget): The list of #GstEncodingTarget + * + * Since: 0.10.32 + */ +GList * +gst_encoding_list_all_targets (const gchar * categoryname) +{ + GList *res; + GList *tmp1, *tmp2; + gchar *topdir; + + /* Get user-locals */ + topdir = g_build_filename (g_get_home_dir (), ".gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, NULL); + res = get_all_targets (topdir, categoryname); + g_free (topdir); + + /* Get system-wide */ + topdir = g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR, + GST_ENCODING_TARGET_DIRECTORY, NULL); + tmp1 = get_all_targets (topdir, categoryname); + g_free (topdir); + + /* Merge system-wide targets */ + /* FIXME : We should merge the system-wide profiles into the user-locals + * instead of stopping at identical target names */ + for (tmp2 = tmp1; tmp2; tmp2 = tmp2->next) { + GstEncodingTarget *target = (GstEncodingTarget *) tmp2->data; + if (g_list_find_custom (res, target, (GCompareFunc) compare_targets)) + gst_encoding_target_unref (target); + else + res = g_list_append (res, target); + } + g_list_free (tmp1); + + return res; +} diff --git a/gst-libs/gst/pbutils/encoding-target.h b/gst-libs/gst/pbutils/encoding-target.h new file mode 100644 index 0000000000..70c049db3a --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-target.h @@ -0,0 +1,147 @@ +/* GStreamer encoding profile registry + * Copyright (C) 2010 Edward Hervey + * (C) 2010 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_PROFILE_REGISTRY_H__ +#define __GST_PROFILE_REGISTRY_H__ + +#include + +G_BEGIN_DECLS + + +/* FIXME/UNKNOWNS + * + * Should encoding categories be well-known strings/quarks ? + * + */ + +/** + * GST_ENCODING_CATEGORY_DEVICE: + * + * #GstEncodingTarget category for device-specific targets. + * The name of the target will usually be the contructor and model of the device, + * and that target will contain #GstEncodingProfiles suitable for that device. + */ +#define GST_ENCODING_CATEGORY_DEVICE "device" + +/** + * GST_ENCODING_CATEGORY_ONLINE_SERVICE: + * + * #GstEncodingTarget category for online-services. + * The name of the target will usually be the name of the online service + * and that target will contain #GstEncodingProfiles suitable for that online + * service. + */ + +#define GST_ENCODING_CATEGORY_ONLINE_SERVICE "online-service" + +/** + * GST_ENCODING_CATEGORY_STORAGE_EDITING: + * + * #GstEncodingTarget category for storage, archiving and editing targets. + * Those targets can be lossless and/or provide very fast random access content. + * The name of the target will usually be the container type or editing target, + * and that target will contain #GstEncodingProfiles suitable for editing or + * storage. + */ +#define GST_ENCODING_CATEGORY_STORAGE_EDITING "storage-editing" + +/** + * GST_ENCODING_CATEGORY_CAPTURE: + * + * #GstEncodingTarget category for recording and capture. + * Targets within this category are optimized for low latency encoding. + */ +#define GST_ENCODING_CATEGORY_CAPTURE "capture" + +/** + * GstEncodingTarget: + * + * Collection of #GstEncodingProfile for a specific target or use-case. + * + * When being stored/loaded, targets come from a specific category, like + * #GST_ENCODING_CATEGORY_DEVICE. + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_TARGET \ + (gst_encoding_target_get_type ()) +#define GST_ENCODING_TARGET(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_TARGET, GstEncodingTarget)) +#define GST_IS_ENCODING_TARGET(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_TARGET)) + +typedef struct _GstEncodingTarget GstEncodingTarget; +typedef GstMiniObjectClass GstEncodingTargetClass; + +GType gst_encoding_target_get_type (void); + +/** + * gst_encoding_target_unref: + * @target: a #GstEncodingTarget + * + * Decreases the reference count of the @target, possibly freeing it. + * + * Since: 0.10.32 + */ +#define gst_encoding_target_unref(target) \ + (gst_mini_object_unref ((GstMiniObject*) target)) + +/** + * gst_encoding_target_ref: + * @target: a #GstEncodingTarget + * + * Increases the reference count of the @target. + * + * Since: 0.10.32 + */ +#define gst_encoding_target_ref(target) \ + (gst_mini_object_ref ((GstMiniObject*) target)) + +GstEncodingTarget * +gst_encoding_target_new (const gchar *name, const gchar *category, + const gchar *description, const GList *profiles); +const gchar *gst_encoding_target_get_name (GstEncodingTarget *target); +const gchar *gst_encoding_target_get_category (GstEncodingTarget *target); +const gchar *gst_encoding_target_get_description (GstEncodingTarget *target); +const GList *gst_encoding_target_get_profiles (GstEncodingTarget *target); +GstEncodingProfile *gst_encoding_target_get_profile (GstEncodingTarget *target, + const gchar *name); + +gboolean +gst_encoding_target_add_profile (GstEncodingTarget *target, GstEncodingProfile *profile); + +gboolean gst_encoding_target_save (GstEncodingTarget *target, + GError **error); +gboolean gst_encoding_target_save_to_file (GstEncodingTarget *target, + const gchar *filepath, + GError **error); +GstEncodingTarget *gst_encoding_target_load (const gchar *name, + const gchar *category, + GError **error); +GstEncodingTarget *gst_encoding_target_load_from_file (const gchar *filepath, + GError **error); + +GList *gst_encoding_list_available_categories (void); +GList *gst_encoding_list_all_targets (const gchar * categoryname); + +G_END_DECLS + +#endif /* __GST_PROFILE_REGISTRY_H__ */ diff --git a/gst-libs/gst/pbutils/gstdiscoverer-types.c b/gst-libs/gst/pbutils/gstdiscoverer-types.c index a3d6bf0fc0..2edf8e923c 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer-types.c +++ b/gst-libs/gst/pbutils/gstdiscoverer-types.c @@ -377,15 +377,16 @@ gst_discoverer_stream_info_list_free (GList * infos) /** * gst_discoverer_info_get_streams: * @info: a #GstDiscovererInfo - * @streamtype: a #GType of #GstDiscovererStreamInfo + * @streamtype: a #GType derived from #GstDiscovererStreamInfo * * Finds the #GstDiscovererStreamInfo contained in @info that match the * given @streamtype. * - * Returns: A #GList of matching #GstDiscovererStreamInfo. The caller should - * free it with #gst_discoverer_stream_info_list_free. + * Returns: (transfer full) (element-type Gst.DiscovererStreamInfo): A #GList of + * matching #GstDiscovererStreamInfo. The caller should free it with + * gst_discoverer_stream_info_list_free(). * - * Since 0.10.31 + * Since: 0.10.31 */ GList * gst_discoverer_info_get_streams (GstDiscovererInfo * info, GType streamtype) @@ -408,10 +409,11 @@ gst_discoverer_info_get_streams (GstDiscovererInfo * info, GType streamtype) * * Finds all the #GstDiscovererAudioInfo contained in @info * - * Returns: A #GList of matching #GstDiscovererStreamInfo. The caller should - * free it with #gst_discoverer_stream_info_list_free. + * Returns: (transfer full) (element-type Gst.DiscovererStreamInfo): A #GList of + * matching #GstDiscovererStreamInfo. The caller should free it with + * gst_discoverer_stream_info_list_free(). * - * Since 0.10.31 + * Since: 0.10.31 */ GList * gst_discoverer_info_get_audio_streams (GstDiscovererInfo * info) @@ -425,10 +427,11 @@ gst_discoverer_info_get_audio_streams (GstDiscovererInfo * info) * * Finds all the #GstDiscovererVideoInfo contained in @info * - * Returns: A #GList of matching #GstDiscovererStreamInfo. The caller should - * free it with #gst_discoverer_stream_info_list_free. + * Returns: (transfer full) (element-type Gst.DiscovererStreamInfo): A #GList of + * matching #GstDiscovererStreamInfo. The caller should free it with + * gst_discoverer_stream_info_list_free(). * - * Since 0.10.31 + * Since: 0.10.31 */ GList * gst_discoverer_info_get_video_streams (GstDiscovererInfo * info) @@ -442,10 +445,11 @@ gst_discoverer_info_get_video_streams (GstDiscovererInfo * info) * * Finds all the #GstDiscovererContainerInfo contained in @info * - * Returns: A #GList of matching #GstDiscovererStreamInfo. The caller should - * free it with #gst_discoverer_stream_info_list_free. + * Returns: (transfer full) (element-type Gst.DiscovererStreamInfo): A #GList of + * matching #GstDiscovererStreamInfo. The caller should free it with + * gst_discoverer_stream_info_list_free(). * - * Since 0.10.31 + * Since: 0.10.31 */ GList * gst_discoverer_info_get_container_streams (GstDiscovererInfo * info) @@ -461,7 +465,7 @@ gst_discoverer_info_get_container_streams (GstDiscovererInfo * info) * Returns: a human readable name for the stream type of the given @info (ex : "audio", * "container",...). * - * Since 0.10.31 + * Since: 0.10.31 */ const gchar * gst_discoverer_stream_info_get_stream_type_nick (GstDiscovererStreamInfo * info) @@ -493,10 +497,11 @@ gst_discoverer_stream_info_get_stream_type_nick (GstDiscovererStreamInfo * info) * gst_discoverer_stream_info_get_previous: * @info: a #GstDiscovererStreamInfo * - * Returns: the previous #GstDiscovererStreamInfo in a chain. %NULL for starting - * points. Unref with #gst_discoverer_stream_info_unref after usage. + * Returns: (transfer full): the previous #GstDiscovererStreamInfo in a chain. + * %NULL for starting points. Unref with #gst_discoverer_stream_info_unref + * after usage. * - * Since 0.10.31 + * Since: 0.10.31 */ GstDiscovererStreamInfo * gst_discoverer_stream_info_get_previous (GstDiscovererStreamInfo * info) @@ -512,10 +517,11 @@ gst_discoverer_stream_info_get_previous (GstDiscovererStreamInfo * info) * gst_discoverer_stream_info_get_next: * @info: a #GstDiscovererStreamInfo * - * Returns: the next #GstDiscovererStreamInfo in a chain. %NULL for final streams. + * Returns: (transfer full): the next #GstDiscovererStreamInfo in a chain. %NULL + * for final streams. * Unref with #gst_discoverer_stream_info_unref after usage. * - * Since 0.10.31 + * Since: 0.10.31 */ GstDiscovererStreamInfo * gst_discoverer_stream_info_get_next (GstDiscovererStreamInfo * info) @@ -532,9 +538,10 @@ gst_discoverer_stream_info_get_next (GstDiscovererStreamInfo * info) * gst_discoverer_stream_info_get_caps: * @info: a #GstDiscovererStreamInfo * - * Returns: the #GstCaps of the stream. Unref with #gst_caps_unref after usage. + * Returns: (transfer full): the #GstCaps of the stream. Unref with + * #gst_caps_unref after usage. * - * Since 0.10.31 + * Since: 0.10.31 */ GstCaps * gst_discoverer_stream_info_get_caps (GstDiscovererStreamInfo * info) @@ -550,10 +557,10 @@ gst_discoverer_stream_info_get_caps (GstDiscovererStreamInfo * info) * gst_discoverer_stream_info_get_tags: * @info: a #GstDiscovererStreamInfo * - * Returns: the tags contained in this stream. If you wish to use the tags after - * the life-time of @info you will need to copy them. + * Returns: (transfer none): the tags contained in this stream. If you wish to + * use the tags after the life-time of @info you will need to copy them. * - * Since 0.10.31 + * Since: 0.10.31 */ const GstTagList * @@ -568,11 +575,11 @@ gst_discoverer_stream_info_get_tags (GstDiscovererStreamInfo * info) * gst_discoverer_stream_info_get_misc: * @info: a #GstDiscovererStreamInfo * - * Returns: additional information regarding the stream (for example codec version, - * profile, etc..). If you wish to use the #GstStructure after the life-time of - * @info you will need to copy it. + * Returns: (transfer none): additional information regarding the stream (for + * example codec version, profile, etc..). If you wish to use the #GstStructure + * after the life-time of @info you will need to copy it. * - * Since 0.10.31 + * Since: 0.10.31 */ const GstStructure * gst_discoverer_stream_info_get_misc (GstDiscovererStreamInfo * info) @@ -588,10 +595,11 @@ gst_discoverer_stream_info_get_misc (GstDiscovererStreamInfo * info) * gst_discoverer_container_info_get_streams: * @info: a #GstDiscovererStreamInfo * - * Returns: the list of #GstDiscovererStreamInfo this container stream offers. - * Free with #gst_discoverer_stream_info_list_free after usage. + * Returns: (transfer full) (element-type Gst.DiscovererStreamInfo): the list of + * #GstDiscovererStreamInfo this container stream offers. + * Free with gst_discoverer_stream_info_list_free() after usage. * - * Since 0.10.31 + * Since: 0.10.31 */ GList * @@ -622,7 +630,7 @@ gst_discoverer_container_info_get_streams (GstDiscovererContainerInfo * info) * * Returns: the number of channels in the stream. * - * Since 0.10.31 + * Since: 0.10.31 */ AUDIO_INFO_ACCESSOR_CODE (channels, guint, 0); @@ -633,7 +641,7 @@ AUDIO_INFO_ACCESSOR_CODE (channels, guint, 0); * * Returns: the sample rate of the stream in Hertz. * - * Since 0.10.31 + * Since: 0.10.31 */ AUDIO_INFO_ACCESSOR_CODE (sample_rate, guint, 0); @@ -644,7 +652,7 @@ AUDIO_INFO_ACCESSOR_CODE (sample_rate, guint, 0); * * Returns: the number of bits used per sample in each channel. * - * Since 0.10.31 + * Since: 0.10.31 */ AUDIO_INFO_ACCESSOR_CODE (depth, guint, 0); @@ -655,7 +663,7 @@ AUDIO_INFO_ACCESSOR_CODE (depth, guint, 0); * * Returns: the average or nominal bitrate of the stream in bits/second. * - * Since 0.10.31 + * Since: 0.10.31 */ AUDIO_INFO_ACCESSOR_CODE (bitrate, guint, 0); @@ -666,7 +674,7 @@ AUDIO_INFO_ACCESSOR_CODE (bitrate, guint, 0); * * Returns: the maximum bitrate of the stream in bits/second. * - * Since 0.10.31 + * Since: 0.10.31 */ AUDIO_INFO_ACCESSOR_CODE (max_bitrate, guint, 0); @@ -684,7 +692,7 @@ AUDIO_INFO_ACCESSOR_CODE (max_bitrate, guint, 0); * * Returns: the width of the video stream in pixels. * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (width, guint, 0); @@ -695,7 +703,7 @@ VIDEO_INFO_ACCESSOR_CODE (width, guint, 0); * * Returns: the height of the video stream in pixels. * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (height, guint, 0); @@ -704,10 +712,9 @@ VIDEO_INFO_ACCESSOR_CODE (height, guint, 0); * gst_discoverer_video_info_get_depth: * @info: a #GstDiscovererVideoInfo * - * Returns: the depth in bits of the video stream (only relevant for - * video streams). + * Returns: the depth in bits of the video stream. * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (depth, guint, 0); @@ -718,7 +725,7 @@ VIDEO_INFO_ACCESSOR_CODE (depth, guint, 0); * * Returns: the framerate of the video stream (numerator). * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (framerate_num, guint, 0); @@ -729,7 +736,7 @@ VIDEO_INFO_ACCESSOR_CODE (framerate_num, guint, 0); * * Returns: the framerate of the video stream (denominator). * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (framerate_denom, guint, 0); @@ -740,7 +747,7 @@ VIDEO_INFO_ACCESSOR_CODE (framerate_denom, guint, 0); * * Returns: the Pixel Aspect Ratio (PAR) of the video stream (numerator). * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (par_num, guint, 0); @@ -751,7 +758,7 @@ VIDEO_INFO_ACCESSOR_CODE (par_num, guint, 0); * * Returns: the Pixel Aspect Ratio (PAR) of the video stream (denominator). * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (par_denom, guint, 0); @@ -762,7 +769,7 @@ VIDEO_INFO_ACCESSOR_CODE (par_denom, guint, 0); * * Returns: %TRUE if the stream is interlaced, else %FALSE. * - * Since 0.10.31 + * Since: 0.10.31 */ gboolean gst_discoverer_video_info_is_interlaced (const GstDiscovererVideoInfo * info) @@ -778,7 +785,7 @@ gst_discoverer_video_info_is_interlaced (const GstDiscovererVideoInfo * info) * * Returns: the average or nominal bitrate of the video stream in bits/second. * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (bitrate, guint, 0); @@ -789,7 +796,7 @@ VIDEO_INFO_ACCESSOR_CODE (bitrate, guint, 0); * * Returns: the maximum bitrate of the video stream in bits/second. * - * Since 0.10.31 + * Since: 0.10.31 */ VIDEO_INFO_ACCESSOR_CODE (max_bitrate, guint, 0); @@ -801,7 +808,7 @@ VIDEO_INFO_ACCESSOR_CODE (max_bitrate, guint, 0); * Returns: #TRUE if the video stream corresponds to an image (i.e. only contains * one frame). * - * Since 0.10.31 + * Since: 0.10.31 */ gboolean gst_discoverer_video_info_is_image (const GstDiscovererVideoInfo * info) @@ -822,10 +829,10 @@ gst_discoverer_video_info_is_image (const GstDiscovererVideoInfo * info) * gst_discoverer_info_get_uri: * @info: a #GstDiscovererInfo * - * Returns: the URI to which this information corresponds to. Copy it if you - * wish to use it after the life-time of @info. + * Returns: (transfer none): the URI to which this information corresponds to. + * Copy it if you wish to use it after the life-time of @info. * - * Since 0.10.31 + * Since: 0.10.31 */ DISCOVERER_INFO_ACCESSOR_CODE (uri, const gchar *, NULL); @@ -836,7 +843,7 @@ DISCOVERER_INFO_ACCESSOR_CODE (uri, const gchar *, NULL); * * Returns: the result of the discovery as a #GstDiscovererResult. * - * Since 0.10.31 + * Since: 0.10.31 */ DISCOVERER_INFO_ACCESSOR_CODE (result, GstDiscovererResult, GST_DISCOVERER_OK); @@ -845,11 +852,12 @@ DISCOVERER_INFO_ACCESSOR_CODE (result, GstDiscovererResult, GST_DISCOVERER_OK); * gst_discoverer_info_get_stream_info: * @info: a #GstDiscovererInfo * - * Returns: the structure (or topology) of the URI as a #GstDiscovererStreamInfo. + * Returns: (transfer full): the structure (or topology) of the URI as a + * #GstDiscovererStreamInfo. * This structure can be traversed to see the original hierarchy. Unref with - * #gst_discoverer_stream_info_unref after usage. + * gst_discoverer_stream_info_unref() after usage. * - * Since 0.10.31 + * Since: 0.10.31 */ GstDiscovererStreamInfo * @@ -866,10 +874,11 @@ gst_discoverer_info_get_stream_info (GstDiscovererInfo * info) * gst_discoverer_info_get_stream_list: * @info: a #GstDiscovererInfo * - * Returns: the list of all streams contained in the #info. Free after usage - * with #gst_discoverer_stream_info_list_free. + * Returns: (transfer full) (element-type Gst.DiscovererStreamInfo): the list of + * all streams contained in the #info. Free after usage + * with gst_discoverer_stream_info_list_free(). * - * Since 0.10.31 + * Since: 0.10.31 */ GList * gst_discoverer_info_get_stream_list (GstDiscovererInfo * info) @@ -892,20 +901,31 @@ gst_discoverer_info_get_stream_list (GstDiscovererInfo * info) * * Returns: the duration of the URI in #GstClockTime (nanoseconds). * - * Since 0.10.31 + * Since: 0.10.31 */ DISCOVERER_INFO_ACCESSOR_CODE (duration, GstClockTime, GST_CLOCK_TIME_NONE); +/** + * gst_discoverer_info_get_seekable: + * @info: a #GstDiscovererInfo + * + * Returns: the wheter the URI is seekable. + * + * Since: 0.10.32 + */ + +DISCOVERER_INFO_ACCESSOR_CODE (seekable, gboolean, FALSE); + /** * gst_discoverer_info_get_misc: * @info: a #GstDiscovererInfo * - * Returns: Miscellaneous information stored as a #GstStructure (for example: - * information about missing plugins). If you wish to use the #GstStructure - * after the life-time of @info, you will need to copy it. + * Returns: (transfer none): Miscellaneous information stored as a #GstStructure + * (for example: information about missing plugins). If you wish to use the + * #GstStructure after the life-time of @info, you will need to copy it. * - * Since 0.10.31 + * Since: 0.10.31 */ DISCOVERER_INFO_ACCESSOR_CODE (misc, const GstStructure *, NULL); @@ -914,10 +934,50 @@ DISCOVERER_INFO_ACCESSOR_CODE (misc, const GstStructure *, NULL); * gst_discoverer_info_get_tags: * @info: a #GstDiscovererInfo * - * Returns: all tags contained in the %URI. If you wish to use the tags after - * the life-time of @info, you will need to copy them. + * Returns: (transfer none): all tags contained in the %URI. If you wish to use + * the tags after the life-time of @info, you will need to copy them. * - * Since 0.10.31 + * Since: 0.10.31 */ DISCOVERER_INFO_ACCESSOR_CODE (tags, const GstTagList *, NULL); + +/** + * gst_discoverer_info_ref: + * @info: a #GstDiscovererInfo + * + * Increments the reference count of @info. + * + * Returns: the same #GstDiscovererInfo object + * + * Since: 0.10.31 + */ + +/** + * gst_discoverer_info_unref: + * @info: a #GstDiscovererInfo + * + * Decrements the reference count of @info. + * + * Since: 0.10.31 + */ + +/** + * gst_discoverer_stream_info_ref: + * @info: a #GstDiscovererStreamInfo + * + * Increments the reference count of @info. + * + * Returns: the same #GstDiscovererStreamInfo object + * + * Since: 0.10.31 + */ + +/** + * gst_discoverer_stream_info_unref: + * @info: a #GstDiscovererStreamInfo + * + * Decrements the reference count of @info. + * + * Since: 0.10.31 + */ diff --git a/gst-libs/gst/pbutils/gstdiscoverer.c b/gst-libs/gst/pbutils/gstdiscoverer.c index 61208ac41b..4a7d72d27d 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.c +++ b/gst-libs/gst/pbutils/gstdiscoverer.c @@ -36,8 +36,8 @@ * asks for the discovery to begin (through gst_discoverer_start()). * * All the information is returned in a #GstDiscovererInfo structure. - * - * Since 0.10.31 + * + * Since: 0.10.31 */ #ifdef HAVE_CONFIG_H @@ -105,6 +105,15 @@ struct _GstDiscovererPrivate GMainContext *ctx; guint sourceid; guint timeoutid; + + /* reusable queries */ + GstQuery *seeking_query; + + /* Handler ids for various callbacks */ + gulong pad_added_id; + gulong pad_remove_id; + gulong element_added_id; + gulong bus_cb_id; }; #define DISCO_LOCK(dc) g_mutex_lock (dc->priv->lock); @@ -215,7 +224,7 @@ gst_discoverer_class_init (GstDiscovererClass * klass) * @discoverer: the #GstDiscoverer * @info: the results #GstDiscovererInfo * @error: (type GLib.Error): #GError, which will be non-NULL if an error - * occured during discovery + * occurred during discovery * * Will be emitted when all information on a URI could be discovered. */ @@ -242,6 +251,7 @@ static void gst_discoverer_init (GstDiscoverer * dc) { GstElement *tmp; + GstFormat format = GST_FORMAT_TIME; dc->priv = G_TYPE_INSTANCE_GET_PRIVATE (dc, GST_TYPE_DISCOVERER, GstDiscovererPrivate); @@ -263,27 +273,34 @@ gst_discoverer_init (GstDiscoverer * dc) GST_LOG_OBJECT (dc, "Adding uridecodebin to pipeline"); gst_bin_add (dc->priv->pipeline, dc->priv->uridecodebin); - g_signal_connect (dc->priv->uridecodebin, "pad-added", - G_CALLBACK (uridecodebin_pad_added_cb), dc); - g_signal_connect (dc->priv->uridecodebin, "pad-removed", - G_CALLBACK (uridecodebin_pad_removed_cb), dc); + dc->priv->pad_added_id = + g_signal_connect_object (dc->priv->uridecodebin, "pad-added", + G_CALLBACK (uridecodebin_pad_added_cb), dc, 0); + dc->priv->pad_remove_id = + g_signal_connect_object (dc->priv->uridecodebin, "pad-removed", + G_CALLBACK (uridecodebin_pad_removed_cb), dc, 0); GST_LOG_OBJECT (dc, "Getting pipeline bus"); dc->priv->bus = gst_pipeline_get_bus ((GstPipeline *) dc->priv->pipeline); - g_signal_connect (dc->priv->bus, "message", G_CALLBACK (discoverer_bus_cb), - dc); + dc->priv->bus_cb_id = + g_signal_connect_object (dc->priv->bus, "message", + G_CALLBACK (discoverer_bus_cb), dc, 0); GST_DEBUG_OBJECT (dc, "Done initializing Discoverer"); /* This is ugly. We get the GType of decodebin2 so we can quickly detect * when a decodebin2 is added to uridecodebin so we can set the * post-stream-topology setting to TRUE */ - g_signal_connect (dc->priv->uridecodebin, "element-added", - G_CALLBACK (uridecodebin_element_added_cb), dc); + dc->priv->element_added_id = + g_signal_connect_object (dc->priv->uridecodebin, "element-added", + G_CALLBACK (uridecodebin_element_added_cb), dc, 0); tmp = gst_element_factory_make ("decodebin2", NULL); dc->priv->decodebin2_type = G_OBJECT_TYPE (tmp); gst_object_unref (tmp); + + /* create queries */ + dc->priv->seeking_query = gst_query_new_seeking (format); } static void @@ -297,9 +314,16 @@ discoverer_reset (GstDiscoverer * dc) dc->priv->pending_uris = NULL; } - gst_element_set_state ((GstElement *) dc->priv->pipeline, GST_STATE_NULL); + if (dc->priv->pipeline) + gst_element_set_state ((GstElement *) dc->priv->pipeline, GST_STATE_NULL); } +#define DISCONNECT_SIGNAL(o,i) G_STMT_START{ \ + if ((i) && g_signal_handler_is_connected ((o), (i))) \ + g_signal_handler_disconnect ((o), (i)); \ + (i) = 0; \ +}G_STMT_END + static void gst_discoverer_dispose (GObject * obj) { @@ -310,18 +334,34 @@ gst_discoverer_dispose (GObject * obj) discoverer_reset (dc); if (G_LIKELY (dc->priv->pipeline)) { + /* Workaround for bug #118536 */ + DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->pad_added_id); + DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->pad_remove_id); + DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->element_added_id); + DISCONNECT_SIGNAL (dc->priv->bus, dc->priv->bus_cb_id); + /* pipeline was set to NULL in _reset */ gst_object_unref (dc->priv->pipeline); gst_object_unref (dc->priv->bus); + dc->priv->pipeline = NULL; dc->priv->uridecodebin = NULL; dc->priv->bus = NULL; } + gst_discoverer_stop (dc); + if (dc->priv->lock) { g_mutex_free (dc->priv->lock); dc->priv->lock = NULL; } + + if (dc->priv->seeking_query) { + gst_query_unref (dc->priv->seeking_query); + dc->priv->seeking_query = NULL; + } + + G_OBJECT_CLASS (gst_discoverer_parent_class)->dispose (obj); } static void @@ -373,12 +413,24 @@ static gboolean _event_probe (GstPad * pad, GstEvent * event, PrivateStream * ps) { if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) { - GstTagList *tl = NULL; + GstTagList *tl = NULL, *tmp; gst_event_parse_tag (event, &tl); GST_DEBUG_OBJECT (pad, "tags %" GST_PTR_FORMAT, tl); DISCO_LOCK (ps->dc); - ps->tags = gst_tag_list_merge (ps->tags, tl, GST_TAG_MERGE_APPEND); + /* If preroll is complete, drop these tags - the collected information is + * possibly already being processed and adding more tags would be racy */ + if (G_LIKELY (ps->dc->priv->processing)) { + GST_DEBUG_OBJECT (pad, "private stream %p old tags %" GST_PTR_FORMAT, ps, + ps->tags); + tmp = gst_tag_list_merge (ps->tags, tl, GST_TAG_MERGE_APPEND); + if (ps->tags) + gst_tag_list_free (ps->tags); + ps->tags = tmp; + GST_DEBUG_OBJECT (pad, "private stream %p new tags %" GST_PTR_FORMAT, ps, + tmp); + } else + GST_DEBUG_OBJECT (pad, "Dropping tags since preroll is done"); DISCO_UNLOCK (ps->dc); } @@ -575,7 +627,8 @@ collect_information (GstDiscoverer * dc, const GstStructure * st, if (gst_structure_id_has_field (st, _TAGS_QUARK)) { gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags_st, NULL); - if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp)) + if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp) || + gst_structure_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp)) info->bitrate = utmp; if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp)) @@ -627,7 +680,8 @@ collect_information (GstDiscoverer * dc, const GstStructure * st, if (gst_structure_id_has_field (st, _TAGS_QUARK)) { gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags_st, NULL); - if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp)) + if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp) || + gst_structure_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp)) info->bitrate = utmp; if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp)) @@ -817,12 +871,21 @@ parse_stream_topology (GstDiscoverer * dc, const GstStructure * topology, res = (GstDiscovererStreamInfo *) cont; if (gst_structure_id_has_field (topology, _TAGS_QUARK)) { + GstTagList *tmp; + gst_structure_id_get (topology, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags, NULL); - cont->parent.tags = + + GST_DEBUG ("Merge tags %" GST_PTR_FORMAT, tags); + + tmp = gst_tag_list_merge (cont->parent.tags, (GstTagList *) tags, GST_TAG_MERGE_APPEND); gst_tag_list_free (tags); + if (cont->parent.tags) + gst_tag_list_free (cont->parent.tags); + cont->parent.tags = tmp; + GST_DEBUG ("Container info tags %" GST_PTR_FORMAT, tmp); } for (i = 0; i < len; i++) { @@ -859,18 +922,31 @@ discoverer_collect (GstDiscoverer * dc) if (dc->priv->streams) { /* FIXME : Make this querying optional */ if (TRUE) { + GstElement *pipeline = (GstElement *) dc->priv->pipeline; GstFormat format = GST_FORMAT_TIME; gint64 dur; GST_DEBUG ("Attempting to query duration"); - if (gst_element_query_duration ((GstElement *) dc->priv->pipeline, - &format, &dur)) { + if (gst_element_query_duration (pipeline, &format, &dur)) { if (format == GST_FORMAT_TIME) { GST_DEBUG ("Got duration %" GST_TIME_FORMAT, GST_TIME_ARGS (dur)); dc->priv->current_info->duration = (guint64) dur; } } + + if (dc->priv->seeking_query) { + if (gst_element_query (pipeline, dc->priv->seeking_query)) { + gboolean seekable; + + gst_query_parse_seeking (dc->priv->seeking_query, &format, + &seekable, NULL, NULL); + if (format == GST_FORMAT_TIME) { + GST_DEBUG ("Got seekable %d", seekable); + dc->priv->current_info->seekable = seekable; + } + } + } } if (dc->priv->current_topology) @@ -908,14 +984,34 @@ discoverer_collect (GstDiscoverer * dc) } } +static void +get_async_cb (gpointer cb_data, GSource * source, GSourceFunc * func, + gpointer * data) +{ + *func = (GSourceFunc) async_timeout_cb; + *data = cb_data; +} + +/* Wrapper since GSourceCallbackFuncs don't expect a return value from ref() */ +static void +_void_g_object_ref (gpointer object) +{ + g_object_ref (G_OBJECT (object)); +} + static void handle_current_async (GstDiscoverer * dc) { GSource *source; + static GSourceCallbackFuncs cb_funcs = { + .ref = _void_g_object_ref, + .unref = g_object_unref, + .get = get_async_cb, + }; /* Attach a timeout to the main context */ source = g_timeout_source_new (dc->priv->timeout / GST_MSECOND); - g_source_set_callback (source, (GSourceFunc) async_timeout_cb, dc, NULL); + g_source_set_callback_indirect (source, g_object_ref (dc), &cb_funcs); dc->priv->timeoutid = g_source_attach (source, dc->priv->ctx); g_source_unref (source); } @@ -927,7 +1023,8 @@ handle_message (GstDiscoverer * dc, GstMessage * msg) { gboolean done = FALSE; - GST_DEBUG ("got a %s message", GST_MESSAGE_TYPE_NAME (msg)); + GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "got a %s message", + GST_MESSAGE_TYPE_NAME (msg)); switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ERROR:{ @@ -935,8 +1032,8 @@ handle_message (GstDiscoverer * dc, GstMessage * msg) gchar *debug; gst_message_parse_error (msg, &gerr, &debug); - GST_WARNING ("Got an error [debug:%s]", debug); - GST_WARNING ("Got an error [message:%s]", gerr->message); + GST_WARNING_OBJECT (GST_MESSAGE_SRC (msg), + "Got an error [debug:%s], [message:%s]", debug, gerr->message); dc->priv->current_error = gerr; g_free (debug); @@ -967,7 +1064,8 @@ handle_message (GstDiscoverer * dc, GstMessage * msg) GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "structure %" GST_PTR_FORMAT, msg->structure); if (sttype == _MISSING_PLUGIN_QUARK) { - GST_DEBUG ("Setting result to MISSING_PLUGINS"); + GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), + "Setting result to MISSING_PLUGINS"); dc->priv->current_info->result = GST_DISCOVERER_MISSING_PLUGINS; dc->priv->current_info->misc = gst_structure_copy (msg->structure); } else if (sttype == _STREAM_TOPOLOGY_QUARK) { @@ -978,15 +1076,20 @@ handle_message (GstDiscoverer * dc, GstMessage * msg) case GST_MESSAGE_TAG: { - GstTagList *tl; + GstTagList *tl, *tmp; gst_message_parse_tag (msg, &tl); - GST_DEBUG ("Got tags %" GST_PTR_FORMAT, tl); + GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Got tags %" GST_PTR_FORMAT, tl); /* Merge with current tags */ - dc->priv->current_info->tags = + tmp = gst_tag_list_merge (dc->priv->current_info->tags, tl, GST_TAG_MERGE_APPEND); gst_tag_list_free (tl); + if (dc->priv->current_info->tags) + gst_tag_list_free (dc->priv->current_info->tags); + dc->priv->current_info->tags = tmp; + GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Current info %p, tags %" + GST_PTR_FORMAT, dc->priv->current_info, tmp); } break; @@ -1109,7 +1212,10 @@ discoverer_bus_cb (GstBus * bus, GstMessage * msg, GstDiscoverer * dc) if (dc->priv->processing) { if (handle_message (dc, msg)) { GST_DEBUG ("Stopping asynchronously"); + /* Serialise with _event_probe() */ + DISCO_LOCK (dc); dc->priv->processing = FALSE; + DISCO_UNLOCK (dc); discoverer_collect (dc); discoverer_cleanup (dc); } @@ -1119,12 +1225,14 @@ discoverer_bus_cb (GstBus * bus, GstMessage * msg, GstDiscoverer * dc) static gboolean async_timeout_cb (GstDiscoverer * dc) { - dc->priv->timeoutid = 0; - GST_DEBUG ("Setting result to TIMEOUT"); - dc->priv->current_info->result = GST_DISCOVERER_TIMEOUT; - dc->priv->processing = FALSE; - discoverer_collect (dc); - discoverer_cleanup (dc); + if (!g_source_is_destroyed (g_main_current_source ())) { + dc->priv->timeoutid = 0; + GST_DEBUG ("Setting result to TIMEOUT"); + dc->priv->current_info->result = GST_DISCOVERER_TIMEOUT; + dc->priv->processing = FALSE; + discoverer_collect (dc); + discoverer_cleanup (dc); + } return FALSE; } @@ -1175,11 +1283,11 @@ beach: /** * gst_discoverer_start: * @discoverer: A #GstDiscoverer - * + * * Allow asynchronous discovering of URIs to take place. * A #GMainLoop must be available for #GstDiscoverer to properly work in * asynchronous mode. - * + * * Since: 0.10.31 */ void @@ -1221,8 +1329,8 @@ gst_discoverer_start (GstDiscoverer * discoverer) * * Stop the discovery of any pending URIs and clears the list of * pending URIS (if any). - * - * Since 0.10.31 + * + * Since: 0.10.31 */ void gst_discoverer_stop (GstDiscoverer * discoverer) @@ -1240,9 +1348,11 @@ gst_discoverer_stop (GstDiscoverer * discoverer) /* We prevent any further processing by setting the bus to * flushing and setting the pipeline to READY. * _reset() will take care of the rest of the cleanup */ - gst_bus_set_flushing (discoverer->priv->bus, TRUE); - gst_element_set_state ((GstElement *) discoverer->priv->pipeline, - GST_STATE_READY); + if (discoverer->priv->bus) + gst_bus_set_flushing (discoverer->priv->bus, TRUE); + if (discoverer->priv->pipeline) + gst_element_set_state ((GstElement *) discoverer->priv->pipeline, + GST_STATE_READY); } discoverer->priv->running = FALSE; DISCO_UNLOCK (discoverer); @@ -1278,11 +1388,12 @@ gst_discoverer_stop (GstDiscoverer * discoverer) * discovery of the @uri will only take place if gst_discoverer_start() has * been called. * - * A copy of @uri will be done internally, the caller can safely g_free() afterwards. + * A copy of @uri will be made internally, so the caller can safely g_free() + * afterwards. * * Returns: %TRUE if the @uri was succesfully appended to the list of pending * uris, else %FALSE - * + * * Since: 0.10.31 */ gboolean @@ -1311,14 +1422,16 @@ gst_discoverer_discover_uri_async (GstDiscoverer * discoverer, * gst_discoverer_discover_uri: * @discoverer: A #GstDiscoverer * @uri: The URI to run on. - * @err: If an error occured, this field will be filled in. + * @err: If an error occurred, this field will be filled in. * * Synchronously discovers the given @uri. * - * A copy of @uri will be done internally, the caller can safely g_free() afterwards. + * A copy of @uri will be made internally, so the caller can safely g_free() + * afterwards. + * + * Returns: (transfer full): the result of the scanning. Can be %NULL if an + * error occurred. * - * Returns: see #GstDiscovererInfo. The caller must unref this structure after use. - * * Since: 0.10.31 */ GstDiscovererInfo * @@ -1369,11 +1482,11 @@ gst_discoverer_discover_uri (GstDiscoverer * discoverer, const gchar * uri, * * Creates a new #GstDiscoverer with the provided timeout. * - * Returns: The new #GstDiscoverer. Free with gst_object_unref() when done. - * If an error happened when creating the discoverer, @err will be set + * Returns: (transfer full): The new #GstDiscoverer. + * If an error occurred when creating the discoverer, @err will be set * accordingly and %NULL will be returned. If @err is set, the caller must * free it when no longer needed using g_error_free(). - * + * * Since: 0.10.31 */ GstDiscoverer * diff --git a/gst-libs/gst/pbutils/gstdiscoverer.h b/gst-libs/gst/pbutils/gstdiscoverer.h index 9a80de02c0..671dbaa4ed 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.h +++ b/gst-libs/gst/pbutils/gstdiscoverer.h @@ -38,9 +38,21 @@ GType gst_discoverer_stream_info_get_type (void); /** * GstDiscovererStreamInfo: * - * Base structure for informations concerning a media stream. Depending on the @streamtype, - * One can find more media-specific information in #GstDiscovererAudioInfo, - * #GstDiscovererVideoInfo, #GstDiscovererContainerInfo. + * Base structure for information concerning a media stream. Depending on the + * stream type, one can find more media-specific information in + * #GstDiscovererAudioInfo, #GstDiscovererVideoInfo, and + * #GstDiscovererContainerInfo. + * + * The #GstDiscovererStreamInfo represents the topology of the stream. Siblings + * can be iterated over with gst_discoverer_stream_info_get_next() and + * gst_discoverer_stream_info_get_previous(). Children (sub-streams) of a + * stream can be accessed using the #GstDiscovererContainerInfo API. + * + * As a simple example, if you run #GstDiscoverer on an AVI file with one audio + * and one video stream, you will get a #GstDiscovererContainerInfo + * corresponding to the AVI container, which in turn will have a + * #GstDiscovererAudioInfo sub-stream and a #GstDiscovererVideoInfo sub-stream + * for the audio and video streams respectively. * * Since: 0.10.31 */ @@ -102,7 +114,7 @@ guint gst_discoverer_audio_info_get_max_bitrate(const GstDiscovererAudioInfo* in /** * GstDiscovererVideoInfo: * - * #GstDiscovererStreamInfo specific to video streams. + * #GstDiscovererStreamInfo specific to video streams (this includes images). * * Since: 0.10.31 */ @@ -179,6 +191,7 @@ GstDiscovererResult gst_discoverer_info_get_result(const GstDiscovererInfo GstDiscovererStreamInfo* gst_discoverer_info_get_stream_info(GstDiscovererInfo* info); GList* gst_discoverer_info_get_stream_list(GstDiscovererInfo* info); GstClockTime gst_discoverer_info_get_duration(const GstDiscovererInfo* info); +gboolean gst_discoverer_info_get_seekable(const GstDiscovererInfo* info); const GstStructure* gst_discoverer_info_get_misc(const GstDiscovererInfo* info); const GstTagList* gst_discoverer_info_get_tags(const GstDiscovererInfo* info); diff --git a/gst-libs/gst/pbutils/install-plugins.c b/gst-libs/gst/pbutils/install-plugins.c index 60a426357a..9b5f45e1df 100644 --- a/gst-libs/gst/pbutils/install-plugins.c +++ b/gst-libs/gst/pbutils/install-plugins.c @@ -608,8 +608,8 @@ gst_install_plugins_installer_exited (GPid pid, gint status, gpointer data) * gst_install_plugins_async: * @details: NULL-terminated array of installer string details (see below) * @ctx: a #GstInstallPluginsContext, or NULL - * @func: the function to call when the installer program returns - * @user_data: the user data to pass to @func when called, or NULL + * @func: (scope async): the function to call when the installer program returns + * @user_data: (closure): the user data to pass to @func when called, or NULL * * Requests plugin installation without blocking. Once the plugins have been * installed or installation has failed, @func will be called with the result diff --git a/gst-libs/gst/pbutils/missing-plugins.c b/gst-libs/gst/pbutils/missing-plugins.c index ca82ce5c8b..1b6d44bb20 100644 --- a/gst-libs/gst/pbutils/missing-plugins.c +++ b/gst-libs/gst/pbutils/missing-plugins.c @@ -166,7 +166,7 @@ copy_and_clean_caps (const GstCaps * caps) * that a source element for a particular URI protocol is missing. This * function is mainly for use in plugins. * - * Returns: a new #GstMessage, or NULL on error + * Returns: (transfer full): a new #GstMessage, or NULL on error */ GstMessage * gst_missing_uri_source_message_new (GstElement * element, @@ -199,7 +199,7 @@ gst_missing_uri_source_message_new (GstElement * element, * that a sink element for a particular URI protocol is missing. This * function is mainly for use in plugins. * - * Returns: a new #GstMessage, or NULL on error + * Returns: (transfer full): a new #GstMessage, or NULL on error */ GstMessage * gst_missing_uri_sink_message_new (GstElement * element, const gchar * protocol) @@ -231,7 +231,7 @@ gst_missing_uri_sink_message_new (GstElement * element, const gchar * protocol) * that a certain required element is missing. This function is mainly for * use in plugins. * - * Returns: a new #GstMessage, or NULL on error + * Returns: (transfer full): a new #GstMessage, or NULL on error */ GstMessage * gst_missing_element_message_new (GstElement * element, @@ -263,7 +263,7 @@ gst_missing_element_message_new (GstElement * element, * that a decoder element for a particular set of (fixed) caps is missing. * This function is mainly for use in plugins. * - * Returns: a new #GstMessage, or NULL on error + * Returns: (transfer full): a new #GstMessage, or NULL on error */ GstMessage * gst_missing_decoder_message_new (GstElement * element, @@ -303,7 +303,7 @@ gst_missing_decoder_message_new (GstElement * element, * that an encoder element for a particular set of (fixed) caps is missing. * This function is mainly for use in plugins. * - * Returns: a new #GstMessage, or NULL on error + * Returns: (transfer full): a new #GstMessage, or NULL on error */ GstMessage * gst_missing_encoder_message_new (GstElement * element, diff --git a/gst-libs/gst/pbutils/pbutils-private.h b/gst-libs/gst/pbutils/pbutils-private.h index 04bcd4c340..5031df0345 100644 --- a/gst-libs/gst/pbutils/pbutils-private.h +++ b/gst-libs/gst/pbutils/pbutils-private.h @@ -78,6 +78,7 @@ struct _GstDiscovererInfo { GstClockTime duration; GstStructure *misc; GstTagList *tags; + gboolean seekable; }; /* missing-plugins.c */ diff --git a/gst-libs/gst/pbutils/pbutils.h b/gst-libs/gst/pbutils/pbutils.h index 19b1d1f095..b9277b1382 100644 --- a/gst-libs/gst/pbutils/pbutils.h +++ b/gst-libs/gst/pbutils/pbutils.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include G_BEGIN_DECLS diff --git a/gst-libs/gst/riff/Makefile.am b/gst-libs/gst/riff/Makefile.am index 171891b4ea..0bde2b0fb3 100644 --- a/gst-libs/gst/riff/Makefile.am +++ b/gst-libs/gst/riff/Makefile.am @@ -61,7 +61,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=$(builddir)/../tag \ diff --git a/gst-libs/gst/riff/riff-media.c b/gst-libs/gst/riff/riff-media.c index 9340b2c5df..40b2bf628c 100644 --- a/gst-libs/gst/riff/riff-media.c +++ b/gst-libs/gst/riff/riff-media.c @@ -717,6 +717,22 @@ gst_riff_create_video_caps (guint32 codec_fcc, break; } + case GST_MAKE_FOURCC ('C', 'S', 'C', 'D'): + { + if (strf) { + gint depth = (strf->bit_cnt != 0) ? (gint) strf->bit_cnt : 24; + + caps = gst_caps_new_simple ("video/x-camstudio", "depth", G_TYPE_INT, + depth, NULL); + } else { + /* template caps */ + caps = gst_caps_new_simple ("video/x-camstudio", NULL); + } + if (codec_name) + *codec_name = g_strdup ("Camstudio"); + break; + } + case GST_MAKE_FOURCC ('V', 'C', 'R', '1'): caps = gst_caps_new_simple ("video/x-ati-vcr", "vcrversion", G_TYPE_INT, 1, NULL); @@ -1737,6 +1753,7 @@ gst_riff_create_video_template_caps (void) GST_MAKE_FOURCC ('A', 'S', 'V', '1'), GST_MAKE_FOURCC ('A', 'S', 'V', '2'), GST_MAKE_FOURCC ('C', 'L', 'J', 'R'), + GST_MAKE_FOURCC ('C', 'S', 'C', 'D'), GST_MAKE_FOURCC ('C', 'Y', 'U', 'V'), GST_MAKE_FOURCC ('D', 'I', 'B', ' '), GST_MAKE_FOURCC ('D', 'I', 'V', '3'), diff --git a/gst-libs/gst/rtp/Makefile.am b/gst-libs/gst/rtp/Makefile.am index 7921b09599..130f2c91f9 100644 --- a/gst-libs/gst/rtp/Makefile.am +++ b/gst-libs/gst/rtp/Makefile.am @@ -57,7 +57,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/rtp/gstbasertpaudiopayload.c b/gst-libs/gst/rtp/gstbasertpaudiopayload.c index 512455a69d..d8ba21bf3f 100644 --- a/gst-libs/gst/rtp/gstbasertpaudiopayload.c +++ b/gst-libs/gst/rtp/gstbasertpaudiopayload.c @@ -482,12 +482,11 @@ gst_base_rtp_audio_payload_push (GstBaseRTPAudioPayload * baseaudiopayload, static GstFlowReturn gst_base_rtp_audio_payload_push_buffer (GstBaseRTPAudioPayload * - baseaudiopayload, GstBuffer * buffer) + baseaudiopayload, GstBuffer * buffer, GstClockTime timestamp) { GstBaseRTPPayload *basepayload; GstBaseRTPAudioPayloadPrivate *priv; GstBuffer *outbuf; - GstClockTime timestamp; guint8 *payload; guint payload_len; GstFlowReturn ret; @@ -496,7 +495,6 @@ gst_base_rtp_audio_payload_push_buffer (GstBaseRTPAudioPayload * basepayload = GST_BASE_RTP_PAYLOAD (baseaudiopayload); payload_len = GST_BUFFER_SIZE (buffer); - timestamp = GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG_OBJECT (baseaudiopayload, "Pushing %d bytes ts %" GST_TIME_FORMAT, payload_len, GST_TIME_ARGS (timestamp)); @@ -607,7 +605,9 @@ gst_base_rtp_audio_payload_flush (GstBaseRTPAudioPayload * baseaudiopayload, * anything. */ buffer = gst_adapter_take_buffer (adapter, payload_len); - ret = gst_base_rtp_audio_payload_push_buffer (baseaudiopayload, buffer); + ret = + gst_base_rtp_audio_payload_push_buffer (baseaudiopayload, buffer, + timestamp); } else { /* create buffer to hold the payload */ outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); @@ -814,23 +814,22 @@ gst_base_rtp_audio_payload_handle_buffer (GstBaseRTPPayload * guint align; guint size; gboolean discont; + GstClockTime timestamp; ret = GST_FLOW_OK; payload = GST_BASE_RTP_AUDIO_PAYLOAD_CAST (basepayload); priv = payload->priv; + timestamp = GST_BUFFER_TIMESTAMP (buffer); discont = GST_BUFFER_IS_DISCONT (buffer); if (discont) { - GstClockTime timestamp; GST_DEBUG_OBJECT (payload, "Got DISCONT"); /* flush everything out of the adapter, mark DISCONT */ ret = gst_base_rtp_audio_payload_flush (payload, -1, -1); priv->discont = TRUE; - timestamp = GST_BUFFER_TIMESTAMP (buffer); - /* get the distance between the timestamp gap and produce the same gap in * the RTP timestamps */ if (priv->last_timestamp != -1 && timestamp != -1) { @@ -878,7 +877,7 @@ gst_base_rtp_audio_payload_handle_buffer (GstBaseRTPPayload * /* If buffer fits on an RTP packet, let's just push it through * this will check against max_ptime and max_mtu */ GST_DEBUG_OBJECT (payload, "Fast packet push"); - ret = gst_base_rtp_audio_payload_push_buffer (payload, buffer); + ret = gst_base_rtp_audio_payload_push_buffer (payload, buffer, timestamp); } else { /* push the buffer in the adapter */ gst_adapter_push (priv->adapter, buffer); diff --git a/gst-libs/gst/rtp/gstbasertpdepayload.c b/gst-libs/gst/rtp/gstbasertpdepayload.c index 6c9723ab5c..fdc861d205 100644 --- a/gst-libs/gst/rtp/gstbasertpdepayload.c +++ b/gst-libs/gst/rtp/gstbasertpdepayload.c @@ -97,6 +97,8 @@ static void gst_base_rtp_depayload_set_gst_timestamp (GstBaseRTPDepayload * filter, guint32 rtptime, GstBuffer * buf); static gboolean gst_base_rtp_depayload_packet_lost (GstBaseRTPDepayload * filter, GstEvent * event); +static gboolean gst_base_rtp_depayload_handle_event (GstBaseRTPDepayload * + filter, GstEvent * event); GST_BOILERPLATE (GstBaseRTPDepayload, gst_base_rtp_depayload, GstElement, GST_TYPE_ELEMENT); @@ -142,6 +144,7 @@ gst_base_rtp_depayload_class_init (GstBaseRTPDepayloadClass * klass) klass->set_gst_timestamp = gst_base_rtp_depayload_set_gst_timestamp; klass->packet_lost = gst_base_rtp_depayload_packet_lost; + klass->handle_event = gst_base_rtp_depayload_handle_event; GST_DEBUG_CATEGORY_INIT (basertpdepayload_debug, "basertpdepayload", 0, "Base class for RTP Depayloaders"); @@ -407,14 +410,12 @@ no_process: } static gboolean -gst_base_rtp_depayload_handle_sink_event (GstPad * pad, GstEvent * event) +gst_base_rtp_depayload_handle_event (GstBaseRTPDepayload * filter, + GstEvent * event) { - GstBaseRTPDepayload *filter; gboolean res = TRUE; gboolean forward = TRUE; - filter = GST_BASE_RTP_DEPAYLOAD (GST_OBJECT_PARENT (pad)); - switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED); @@ -474,6 +475,22 @@ gst_base_rtp_depayload_handle_sink_event (GstPad * pad, GstEvent * event) return res; } +static gboolean +gst_base_rtp_depayload_handle_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean res = FALSE; + GstBaseRTPDepayload *filter; + GstBaseRTPDepayloadClass *bclass; + + filter = GST_BASE_RTP_DEPAYLOAD (GST_OBJECT_PARENT (pad)); + + bclass = GST_BASE_RTP_DEPAYLOAD_GET_CLASS (filter); + if (bclass->handle_event) + res = bclass->handle_event (filter, event); + + return res; +} + static GstEvent * create_segment_event (GstBaseRTPDepayload * filter, gboolean update, GstClockTime position) @@ -496,30 +513,58 @@ create_segment_event (GstBaseRTPDepayload * filter, gboolean update, return event; } -static GstFlowReturn -gst_base_rtp_depayload_push_full (GstBaseRTPDepayload * filter, - gboolean do_ts, guint32 rtptime, GstBuffer * out_buf) +typedef struct { - GstFlowReturn ret; - GstCaps *srccaps; + GstBaseRTPDepayload *depayload; GstBaseRTPDepayloadClass *bclass; + GstCaps *caps; + gboolean do_ts; + gboolean rtptime; +} HeaderData; + +static GstBufferListItem +set_headers (GstBuffer ** buffer, guint group, guint idx, HeaderData * data) +{ + GstBaseRTPDepayload *depayload = data->depayload; + + *buffer = gst_buffer_make_metadata_writable (*buffer); + gst_buffer_set_caps (*buffer, data->caps); + + /* set the timestamp if we must and can */ + if (data->bclass->set_gst_timestamp && data->do_ts) + data->bclass->set_gst_timestamp (depayload, data->rtptime, *buffer); + + if (G_UNLIKELY (depayload->priv->discont)) { + GST_LOG_OBJECT (depayload, "Marking DISCONT on output buffer"); + GST_BUFFER_FLAG_SET (*buffer, GST_BUFFER_FLAG_DISCONT); + depayload->priv->discont = FALSE; + } + + return GST_BUFFER_LIST_SKIP_GROUP; +} + +static GstFlowReturn +gst_base_rtp_depayload_prepare_push (GstBaseRTPDepayload * filter, + gboolean do_ts, guint32 rtptime, gboolean is_list, gpointer obj) +{ GstBaseRTPDepayloadPrivate *priv; + HeaderData data; priv = filter->priv; - /* almost certainly required */ - out_buf = gst_buffer_make_metadata_writable (out_buf); + data.depayload = filter; + data.caps = GST_PAD_CAPS (filter->srcpad); + data.rtptime = rtptime; + data.do_ts = do_ts; + data.bclass = GST_BASE_RTP_DEPAYLOAD_GET_CLASS (filter); - /* set the caps if any */ - srccaps = GST_PAD_CAPS (filter->srcpad); - if (G_LIKELY (srccaps)) - gst_buffer_set_caps (out_buf, srccaps); - - bclass = GST_BASE_RTP_DEPAYLOAD_GET_CLASS (filter); - - /* set the timestamp if we must and can */ - if (bclass->set_gst_timestamp && do_ts) - bclass->set_gst_timestamp (filter, rtptime, out_buf); + if (is_list) { + GstBufferList **blist = obj; + gst_buffer_list_foreach (*blist, (GstBufferListFunc) set_headers, &data); + } else { + GstBuffer **buf = obj; + set_headers (buf, 0, 0, &data); + } /* if this is the first buffer send a NEWSEGMENT */ if (G_UNLIKELY (filter->need_newsegment)) { @@ -533,20 +578,7 @@ gst_base_rtp_depayload_push_full (GstBaseRTPDepayload * filter, GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer"); } - if (G_UNLIKELY (priv->discont)) { - GST_LOG_OBJECT (filter, "Marking DISCONT on output buffer"); - GST_BUFFER_FLAG_SET (out_buf, GST_BUFFER_FLAG_DISCONT); - priv->discont = FALSE; - } - - /* push it */ - GST_LOG_OBJECT (filter, "Pushing buffer size %d, timestamp %" GST_TIME_FORMAT, - GST_BUFFER_SIZE (out_buf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out_buf))); - - ret = gst_pad_push (filter->srcpad, out_buf); - - return ret; + return GST_FLOW_OK; } /** @@ -569,7 +601,18 @@ GstFlowReturn gst_base_rtp_depayload_push_ts (GstBaseRTPDepayload * filter, guint32 timestamp, GstBuffer * out_buf) { - return gst_base_rtp_depayload_push_full (filter, TRUE, timestamp, out_buf); + GstFlowReturn res; + + res = + gst_base_rtp_depayload_prepare_push (filter, TRUE, timestamp, FALSE, + &out_buf); + + if (G_LIKELY (res == GST_FLOW_OK)) + res = gst_pad_push (filter->srcpad, out_buf); + else + gst_buffer_unref (out_buf); + + return res; } /** @@ -589,7 +632,44 @@ gst_base_rtp_depayload_push_ts (GstBaseRTPDepayload * filter, guint32 timestamp, GstFlowReturn gst_base_rtp_depayload_push (GstBaseRTPDepayload * filter, GstBuffer * out_buf) { - return gst_base_rtp_depayload_push_full (filter, FALSE, 0, out_buf); + GstFlowReturn res; + + res = gst_base_rtp_depayload_prepare_push (filter, FALSE, 0, FALSE, &out_buf); + + if (G_LIKELY (res == GST_FLOW_OK)) + res = gst_pad_push (filter->srcpad, out_buf); + else + gst_buffer_unref (out_buf); + + return res; +} + +/** + * gst_base_rtp_depayload_push_list: + * @filter: a #GstBaseRTPDepayload + * @out_list: a #GstBufferList + * + * Push @out_list to the peer of @filter. This function takes ownership of + * @out_list. + * + * Returns: a #GstFlowReturn. + * + * Since: 0.10.32 + */ +GstFlowReturn +gst_base_rtp_depayload_push_list (GstBaseRTPDepayload * filter, + GstBufferList * out_list) +{ + GstFlowReturn res; + + res = gst_base_rtp_depayload_prepare_push (filter, TRUE, 0, TRUE, &out_list); + + if (G_LIKELY (res == GST_FLOW_OK)) + res = gst_pad_push_list (filter->srcpad, out_list); + else + gst_buffer_list_unref (out_list); + + return res; } /* convert the PacketLost event form a jitterbuffer to a segment update. diff --git a/gst-libs/gst/rtp/gstbasertpdepayload.h b/gst-libs/gst/rtp/gstbasertpdepayload.h index d20ed91489..000c1167ff 100644 --- a/gst-libs/gst/rtp/gstbasertpdepayload.h +++ b/gst-libs/gst/rtp/gstbasertpdepayload.h @@ -119,8 +119,13 @@ struct _GstBaseRTPDepayloadClass * The default implementation of this message pushes a segment update. */ gboolean (*packet_lost) (GstBaseRTPDepayload *filter, GstEvent *event); + /* the default implementation does the default actions for events but + * implementation can override. + * Since: 0.10.32 */ + gboolean (*handle_event) (GstBaseRTPDepayload * filter, GstEvent * event); + /*< private >*/ - gpointer _gst_reserved[GST_PADDING-1]; + gpointer _gst_reserved[GST_PADDING-2]; }; GType gst_base_rtp_depayload_get_type (void); @@ -128,6 +133,8 @@ GType gst_base_rtp_depayload_get_type (void); GstFlowReturn gst_base_rtp_depayload_push (GstBaseRTPDepayload *filter, GstBuffer *out_buf); GstFlowReturn gst_base_rtp_depayload_push_ts (GstBaseRTPDepayload *filter, guint32 timestamp, GstBuffer *out_buf); +GstFlowReturn gst_base_rtp_depayload_push_list (GstBaseRTPDepayload *filter, GstBufferList *out_list); + G_END_DECLS diff --git a/gst-libs/gst/rtp/gstbasertppayload.c b/gst-libs/gst/rtp/gstbasertppayload.c index e9c98811e7..a389185589 100644 --- a/gst-libs/gst/rtp/gstbasertppayload.c +++ b/gst-libs/gst/rtp/gstbasertppayload.c @@ -402,15 +402,25 @@ gst_basertppayload_event (GstPad * pad, GstEvent * event) case GST_EVENT_NEWSEGMENT: { gboolean update; - gdouble rate; + gdouble rate, arate; GstFormat fmt; gint64 start, stop, position; + GstSegment *segment; - gst_event_parse_new_segment (event, &update, &rate, &fmt, &start, &stop, - &position); - gst_segment_set_newsegment (&basertppayload->segment, update, rate, fmt, - start, stop, position); + segment = &basertppayload->segment; + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt, + &start, &stop, &position); + gst_segment_set_newsegment_full (segment, update, rate, arate, fmt, start, + stop, position); + + GST_DEBUG_OBJECT (basertppayload, + "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " + "format %d, " + "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" + G_GINT64_FORMAT ", accum %" G_GINT64_FORMAT, update, rate, arate, + segment->format, segment->start, segment->stop, segment->time, + segment->accum); /* fallthrough */ } default: @@ -777,12 +787,16 @@ gst_basertppayload_prepare_push (GstBaseRTPPayload * payload, rtime = gst_segment_to_running_time (&payload->segment, GST_FORMAT_TIME, data.timestamp); - GST_LOG_OBJECT (payload, - "Using running_time %" GST_TIME_FORMAT " for RTP timestamp", - GST_TIME_ARGS (rtime)); - - rtime = gst_util_uint64_scale_int (rtime, payload->clock_rate, GST_SECOND); - + if (rtime == -1) { + GST_LOG_OBJECT (payload, "Clipped timestamp, using base RTP timestamp"); + rtime = 0; + } else { + GST_LOG_OBJECT (payload, + "Using running_time %" GST_TIME_FORMAT " for RTP timestamp", + GST_TIME_ARGS (rtime)); + rtime = + gst_util_uint64_scale_int (rtime, payload->clock_rate, GST_SECOND); + } /* add running_time in clock-rate units to the base timestamp */ data.rtptime = payload->ts_base + rtime; } else { @@ -810,8 +824,8 @@ gst_basertppayload_prepare_push (GstBaseRTPPayload * payload, GST_BUFFER_SIZE (GST_BUFFER (obj)), payload->seqnum, data.rtptime, GST_TIME_ARGS (data.timestamp)); - if (g_atomic_int_compare_and_exchange (&payload->priv-> - notified_first_timestamp, 1, 0)) { + if (g_atomic_int_compare_and_exchange (&payload-> + priv->notified_first_timestamp, 1, 0)) { g_object_notify (G_OBJECT (payload), "timestamp"); g_object_notify (G_OBJECT (payload), "seqnum"); } diff --git a/gst-libs/gst/rtp/gstrtpbuffer.c b/gst-libs/gst/rtp/gstrtpbuffer.c index 68fc67a95d..8f94d3f5d4 100644 --- a/gst-libs/gst/rtp/gstrtpbuffer.c +++ b/gst-libs/gst/rtp/gstrtpbuffer.c @@ -453,33 +453,20 @@ gst_rtp_buffer_list_validate (GstBufferList * list) guint8 *packet_payload; guint payload_size; guint packet_size; + guint j, n_buffers; - /* each group should consists of 2 buffers: one containing the RTP header - * and the other one the payload, FIXME, relax the requirement of only one - * payload buffer. */ - if (gst_buffer_list_iterator_n_buffers (it) != 2) + /* each group should consists of at least 1 buffer: The first buffer always + * contains the complete RTP header. Next buffers contain the payload */ + n_buffers = gst_buffer_list_iterator_n_buffers (it); + if (n_buffers < 1) goto invalid_list; - /* get the RTP header */ + /* get the RTP header (and if n_buffers == 1 also the payload) */ rtpbuf = gst_buffer_list_iterator_next (it); packet_header = GST_BUFFER_DATA (rtpbuf); if (packet_header == NULL) goto invalid_list; - /* get the payload */ - paybuf = gst_buffer_list_iterator_next (it); - packet_payload = GST_BUFFER_DATA (paybuf); - if (packet_payload == NULL) { - goto invalid_list; - } - payload_size = GST_BUFFER_SIZE (paybuf); - if (payload_size == 0) { - goto invalid_list; - } - - /* the size of the RTP packet within the current group */ - packet_size = GST_BUFFER_SIZE (rtpbuf) + payload_size; - /* check the sequence number */ if (G_UNLIKELY (i == 0)) { prev_seqnum = g_ntohs (GST_RTP_HEADER_SEQ (packet_header)); @@ -489,6 +476,25 @@ gst_rtp_buffer_list_validate (GstBufferList * list) goto invalid_list; } + packet_size = GST_BUFFER_SIZE (rtpbuf); + packet_payload = NULL; + payload_size = 0; + + /* get the payload buffers */ + for (j = 1; j < n_buffers; j++) { + /* get the payload */ + paybuf = gst_buffer_list_iterator_next (it); + + if ((packet_payload = GST_BUFFER_DATA (paybuf)) == NULL) + goto invalid_list; + + if ((payload_size = GST_BUFFER_SIZE (paybuf)) == 0) + goto invalid_list; + + /* the size of the RTP packet within the current group */ + packet_size += payload_size; + } + /* validate packet */ if (!validate_data (packet_header, packet_size, packet_payload, payload_size)) { @@ -739,7 +745,7 @@ gst_rtp_buffer_get_extension_data (GstBuffer * buffer, guint16 * bits, * * Returns: True if done. * - * Since : 0.10.18 + * Since: 0.10.18 */ gboolean gst_rtp_buffer_set_extension_data (GstBuffer * buffer, guint16 bits, diff --git a/gst-libs/gst/rtsp/Makefile.am b/gst-libs/gst/rtsp/Makefile.am index cb7653e473..4ed474273d 100644 --- a/gst-libs/gst/rtsp/Makefile.am +++ b/gst-libs/gst/rtsp/Makefile.am @@ -56,8 +56,8 @@ gir_headers=$(patsubst %,$(srcdir)/%, $(libgstrtspinclude_HEADERS)) gir_headers+=$(patsubst %,$(builddir)/%, $(nodist_libgstrtspinclude_HEADERS)) gir_sources=$(patsubst %,$(srcdir)/%, $(libgstrtsp_@GST_MAJORMINOR@_la_SOURCES)) gir_sources+=$(patsubst %,$(builddir)/%, $(nodist_libgstrtsp_@GST_MAJORMINOR@_la_SOURCES)) -gir_cincludes=$(patsubst %,--c-include='gst/audio/%',$(libgstrtspinclude_HEADERS)) -gir_cincludes+=$(patsubst %,--c-include='gst/audio/%',$(nodist_libgstrtspinclude_HEADERS)) +gir_cincludes=$(patsubst %,--c-include='gst/rtsp/%',$(libgstrtspinclude_HEADERS)) +gir_cincludes+=$(patsubst %,--c-include='gst/rtsp/%',$(nodist_libgstrtspinclude_HEADERS)) GstRtsp-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstrtsp-@GST_MAJORMINOR@.la $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ @@ -90,7 +90,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=$(builddir)/../sdp \ diff --git a/gst-libs/gst/rtsp/gstrtsptransport.h b/gst-libs/gst/rtsp/gstrtsptransport.h index 9250b02981..995d753a3f 100644 --- a/gst-libs/gst/rtsp/gstrtsptransport.h +++ b/gst-libs/gst/rtsp/gstrtsptransport.h @@ -96,6 +96,9 @@ typedef enum { #define GST_TYPE_RTSP_LOWER_TRANS (gst_rtsp_lower_trans_get_type()) GType gst_rtsp_lower_trans_get_type (void); +typedef struct _GstRTSPRange GstRTSPRange; +typedef struct _GstRTSPTransport GstRTSPTransport; + /** * GstRTSPRange: * @min: minimum value of the range @@ -103,10 +106,11 @@ GType gst_rtsp_lower_trans_get_type (void); * * A type to specify a range. */ -typedef struct _GstRTSPRange { + +struct _GstRTSPRange { gint min; gint max; -} GstRTSPRange; +}; /** * GstRTSPTransport: @@ -128,7 +132,8 @@ typedef struct _GstRTSPRange { * * A structure holding the RTSP transport values. */ -typedef struct _GstRTSPTransport { + +struct _GstRTSPTransport { GstRTSPTransMode trans; GstRTSPProfile profile; GstRTSPLowerTrans lower_transport; @@ -151,7 +156,7 @@ typedef struct _GstRTSPTransport { /* RTP specific */ guint ssrc; -} GstRTSPTransport; +}; GstRTSPResult gst_rtsp_transport_new (GstRTSPTransport **transport); GstRTSPResult gst_rtsp_transport_init (GstRTSPTransport *transport); diff --git a/gst-libs/gst/rtsp/gstrtspurl.c b/gst-libs/gst/rtsp/gstrtspurl.c index 0a71091cf7..6973e09c38 100644 --- a/gst-libs/gst/rtsp/gstrtspurl.c +++ b/gst-libs/gst/rtsp/gstrtspurl.c @@ -341,3 +341,76 @@ gst_rtsp_url_get_request_uri (const GstRTSPUrl * url) return uri; } + +static int +hex_to_int (gchar c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + return -1; +} + +static void +unescape_path_component (gchar * comp) +{ + guint len = strlen (comp); + guint i; + + for (i = 0; i + 2 < len; i++) + if (comp[i] == '%') { + int a, b; + + a = hex_to_int (comp[i + 1]); + b = hex_to_int (comp[i + 2]); + + /* The a||b check is to ensure that the byte is not '\0' */ + if (a >= 0 && b >= 0 && (a || b)) { + comp[i] = (gchar) (a * 16 + b); + memmove (comp + i + 1, comp + i + 3, len - i - 3); + len -= 2; + comp[len] = '\0'; + } + } +} + +/** + * gst_rtsp_url_decode_path_components: + * @url: a #GstRTSPUrl + * + * Splits the path of @url on '/' boundaries, decoding the resulting components, + * + * The decoding performed by this routine is "URI decoding", as defined in RFC + * 3986, commonly known as percent-decoding. For example, a string "foo%2fbar" + * will decode to "foo/bar" -- the %2f being replaced by the corresponding byte + * with hex value 0x2f. Note that there is no guarantee that the resulting byte + * sequence is valid in any given encoding. As a special case, %00 is not + * unescaped to NUL, as that would prematurely terminate the string. + * + * Also note that since paths usually start with a slash, the first component + * will usually be the empty string. + * + * Returns: a string vector. g_strfreev() after usage. + * + * Since: 0.10.32 + */ +gchar ** +gst_rtsp_url_decode_path_components (const GstRTSPUrl * url) +{ + gchar **ret; + guint i; + + g_return_val_if_fail (url != NULL, NULL); + g_return_val_if_fail (url->abspath != NULL, NULL); + + ret = g_strsplit (url->abspath, "/", -1); + + for (i = 0; ret[i]; i++) + unescape_path_component (ret[i]); + + return ret; +} diff --git a/gst-libs/gst/rtsp/gstrtspurl.h b/gst-libs/gst/rtsp/gstrtspurl.h index db9620ec7a..b34ee870fc 100644 --- a/gst-libs/gst/rtsp/gstrtspurl.h +++ b/gst-libs/gst/rtsp/gstrtspurl.h @@ -92,6 +92,8 @@ GstRTSPResult gst_rtsp_url_parse (const gchar *urlstr, GstRTSPUrl GstRTSPUrl* gst_rtsp_url_copy (const GstRTSPUrl *url); void gst_rtsp_url_free (GstRTSPUrl *url); gchar* gst_rtsp_url_get_request_uri (const GstRTSPUrl *url); +gchar** gst_rtsp_url_decode_path_components + (const GstRTSPUrl *url); GstRTSPResult gst_rtsp_url_set_port (GstRTSPUrl *url, guint16 port); GstRTSPResult gst_rtsp_url_get_port (const GstRTSPUrl *url, guint16 *port); diff --git a/gst-libs/gst/sdp/Makefile.am b/gst-libs/gst/sdp/Makefile.am index 1ba7dc3a29..6c8dae21d9 100644 --- a/gst-libs/gst/sdp/Makefile.am +++ b/gst-libs/gst/sdp/Makefile.am @@ -16,7 +16,7 @@ BUILT_GIRSOURCES = GstSdp-@GST_MAJORMINOR@.gir gir_headers=$(patsubst %,$(srcdir)/%, $(libgstsdpinclude_HEADERS)) gir_sources=$(patsubst %,$(srcdir)/%, $(libgstsdp_@GST_MAJORMINOR@_la_SOURCES)) -gir_cincludes=$(patsubst %,--c-include='gst/audio/%',$(libgstsdpinclude_HEADERS)) +gir_cincludes=$(patsubst %,--c-include='gst/sdp/%',$(libgstsdpinclude_HEADERS)) GstSdp-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstsdp-@GST_MAJORMINOR@.la $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ @@ -45,7 +45,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/tag/Makefile.am b/gst-libs/gst/tag/Makefile.am index 7443059f01..9dce438e7b 100644 --- a/gst-libs/gst/tag/Makefile.am +++ b/gst-libs/gst/tag/Makefile.am @@ -23,7 +23,7 @@ BUILT_GIRSOURCES = GstTag-@GST_MAJORMINOR@.gir gir_headers=$(patsubst %,$(srcdir)/%, $(libgsttaginclude_HEADERS)) gir_sources=$(patsubst %,$(srcdir)/%, $(libgsttag_@GST_MAJORMINOR@_la_SOURCES)) -gir_cincludes=$(patsubst %,--c-include='gst/audio/%',$(libgsttainclude_HEADERS)) +gir_cincludes=$(patsubst %,--c-include='gst/tag/%',$(libgsttaginclude_HEADERS)) GstTag-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgsttag-@GST_MAJORMINOR@.la $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ @@ -54,7 +54,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/tag/gstexiftag.c b/gst-libs/gst/tag/gstexiftag.c index e72bf3a3a6..cc39acd1e3 100644 --- a/gst-libs/gst/tag/gstexiftag.c +++ b/gst-libs/gst/tag/gstexiftag.c @@ -752,7 +752,7 @@ static void write_exif_undefined_tag (GstExifWriter * writer, guint16 tag, const guint8 * data, gint size) { - guint32 offset; + guint32 offset = 0; if (size > 4) { /* we only use the data offset here, later we add up the @@ -773,7 +773,7 @@ static void write_exif_ascii_tag (GstExifWriter * writer, guint16 tag, const gchar * str) { gint size; - guint32 offset; + guint32 offset = 0; size = strlen (str) + 1; diff --git a/gst-libs/gst/tag/gstid3tag.c b/gst-libs/gst/tag/gstid3tag.c index fc13fb2c5a..8c9b352643 100644 --- a/gst-libs/gst/tag/gstid3tag.c +++ b/gst-libs/gst/tag/gstid3tag.c @@ -201,6 +201,7 @@ static const GstTagEntryMatch tag_matches[] = { {GST_TAG_COMPOSER, "TCOM"}, {GST_TAG_COPYRIGHT, "TCOP"}, {GST_TAG_COPYRIGHT_URI, "WCOP"}, + {GST_TAG_ENCODED_BY, "TENC"}, {GST_TAG_GENRE, "TCON"}, {GST_TAG_DATE, "TDRC"}, {GST_TAG_COMMENT, "COMM"}, diff --git a/gst-libs/gst/tag/gsttagdemux.c b/gst-libs/gst/tag/gsttagdemux.c index fdc09b83ce..8bb331434e 100644 --- a/gst-libs/gst/tag/gsttagdemux.c +++ b/gst-libs/gst/tag/gsttagdemux.c @@ -156,6 +156,7 @@ static GstStateChangeReturn gst_tag_demux_change_state (GstElement * element, static gboolean gst_tag_demux_pad_query (GstPad * pad, GstQuery * query); static const GstQueryType *gst_tag_demux_get_query_types (GstPad * pad); static gboolean gst_tag_demux_get_upstream_size (GstTagDemux * tagdemux); +static void gst_tag_demux_send_pending_events (GstTagDemux * tagdemux); static void gst_tag_demux_send_tag_event (GstTagDemux * tagdemux); static gboolean gst_tag_demux_send_new_segment (GstTagDemux * tagdemux); @@ -668,8 +669,6 @@ gst_tag_demux_chain (GstPad * pad, GstBuffer * buf) return GST_FLOW_UNEXPECTED; } if (outbuf) { - GList *events; - if (G_UNLIKELY (demux->priv->srcpad == NULL)) { gst_buffer_unref (outbuf); return GST_FLOW_ERROR; @@ -685,17 +684,7 @@ gst_tag_demux_chain (GstPad * pad, GstBuffer * buf) } /* send any pending events we cached */ - GST_OBJECT_LOCK (demux); - events = demux->priv->pending_events; - demux->priv->pending_events = NULL; - GST_OBJECT_UNLOCK (demux); - - while (events != NULL) { - GST_DEBUG_OBJECT (demux->priv->srcpad, "sending cached %s event: %" - GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (events->data), events->data); - gst_pad_push_event (demux->priv->srcpad, GST_EVENT (events->data)); - events = g_list_delete_link (events, events); - } + gst_tag_demux_send_pending_events (demux); /* Send our own pending tag event */ if (demux->priv->send_tag_event) { @@ -1333,6 +1322,13 @@ gst_tag_demux_src_getrange (GstPad * srcpad, { GstTagDemux *demux = GST_TAG_DEMUX (GST_PAD_PARENT (srcpad)); + /* downstream in pull mode won't miss a newsegment event, + * but it likely appreciates other (tag) events */ + if (demux->priv->need_newseg) { + gst_tag_demux_send_pending_events (demux); + demux->priv->need_newseg = FALSE; + } + if (demux->priv->send_tag_event) { gst_tag_demux_send_tag_event (demux); demux->priv->send_tag_event = FALSE; @@ -1418,6 +1414,25 @@ gst_tag_demux_get_query_types (GstPad * pad) return types; } +static void +gst_tag_demux_send_pending_events (GstTagDemux * demux) +{ + GList *events; + + /* send any pending events we cached */ + GST_OBJECT_LOCK (demux); + events = demux->priv->pending_events; + demux->priv->pending_events = NULL; + GST_OBJECT_UNLOCK (demux); + + while (events != NULL) { + GST_DEBUG_OBJECT (demux->priv->srcpad, "sending cached %s event: %" + GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (events->data), events->data); + gst_pad_push_event (demux->priv->srcpad, GST_EVENT (events->data)); + events = g_list_delete_link (events, events); + } +} + static void gst_tag_demux_send_tag_event (GstTagDemux * demux) { diff --git a/gst-libs/gst/tag/gstxmptag.c b/gst-libs/gst/tag/gstxmptag.c index 758b9776d2..c3bc6360e8 100644 --- a/gst-libs/gst/tag/gstxmptag.c +++ b/gst-libs/gst/tag/gstxmptag.c @@ -957,15 +957,6 @@ struct _GstXmpNamespaceMap const gchar *original_ns; gchar *gstreamer_ns; }; -static GstXmpNamespaceMap ns_map[] = { - {"dc", NULL}, - {"exif", NULL}, - {"tiff", NULL}, - {"xap", NULL}, - {"photoshop", NULL}, - {"Iptc4xmpCore", NULL}, - {NULL, NULL} -}; /* parsing */ @@ -1021,7 +1012,7 @@ read_one_tag (GstTagList * list, const gchar * tag, XmpTag * xmptag, gint num_digits = 0; /* find the number of digits */ - while (isdigit (usec_str[num_digits++]) && num_digits < 6); + while (isdigit ((gint) usec_str[num_digits++]) && num_digits < 6); if (num_digits > 0) { /* fill up to 6 digits with 0 */ @@ -1153,6 +1144,16 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer) XmpTag *last_xmp_tag = NULL; GSList *pending_tags = NULL; + GstXmpNamespaceMap ns_map[] = { + {"dc", NULL}, + {"exif", NULL}, + {"tiff", NULL}, + {"xap", NULL}, + {"photoshop", NULL}, + {"Iptc4xmpCore", NULL}, + {NULL, NULL} + }; + xmp_tags_initialize (); g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); diff --git a/gst-libs/gst/video/Makefile.am b/gst-libs/gst/video/Makefile.am index 793ede5330..fd91258f0e 100644 --- a/gst-libs/gst/video/Makefile.am +++ b/gst-libs/gst/video/Makefile.am @@ -68,7 +68,8 @@ typelibsdir = $(libdir)/girepository-1.0/ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) %.typelib: %.gir $(INTROSPECTION_COMPILER) - $(AM_V_GEN)$(INTROSPECTION_COMPILER) \ + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ --includedir=$(srcdir) \ --includedir=$(builddir) \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@` \ diff --git a/gst-libs/gst/video/video.c b/gst-libs/gst/video/video.c index 6635125b2e..d9569a4e8c 100644 --- a/gst-libs/gst/video/video.c +++ b/gst-libs/gst/video/video.c @@ -367,7 +367,9 @@ gst_video_format_parse_caps (GstCaps * caps, GstVideoFormat * format, } have_alpha = gst_structure_get_int (structure, "alpha_mask", &alpha_mask); - if (depth == 24 && bpp == 32 && endianness == G_BIG_ENDIAN) { + if (depth == 30 && bpp == 32 && endianness == G_BIG_ENDIAN) { + *format = GST_VIDEO_FORMAT_r210; + } else if (depth == 24 && bpp == 32 && endianness == G_BIG_ENDIAN) { *format = gst_video_format_from_rgb32_masks (red_mask, green_mask, blue_mask); if (*format == GST_VIDEO_FORMAT_UNKNOWN) { @@ -395,6 +397,15 @@ gst_video_format_parse_caps (GstCaps * caps, GstVideoFormat * format, } } else if (depth == 8 && bpp == 8) { *format = GST_VIDEO_FORMAT_RGB8_PALETTED; + } else if (depth == 64 && bpp == 64) { + *format = gst_video_format_from_rgba32_masks (red_mask, green_mask, + blue_mask, alpha_mask); + if (*format == GST_VIDEO_FORMAT_ARGB) { + *format = GST_VIDEO_FORMAT_ARGB64; + } else { + *format = GST_VIDEO_FORMAT_UNKNOWN; + ok = FALSE; + } } else { ok = FALSE; } @@ -566,9 +577,9 @@ gst_video_format_new_caps (GstVideoFormat format, int width, } if (gst_video_format_is_rgb (format)) { GstCaps *caps; - int red_mask; - int blue_mask; - int green_mask; + int red_mask = 0; + int blue_mask = 0; + int green_mask = 0; int alpha_mask; int depth; int bpp; @@ -615,10 +626,25 @@ gst_video_format_new_caps (GstVideoFormat format, int width, depth = 8; have_alpha = FALSE; break; + case GST_VIDEO_FORMAT_ARGB64: + bpp = 64; + depth = 64; + have_alpha = TRUE; + break; + case GST_VIDEO_FORMAT_r210: + bpp = 32; + depth = 30; + have_alpha = FALSE; + break; default: return NULL; } - if (bpp == 32 || bpp == 24) { + if (bpp == 32 && depth == 30) { + red_mask = 0x3ff00000; + green_mask = 0x000ffc00; + blue_mask = 0x000003ff; + have_alpha = FALSE; + } else if (bpp == 32 || bpp == 24 || bpp == 64) { if (bpp == 32) { mask = 0xff000000; } else { @@ -797,6 +823,8 @@ gst_video_format_from_fourcc (guint32 fourcc) return GST_VIDEO_FORMAT_YVU9; case GST_MAKE_FOURCC ('I', 'Y', 'U', '1'): return GST_VIDEO_FORMAT_IYU1; + case GST_MAKE_FOURCC ('A', 'Y', '6', '4'): + return GST_VIDEO_FORMAT_AYUV64; default: return GST_VIDEO_FORMAT_UNKNOWN; } @@ -862,6 +890,8 @@ gst_video_format_to_fourcc (GstVideoFormat format) return GST_MAKE_FOURCC ('Y', 'V', 'U', '9'); case GST_VIDEO_FORMAT_IYU1: return GST_MAKE_FOURCC ('I', 'Y', 'U', '1'); + case GST_VIDEO_FORMAT_AYUV64: + return GST_MAKE_FOURCC ('A', 'Y', '6', '4'); default: return 0; } @@ -1000,6 +1030,7 @@ gst_video_format_is_rgb (GstVideoFormat format) case GST_VIDEO_FORMAT_YUV9: case GST_VIDEO_FORMAT_YVU9: case GST_VIDEO_FORMAT_IYU1: + case GST_VIDEO_FORMAT_AYUV64: return FALSE; case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_BGRx: @@ -1016,6 +1047,8 @@ gst_video_format_is_rgb (GstVideoFormat format) case GST_VIDEO_FORMAT_RGB15: case GST_VIDEO_FORMAT_BGR15: case GST_VIDEO_FORMAT_RGB8_PALETTED: + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_r210: return TRUE; default: return FALSE; @@ -1057,6 +1090,7 @@ gst_video_format_is_yuv (GstVideoFormat format) case GST_VIDEO_FORMAT_YUV9: case GST_VIDEO_FORMAT_YVU9: case GST_VIDEO_FORMAT_IYU1: + case GST_VIDEO_FORMAT_AYUV64: return TRUE; case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_BGRx: @@ -1073,6 +1107,8 @@ gst_video_format_is_yuv (GstVideoFormat format) case GST_VIDEO_FORMAT_RGB15: case GST_VIDEO_FORMAT_BGR15: case GST_VIDEO_FORMAT_RGB8_PALETTED: + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_r210: return FALSE; default: return FALSE; @@ -1146,6 +1182,8 @@ gst_video_format_has_alpha (GstVideoFormat format) case GST_VIDEO_FORMAT_ABGR: case GST_VIDEO_FORMAT_A420: case GST_VIDEO_FORMAT_RGB8_PALETTED: + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: return TRUE; case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_BGRx: @@ -1157,12 +1195,83 @@ gst_video_format_has_alpha (GstVideoFormat format) case GST_VIDEO_FORMAT_BGR16: case GST_VIDEO_FORMAT_RGB15: case GST_VIDEO_FORMAT_BGR15: + case GST_VIDEO_FORMAT_r210: return FALSE; default: return FALSE; } } +/** + * gst_video_format_get_component_depth: + * @format: a #GstVideoFormat + * + * Returns the number of bits used to encode an individual pixel of + * a given component. Typically this is 8, although higher and lower + * values are possible for some formats. + * + * Since: 0.10.33 + * + * Returns: depth of component + */ +int +gst_video_format_get_component_depth (GstVideoFormat format, int component) +{ + if (component == 3 && !gst_video_format_has_alpha (format)) + return 0; + + switch (format) { + case GST_VIDEO_FORMAT_RGB16: + case GST_VIDEO_FORMAT_BGR16: + if (component == 1) + return 6; + return 5; + case GST_VIDEO_FORMAT_RGB15: + case GST_VIDEO_FORMAT_BGR15: + return 5; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_YVYU: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_Y41B: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_NV21: + case GST_VIDEO_FORMAT_v308: + case GST_VIDEO_FORMAT_Y800: + case GST_VIDEO_FORMAT_YUV9: + case GST_VIDEO_FORMAT_YVU9: + case GST_VIDEO_FORMAT_IYU1: + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_A420: + case GST_VIDEO_FORMAT_RGB8_PALETTED: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_BGR: + default: + return 8; + case GST_VIDEO_FORMAT_v210: + case GST_VIDEO_FORMAT_UYVP: + case GST_VIDEO_FORMAT_r210: + return 10; + case GST_VIDEO_FORMAT_Y16: + case GST_VIDEO_FORMAT_v216: + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + return 16; + } + +} + /** * gst_video_format_get_row_stride: * @format: a #GstVideoFormat @@ -1210,6 +1319,7 @@ gst_video_format_get_row_stride (GstVideoFormat format, int component, case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_r210: return width * 4; case GST_VIDEO_FORMAT_RGB16: case GST_VIDEO_FORMAT_BGR16: @@ -1268,6 +1378,9 @@ gst_video_format_get_row_stride (GstVideoFormat format, int component, case GST_VIDEO_FORMAT_IYU1: return GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) + GST_ROUND_UP_4 (width) / 2); + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + return width * 8; default: return 0; } @@ -1324,6 +1437,7 @@ gst_video_format_get_pixel_stride (GstVideoFormat format, int component) case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_r210: return 4; case GST_VIDEO_FORMAT_RGB16: case GST_VIDEO_FORMAT_BGR16: @@ -1362,6 +1476,9 @@ gst_video_format_get_pixel_stride (GstVideoFormat format, int component) return 0; case GST_VIDEO_FORMAT_RGB8_PALETTED: return 1; + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + return 8; default: return 0; } @@ -1438,6 +1555,9 @@ gst_video_format_get_component_width (GstVideoFormat format, case GST_VIDEO_FORMAT_Y800: case GST_VIDEO_FORMAT_Y16: case GST_VIDEO_FORMAT_RGB8_PALETTED: + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + case GST_VIDEO_FORMAT_r210: return width; case GST_VIDEO_FORMAT_A420: if (component == 0 || component == 3) { @@ -1514,6 +1634,9 @@ gst_video_format_get_component_height (GstVideoFormat format, case GST_VIDEO_FORMAT_UYVP: case GST_VIDEO_FORMAT_RGB8_PALETTED: case GST_VIDEO_FORMAT_IYU1: + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + case GST_VIDEO_FORMAT_r210: return height; case GST_VIDEO_FORMAT_A420: if (component == 0 || component == 3) { @@ -1697,6 +1820,7 @@ gst_video_format_get_component_offset (GstVideoFormat format, case GST_VIDEO_FORMAT_Y444: return GST_ROUND_UP_4 (width) * height * component; case GST_VIDEO_FORMAT_v210: + case GST_VIDEO_FORMAT_r210: /* v210 is bit-packed, so this doesn't make sense */ return 0; case GST_VIDEO_FORMAT_v216: @@ -1776,6 +1900,17 @@ gst_video_format_get_component_offset (GstVideoFormat format, return 0; if (component == 2) return 4; + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + if (component == 0) + return 2; + if (component == 1) + return 4; + if (component == 2) + return 6; + if (component == 3) + return 0; + return 0; default: return 0; } @@ -1825,6 +1960,7 @@ gst_video_format_get_size (GstVideoFormat format, int width, int height) case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_r210: return width * 4 * height; case GST_VIDEO_FORMAT_RGB16: case GST_VIDEO_FORMAT_BGR16: @@ -1871,6 +2007,9 @@ gst_video_format_get_size (GstVideoFormat format, int width, int height) size += GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) / 4) * (GST_ROUND_UP_4 (height) / 4) * 2; return size; + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + return width * 8 * height; default: return 0; } diff --git a/gst-libs/gst/video/video.h b/gst-libs/gst/video/video.h index 96c5a85d9d..969cb05072 100644 --- a/gst-libs/gst/video/video.h +++ b/gst-libs/gst/video/video.h @@ -69,6 +69,9 @@ G_BEGIN_DECLS * @GST_VIDEO_FORMAT_YUV9: planar 4:1:0 YUV (Since: 0.10.32) * @GST_VIDEO_FORMAT_YVU9: planar 4:1:0 YUV (like YUV9 but UV planes swapped) (Since: 0.10.32) * @GST_VIDEO_FORMAT_IYU1: packed 4:1:1 YUV (Cb-Y0-Y1-Cr-Y2-Y3 ...) (Since: 0.10.32) + * @GST_VIDEO_FORMAT_ARGB64: rgb with alpha channel first, 16 bits per channel (Since: 0.10.33) + * @GST_VIDEO_FORMAT_AY64: packed 4:4:4 YUV with alpha channel, 16 bits per channel (A0-Y0-U0-V0 ...) (Since: 0.10.33) + * @GST_VIDEO_FORMAT_r210: packed 4:4:4 RGB, 10 bits per channel (Since: 0.10.33) * * Enum value describing the most common video formats. */ @@ -112,7 +115,10 @@ typedef enum { GST_VIDEO_FORMAT_RGB8_PALETTED, GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YVU9, - GST_VIDEO_FORMAT_IYU1 + GST_VIDEO_FORMAT_IYU1, + GST_VIDEO_FORMAT_ARGB64, + GST_VIDEO_FORMAT_AYUV64, + GST_VIDEO_FORMAT_r210 } GstVideoFormat; #define GST_VIDEO_BYTE1_MASK_32 "0xFF000000" @@ -232,6 +238,19 @@ typedef enum { "height = " GST_VIDEO_SIZE_RANGE ", " \ "framerate = " GST_VIDEO_FPS_RANGE +#define __GST_VIDEO_CAPS_MAKE_64A(R, G, B, A) \ + "video/x-raw-rgb, " \ + "bpp = (int) 64, " \ + "depth = (int) 64, " \ + "endianness = (int) BIG_ENDIAN, " \ + "red_mask = (int) " GST_VIDEO_BYTE ## R ## _MASK_32 ", " \ + "green_mask = (int) " GST_VIDEO_BYTE ## G ## _MASK_32 ", " \ + "blue_mask = (int) " GST_VIDEO_BYTE ## B ## _MASK_32 ", " \ + "alpha_mask = (int) " GST_VIDEO_BYTE ## A ## _MASK_32 ", " \ + "width = " GST_VIDEO_SIZE_RANGE ", " \ + "height = " GST_VIDEO_SIZE_RANGE ", " \ + "framerate = " GST_VIDEO_FPS_RANGE + /* 24 bit */ @@ -296,6 +315,24 @@ typedef enum { #define GST_VIDEO_CAPS_BGR_15 \ __GST_VIDEO_CAPS_MAKE_15 (3, 2, 1) +/* 30 bit */ +#define GST_VIDEO_CAPS_r210 \ + "video/x-raw-rgb, " \ + "bpp = (int) 32, " \ + "depth = (int) 30, " \ + "endianness = (int) BIG_ENDIAN, " \ + "red_mask = (int) 0x3ff00000, " \ + "green_mask = (int) 0x000ffc00, " \ + "blue_mask = (int) 0x000003ff, " \ + "width = " GST_VIDEO_SIZE_RANGE ", " \ + "height = " GST_VIDEO_SIZE_RANGE ", " \ + "framerate = " GST_VIDEO_FPS_RANGE + +/* 64 bit alpha */ + +#define GST_VIDEO_CAPS_ARGB_64 \ + __GST_VIDEO_CAPS_MAKE_64A (2, 3, 4, 1) + /** * GST_VIDEO_CAPS_RGB8_PALETTED: * @@ -388,6 +425,16 @@ typedef enum { */ #define GST_VIDEO_BUFFER_ONEFIELD GST_BUFFER_FLAG_MEDIA3 +/** + * GST_VIDEO_BUFFER_PROGRESSIVE: + * + * If the #GstBuffer is telecined, then the buffer is progressive if the + * %GST_VIDEO_BUFFER_PROGRESSIVE flag is set, else it is telecine mixed. + * + * Since: 0.10.33 + */ +#define GST_VIDEO_BUFFER_PROGRESSIVE GST_BUFFER_FLAG_MEDIA4 + /* functions */ const GValue *gst_video_frame_rate (GstPad *pad); gboolean gst_video_get_size (GstPad *pad, @@ -421,6 +468,7 @@ gboolean gst_video_format_is_rgb (GstVideoFormat format); gboolean gst_video_format_is_yuv (GstVideoFormat format); gboolean gst_video_format_is_gray (GstVideoFormat format); gboolean gst_video_format_has_alpha (GstVideoFormat format); +int gst_video_format_get_component_depth (GstVideoFormat format, int component); int gst_video_format_get_row_stride (GstVideoFormat format, int component, int width); int gst_video_format_get_pixel_stride (GstVideoFormat format, int component); diff --git a/gst-plugins-base.doap b/gst-plugins-base.doap index 3ea062fe95..f0dcb1470e 100644 --- a/gst-plugins-base.doap +++ b/gst-plugins-base.doap @@ -22,7 +22,7 @@ A wide range of video and audio decoders, encoders, and filters are included. - + C @@ -34,6 +34,17 @@ A wide range of video and audio decoders, encoders, and filters are included. + + + 0.10.32 + 0.10 + Your Life You Like It Well + 2011-01-21 + + + + + 0.10.31 diff --git a/gst-plugins-base.spec.in b/gst-plugins-base.spec.in index 0e580348d2..b2849274a9 100644 --- a/gst-plugins-base.spec.in +++ b/gst-plugins-base.spec.in @@ -20,8 +20,8 @@ BuildRequires: %{gstreamer}-devel >= %{gst_minver} BuildRequires: gcc-c++ BuildRequires: gtk-doc >= 1.3 -BuildRequires: liboil-devel >= 0.3.14 -Requires: liboil => 0.3.14 +BuildRequires: orc-devel +Requires: orc @USE_GNOME_VFS_TRUE@Requires: gnome-vfs2 > 1.9.4.00 @USE_GNOME_VFS_TRUE@BuildRequires: gnome-vfs2-devel > 1.9.4.00 @@ -81,6 +81,7 @@ rm -rf $RPM_BUILD_ROOT # helper programs %{_bindir}/gst-visualise-%{majorminor} +%{_bindir}/gst-discoverer-%{majorminor} %{_mandir}/man1/gst-visualise-%{majorminor}* # libraries @@ -118,6 +119,7 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/gstreamer-%{majorminor}/libgstaudiotestsrc.so %{_libdir}/gstreamer-%{majorminor}/libgstgdp.so %{_libdir}/gstreamer-%{majorminor}/libgstapp.so +%{_libdir}/gstreamer-%{majorminor}/libgstencodebin.so # Here are packages not in the base plugins package but not dependant # on an external lib @@ -215,6 +217,11 @@ GStreamer Plugins Base library development and header files. %{_includedir}/gstreamer-%{majorminor}/gst/audio/audio-enumtypes.h %{_includedir}/gstreamer-%{majorminor}/gst/video/video-enumtypes.h %{_includedir}/gstreamer-%{majorminor}/gst/interfaces/streamvolume.h +%{_includedir}/gstreamer-%{majorminor}/gst/pbutils/codec-utils.h +%{_includedir}/gstreamer-%{majorminor}/gst/pbutils/encoding-profile.h +%{_includedir}/gstreamer-%{majorminor}/gst/pbutils/encoding-target.h +%{_includedir}/gstreamer-%{majorminor}/gst/pbutils/gstdiscoverer.h +%{_includedir}/gstreamer-%{majorminor}/gst/pbutils/gstpluginsbaseversion.h %{_libdir}/libgstfft-%{majorminor}.so %{_libdir}/libgstrtsp-%{majorminor}.so diff --git a/gst/adder/gstadder.c b/gst/adder/gstadder.c index ab2abd7022..fca3eb2325 100644 --- a/gst/adder/gstadder.c +++ b/gst/adder/gstadder.c @@ -1208,10 +1208,12 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data) if (adder->segment_rate > 0.0) { GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp; GST_BUFFER_OFFSET (outbuf) = adder->offset; + GST_BUFFER_OFFSET_END (outbuf) = next_offset; GST_BUFFER_DURATION (outbuf) = next_timestamp - adder->timestamp; } else { GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp; GST_BUFFER_OFFSET (outbuf) = next_offset; + GST_BUFFER_OFFSET_END (outbuf) = adder->offset; GST_BUFFER_DURATION (outbuf) = adder->timestamp - next_timestamp; } diff --git a/gst/audiorate/gstaudiorate.c b/gst/audiorate/gstaudiorate.c index 057115d823..40d3467a94 100644 --- a/gst/audiorate/gstaudiorate.c +++ b/gst/audiorate/gstaudiorate.c @@ -78,6 +78,7 @@ enum #define DEFAULT_SILENT TRUE #define DEFAULT_TOLERANCE 0 +#define DEFAULT_SKIP_TO_FIRST FALSE enum { @@ -88,7 +89,7 @@ enum ARG_DROP, ARG_SILENT, ARG_TOLERANCE, - /* FILL ME */ + ARG_SKIP_TO_FIRST }; static GstStaticPadTemplate gst_audio_rate_src_template = @@ -212,6 +213,18 @@ gst_audio_rate_class_init (GstAudioRateClass * klass) 0, G_MAXUINT64, DEFAULT_TOLERANCE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstAudioRate:skip-to-first: + * + * Don't produce buffers before the first one we receive. + * + * Since: 0.10.33 + **/ + g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST, + g_param_spec_boolean ("skip-to-first", "Skip to first buffer", + "Don't produce buffers before the first one we receive", + DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + element_class->change_state = gst_audio_rate_change_state; } @@ -556,6 +569,16 @@ gst_audio_rate_chain (GstPad * pad, GstBuffer * buf) audiorate->next_offset = pos; audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset, GST_SECOND, audiorate->rate); + + if (audiorate->skip_to_first && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + GST_DEBUG_OBJECT (audiorate, "but skipping to first buffer instead"); + pos = gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf), + audiorate->rate, GST_SECOND); + GST_DEBUG_OBJECT (audiorate, "so resync to offset %" G_GINT64_FORMAT, + pos); + audiorate->next_offset = pos; + audiorate->next_ts = GST_BUFFER_TIMESTAMP (buf); + } } audiorate->in++; @@ -768,6 +791,9 @@ gst_audio_rate_set_property (GObject * object, case ARG_TOLERANCE: audiorate->tolerance = g_value_get_uint64 (value); break; + case ARG_SKIP_TO_FIRST: + audiorate->skip_to_first = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -799,6 +825,9 @@ gst_audio_rate_get_property (GObject * object, case ARG_TOLERANCE: g_value_set_uint64 (value, audiorate->tolerance); break; + case ARG_SKIP_TO_FIRST: + g_value_set_boolean (value, audiorate->skip_to_first); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/gst/audiorate/gstaudiorate.h b/gst/audiorate/gstaudiorate.h index bcc087bda3..e55bbcfc79 100644 --- a/gst/audiorate/gstaudiorate.h +++ b/gst/audiorate/gstaudiorate.h @@ -58,6 +58,7 @@ struct _GstAudioRate guint64 in, out, add, drop; gboolean silent; guint64 tolerance; + gboolean skip_to_first; /* audio state */ guint64 next_offset; diff --git a/gst/audioresample/gstaudioresample.c b/gst/audioresample/gstaudioresample.c index f61dac2c8f..90967c2671 100644 --- a/gst/audioresample/gstaudioresample.c +++ b/gst/audioresample/gstaudioresample.c @@ -224,6 +224,7 @@ gst_audio_resample_init (GstAudioResample * resample, resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; + gst_base_transform_set_gap_aware (trans, TRUE); gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query); gst_pad_set_query_type_function (trans->srcpad, gst_audio_resample_query_type); @@ -237,11 +238,13 @@ gst_audio_resample_start (GstBaseTransform * base) resample->need_discont = TRUE; + resample->num_gap_samples = 0; + resample->num_nongap_samples = 0; resample->t0 = GST_CLOCK_TIME_NONE; resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; - resample->next_in_offset = GST_BUFFER_OFFSET_NONE; - resample->next_out_offset = GST_BUFFER_OFFSET_NONE; + resample->samples_in = 0; + resample->samples_out = 0; resample->tmp_in = NULL; resample->tmp_in_size = 0; @@ -781,18 +784,48 @@ gst_audio_resample_workspace_realloc (guint8 ** workspace, guint * size, return *workspace; } +/* Push history_len zeros into the filter, but discard the output. */ static void -gst_audio_resample_push_drain (GstAudioResample * resample) +gst_audio_resample_dump_drain (GstAudioResample * resample, guint history_len) +{ + gint outsize; + guint in_len, in_processed; + guint out_len, out_processed; + guint num, den; + gpointer buf; + + g_assert (resample->state != NULL); + + resample->funcs->get_ratio (resample->state, &num, &den); + + in_len = in_processed = history_len; + out_processed = out_len = + gst_util_uint64_scale_int_ceil (history_len, den, num); + outsize = out_len * resample->channels * (resample->funcs->width / 8); + + if (out_len == 0) + return; + + buf = g_malloc (outsize); + resample->funcs->process (resample->state, NULL, &in_processed, buf, + &out_processed); + g_free (buf); + + g_assert (in_len == in_processed); +} + +static void +gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len) { GstBuffer *outbuf; GstFlowReturn res; gint outsize; - guint history_len, out_len, out_processed; + guint in_len, in_processed; + guint out_len, out_processed; gint err; guint num, den; - if (!resample->state) - return; + g_assert (resample->state != NULL); /* Don't drain samples if we were reset. */ if (!GST_CLOCK_TIME_IS_VALID (resample->t0)) @@ -800,11 +833,14 @@ gst_audio_resample_push_drain (GstAudioResample * resample) resample->funcs->get_ratio (resample->state, &num, &den); - history_len = resample->funcs->get_input_latency (resample->state); + in_len = in_processed = history_len; out_len = out_processed = gst_util_uint64_scale_int_ceil (history_len, den, num); outsize = out_len * resample->channels * (resample->width / 8); + if (out_len == 0) + return; + res = gst_pad_alloc_buffer_and_set_caps (GST_BASE_TRANSFORM_SRC_PAD (resample), GST_BUFFER_OFFSET_NONE, outsize, @@ -825,7 +861,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample) } /* process */ - err = resample->funcs->process (resample->state, NULL, &history_len, + err = resample->funcs->process (resample->state, NULL, &in_processed, resample->tmp_out, &out_processed); /* convert output format */ @@ -833,7 +869,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample) GST_BUFFER_DATA (outbuf), out_processed, TRUE); } else { /* don't need to convert data format; process */ - err = resample->funcs->process (resample->state, NULL, &history_len, + err = resample->funcs->process (resample->state, NULL, &in_processed, GST_BUFFER_DATA (outbuf), &out_processed); } @@ -848,29 +884,34 @@ gst_audio_resample_push_drain (GstAudioResample * resample) return; } - if (G_UNLIKELY (out_processed == 0)) { - GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer"); - gst_buffer_unref (outbuf); - return; - } - + /* time */ if (GST_CLOCK_TIME_IS_VALID (resample->t0)) { - GST_BUFFER_OFFSET (outbuf) = resample->next_out_offset; - GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed; GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 + - gst_util_uint64_scale_int_round (GST_BUFFER_OFFSET (outbuf) - - resample->out_offset0, GST_SECOND, resample->outrate); + gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND, + resample->outrate); GST_BUFFER_DURATION (outbuf) = resample->t0 + - gst_util_uint64_scale_int_round (GST_BUFFER_OFFSET_END (outbuf) - - resample->out_offset0, GST_SECOND, resample->outrate) - - GST_BUFFER_TIMESTAMP (outbuf); - resample->next_out_offset += out_processed; - resample->next_in_offset += 0; + gst_util_uint64_scale_int_round (resample->samples_out + out_processed, + GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf); + } else { + GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; + } + /* offset */ + if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) { + GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out; + GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed; } else { GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; - GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; - GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; + } + /* move along */ + resample->samples_out += out_processed; + resample->samples_in += history_len; + + if (G_UNLIKELY (out_processed == 0 && in_len * den > num)) { + GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer"); + gst_buffer_unref (outbuf); + return; } GST_BUFFER_SIZE (outbuf) = @@ -901,25 +942,39 @@ gst_audio_resample_event (GstBaseTransform * base, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: gst_audio_resample_reset_state (resample); + if (resample->state) + resample->funcs->skip_zeros (resample->state); + resample->num_gap_samples = 0; + resample->num_nongap_samples = 0; resample->t0 = GST_CLOCK_TIME_NONE; resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; - resample->next_in_offset = GST_BUFFER_OFFSET_NONE; - resample->next_out_offset = GST_BUFFER_OFFSET_NONE; + resample->samples_in = 0; + resample->samples_out = 0; resample->need_discont = TRUE; break; case GST_EVENT_NEWSEGMENT: - gst_audio_resample_push_drain (resample); + if (resample->state) { + guint latency = resample->funcs->get_input_latency (resample->state); + gst_audio_resample_push_drain (resample, latency); + } gst_audio_resample_reset_state (resample); + if (resample->state) + resample->funcs->skip_zeros (resample->state); + resample->num_gap_samples = 0; + resample->num_nongap_samples = 0; resample->t0 = GST_CLOCK_TIME_NONE; resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; - resample->next_in_offset = GST_BUFFER_OFFSET_NONE; - resample->next_out_offset = GST_BUFFER_OFFSET_NONE; + resample->samples_in = 0; + resample->samples_out = 0; resample->need_discont = TRUE; break; case GST_EVENT_EOS: - gst_audio_resample_push_drain (resample); + if (resample->state) { + guint latency = resample->funcs->get_input_latency (resample->state); + gst_audio_resample_push_drain (resample, latency); + } gst_audio_resample_reset_state (resample); break; default: @@ -941,22 +996,21 @@ gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf) /* no valid timestamps or offsets to compare --> no discontinuity */ if (G_UNLIKELY (!(GST_BUFFER_TIMESTAMP_IS_VALID (buf) && - GST_CLOCK_TIME_IS_VALID (resample->t0) && - resample->in_offset0 != GST_BUFFER_OFFSET_NONE && - resample->next_in_offset != GST_BUFFER_OFFSET_NONE))) + GST_CLOCK_TIME_IS_VALID (resample->t0)))) return FALSE; /* convert the inbound timestamp to an offset. */ offset = - resample->in_offset0 + gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) - resample->t0, resample->inrate, GST_SECOND); /* many elements generate imperfect streams due to rounding errors, so we * permit a small error (up to one sample) without triggering a filter * flush/restart (if triggered incorrectly, this will be audible) */ - delta = ABS ((gint64) (offset - resample->next_in_offset)); - if (delta <= 1) + /* allow even up to more samples, since sink is not so strict anyway, + * so give that one a chance to handle this as configured */ + delta = ABS ((gint64) (offset - resample->samples_in)); + if (delta <= (resample->inrate >> 5)) return FALSE; GST_WARNING_OBJECT (resample, @@ -973,7 +1027,7 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, { guint32 in_len, in_processed; guint32 out_len, out_processed; - gint err; + guint filt_len = resample->funcs->get_filt_len (resample->state); in_len = GST_BUFFER_SIZE (inbuf) / resample->channels; out_len = GST_BUFFER_SIZE (outbuf) / resample->channels; @@ -984,70 +1038,123 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, in_processed = in_len; out_processed = out_len; - if (resample->funcs->width != resample->width) { - /* need to convert data format for processing; ensure we have enough - * workspace available */ - if (!gst_audio_resample_workspace_realloc (&resample->tmp_in, - &resample->tmp_in_size, in_len * resample->channels * - (resample->funcs->width / 8)) || - !gst_audio_resample_workspace_realloc (&resample->tmp_out, - &resample->tmp_out_size, out_len * resample->channels * - (resample->funcs->width / 8))) { - GST_ERROR_OBJECT (resample, "failed to allocate workspace"); - return GST_FLOW_ERROR; + if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + resample->num_nongap_samples = 0; + if (resample->num_gap_samples < filt_len) { + guint zeros_to_push; + if (in_len >= filt_len - resample->num_gap_samples) + zeros_to_push = filt_len - resample->num_gap_samples; + else + zeros_to_push = in_len; + + gst_audio_resample_push_drain (resample, zeros_to_push); + in_len -= zeros_to_push; + resample->num_gap_samples += zeros_to_push; } - /* convert input */ - gst_audio_resample_convert_buffer (resample, GST_BUFFER_DATA (inbuf), - resample->tmp_in, in_len, FALSE); + { + guint num, den; + resample->funcs->get_ratio (resample->state, &num, &den); + if (resample->samples_in + in_len >= filt_len / 2) + out_processed = + gst_util_uint64_scale_int_ceil (resample->samples_in + in_len - + filt_len / 2, den, num) - resample->samples_out; + else + out_processed = 0; - /* process */ - err = resample->funcs->process (resample->state, - resample->tmp_in, &in_processed, resample->tmp_out, &out_processed); + memset (GST_BUFFER_DATA (outbuf), 0, GST_BUFFER_SIZE (outbuf)); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP); + resample->num_gap_samples += in_len; + in_processed = in_len; + } + } else { /* not a gap */ - /* convert output */ - gst_audio_resample_convert_buffer (resample, resample->tmp_out, - GST_BUFFER_DATA (outbuf), out_processed, TRUE); - } else { - /* no format conversion required; process */ - err = resample->funcs->process (resample->state, - GST_BUFFER_DATA (inbuf), &in_processed, - GST_BUFFER_DATA (outbuf), &out_processed); + gint err; + + if (resample->num_gap_samples > filt_len) { + /* push in enough zeros to restore the filter to the right offset */ + guint num, den; + resample->funcs->get_ratio (resample->state, &num, &den); + gst_audio_resample_dump_drain (resample, + (resample->num_gap_samples - filt_len) % num); + } + resample->num_gap_samples = 0; + if (resample->num_nongap_samples < filt_len) { + resample->num_nongap_samples += in_len; + if (resample->num_nongap_samples > filt_len) + resample->num_nongap_samples = filt_len; + } + + if (resample->funcs->width != resample->width) { + /* need to convert data format for processing; ensure we have enough + * workspace available */ + if (!gst_audio_resample_workspace_realloc (&resample->tmp_in, + &resample->tmp_in_size, in_len * resample->channels * + (resample->funcs->width / 8)) || + !gst_audio_resample_workspace_realloc (&resample->tmp_out, + &resample->tmp_out_size, out_len * resample->channels * + (resample->funcs->width / 8))) { + GST_ERROR_OBJECT (resample, "failed to allocate workspace"); + return GST_FLOW_ERROR; + } + + /* convert input */ + gst_audio_resample_convert_buffer (resample, GST_BUFFER_DATA (inbuf), + resample->tmp_in, in_len, FALSE); + + /* process */ + err = resample->funcs->process (resample->state, + resample->tmp_in, &in_processed, resample->tmp_out, &out_processed); + + /* convert output */ + gst_audio_resample_convert_buffer (resample, resample->tmp_out, + GST_BUFFER_DATA (outbuf), out_processed, TRUE); + } else { + /* no format conversion required; process */ + err = resample->funcs->process (resample->state, + GST_BUFFER_DATA (inbuf), &in_processed, + GST_BUFFER_DATA (outbuf), &out_processed); + } + + if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { + GST_ERROR_OBJECT (resample, "Failed to convert data: %s", + resample->funcs->strerror (err)); + return GST_FLOW_ERROR; + } } /* If we wrote more than allocated something is really wrong now and we * should better abort immediately */ g_assert (out_len >= out_processed); - if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { - GST_ERROR_OBJECT (resample, "Failed to convert data: %s", - resample->funcs->strerror (err)); - return GST_FLOW_ERROR; - } - if (G_UNLIKELY (in_len != in_processed)) { GST_WARNING_OBJECT (resample, "converted %d of %d input samples", in_processed, in_len); } + /* time */ if (GST_CLOCK_TIME_IS_VALID (resample->t0)) { - GST_BUFFER_OFFSET (outbuf) = resample->next_out_offset; - GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed; GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 + - gst_util_uint64_scale_int_round (GST_BUFFER_OFFSET (outbuf) - - resample->out_offset0, GST_SECOND, resample->outrate); + gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND, + resample->outrate); GST_BUFFER_DURATION (outbuf) = resample->t0 + - gst_util_uint64_scale_int_round (GST_BUFFER_OFFSET_END (outbuf) - - resample->out_offset0, GST_SECOND, resample->outrate) - - GST_BUFFER_TIMESTAMP (outbuf); - resample->next_out_offset += out_processed; - resample->next_in_offset += in_len; + gst_util_uint64_scale_int_round (resample->samples_out + out_processed, + GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf); } else { - GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; - GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; } + /* offset */ + if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) { + GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out; + GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed; + } else { + GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; + GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; + } + /* move along */ + resample->samples_out += out_processed; + resample->samples_in += in_len; GST_BUFFER_SIZE (outbuf) = out_processed * resample->channels * (resample->width / 8); @@ -1106,24 +1213,29 @@ gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf, /* handle discontinuity */ if (G_UNLIKELY (resample->need_discont)) { + resample->funcs->skip_zeros (resample->state); + resample->num_gap_samples = 0; + resample->num_nongap_samples = 0; + /* reset */ + resample->samples_in = 0; + resample->samples_out = 0; + GST_DEBUG_OBJECT (resample, "found discontinuity; resyncing"); /* resync the timestamp and offset counters if possible */ - if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf) && - GST_BUFFER_OFFSET_IS_VALID (inbuf)) { + if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf)) { resample->t0 = GST_BUFFER_TIMESTAMP (inbuf); + } else { + GST_DEBUG_OBJECT (resample, "... but new timestamp is invalid"); + resample->t0 = GST_CLOCK_TIME_NONE; + } + if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) { resample->in_offset0 = GST_BUFFER_OFFSET (inbuf); resample->out_offset0 = gst_util_uint64_scale_int_round (resample->in_offset0, resample->outrate, resample->inrate); - resample->next_in_offset = resample->in_offset0; - resample->next_out_offset = resample->out_offset0; } else { - GST_DEBUG_OBJECT (resample, "found discontinuity but timestamp and/or " - "offset is invalid, cannot sync output timestamp and offset counter"); - resample->t0 = GST_CLOCK_TIME_NONE; + GST_DEBUG_OBJECT (resample, "... but new offset is invalid"); resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; - resample->next_in_offset = GST_BUFFER_OFFSET_NONE; - resample->next_out_offset = GST_BUFFER_OFFSET_NONE; } /* set DISCONT flag on output buffer */ GST_DEBUG_OBJECT (resample, "marking this buffer with the DISCONT flag"); diff --git a/gst/audioresample/gstaudioresample.h b/gst/audioresample/gstaudioresample.h index 2f41b2cecb..f5f746482d 100644 --- a/gst/audioresample/gstaudioresample.h +++ b/gst/audioresample/gstaudioresample.h @@ -61,9 +61,12 @@ struct _GstAudioResample { GstClockTime t0; guint64 in_offset0; guint64 out_offset0; - guint64 next_in_offset; - guint64 next_out_offset; + guint64 samples_in; + guint64 samples_out; + guint64 num_gap_samples; + guint64 num_nongap_samples; + gint channels; gint inrate; gint outrate; diff --git a/gst/audioresample/resample.c b/gst/audioresample/resample.c index 2cfd5442e3..7d42f0e0fd 100644 --- a/gst/audioresample/resample.c +++ b/gst/audioresample/resample.c @@ -1310,6 +1310,12 @@ speex_resampler_get_output_latency (SpeexResamplerState * st) (st->num_rate >> 1)) / st->num_rate; } +EXPORT int +speex_resampler_get_filt_len (SpeexResamplerState * st) +{ + return st->filt_len; +} + EXPORT int speex_resampler_skip_zeros (SpeexResamplerState * st) { diff --git a/gst/audioresample/speex_resampler.h b/gst/audioresample/speex_resampler.h index 9dc02806a6..894b380f80 100644 --- a/gst/audioresample/speex_resampler.h +++ b/gst/audioresample/speex_resampler.h @@ -73,6 +73,7 @@ #define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride) #define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency) #define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency) +#define speex_resampler_get_filt_len CAT_PREFIX(RANDOM_PREFIX,_resampler_get_filt_len) #define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) #define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) #define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) @@ -333,6 +334,11 @@ int speex_resampler_get_input_latency(SpeexResamplerState *st); */ int speex_resampler_get_output_latency(SpeexResamplerState *st); +/** Get the length of the filter in input samples. + * @param st Resampler state + */ +int speex_resampler_get_filt_len(SpeexResamplerState *st); + /** Make sure that the first samples to go out of the resamplers don't have * leading zeros. This is only useful before starting to use a newly created * resampler. It is recommended to use that when resampling an audio file, as diff --git a/gst/audioresample/speex_resampler_wrapper.h b/gst/audioresample/speex_resampler_wrapper.h index 36d444f879..08a82bc105 100644 --- a/gst/audioresample/speex_resampler_wrapper.h +++ b/gst/audioresample/speex_resampler_wrapper.h @@ -52,6 +52,7 @@ typedef struct { void (*get_ratio) (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int (*get_input_latency) (SpeexResamplerState * st); + int (*get_filt_len) (SpeexResamplerState * st); int (*set_quality) (SpeexResamplerState * st, gint quality); int (*reset_mem) (SpeexResamplerState * st); int (*skip_zeros) (SpeexResamplerState * st); @@ -71,6 +72,7 @@ void resample_float_resampler_get_rate (SpeexResamplerState * st, void resample_float_resampler_get_ratio (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int resample_float_resampler_get_input_latency (SpeexResamplerState * st); +int resample_float_resampler_get_filt_len (SpeexResamplerState * st); int resample_float_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_float_resampler_reset_mem (SpeexResamplerState * st); int resample_float_resampler_skip_zeros (SpeexResamplerState * st); @@ -85,6 +87,7 @@ static const SpeexResampleFuncs float_funcs = resample_float_resampler_get_rate, resample_float_resampler_get_ratio, resample_float_resampler_get_input_latency, + resample_float_resampler_get_filt_len, resample_float_resampler_set_quality, resample_float_resampler_reset_mem, resample_float_resampler_skip_zeros, @@ -104,6 +107,7 @@ void resample_double_resampler_get_rate (SpeexResamplerState * st, void resample_double_resampler_get_ratio (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int resample_double_resampler_get_input_latency (SpeexResamplerState * st); +int resample_double_resampler_get_filt_len (SpeexResamplerState * st); int resample_double_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_double_resampler_reset_mem (SpeexResamplerState * st); int resample_double_resampler_skip_zeros (SpeexResamplerState * st); @@ -118,6 +122,7 @@ static const SpeexResampleFuncs double_funcs = resample_double_resampler_get_rate, resample_double_resampler_get_ratio, resample_double_resampler_get_input_latency, + resample_double_resampler_get_filt_len, resample_double_resampler_set_quality, resample_double_resampler_reset_mem, resample_double_resampler_skip_zeros, @@ -137,6 +142,7 @@ void resample_int_resampler_get_rate (SpeexResamplerState * st, void resample_int_resampler_get_ratio (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int resample_int_resampler_get_input_latency (SpeexResamplerState * st); +int resample_int_resampler_get_filt_len (SpeexResamplerState * st); int resample_int_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_int_resampler_reset_mem (SpeexResamplerState * st); int resample_int_resampler_skip_zeros (SpeexResamplerState * st); @@ -151,6 +157,7 @@ static const SpeexResampleFuncs int_funcs = resample_int_resampler_get_rate, resample_int_resampler_get_ratio, resample_int_resampler_get_input_latency, + resample_int_resampler_get_filt_len, resample_int_resampler_set_quality, resample_int_resampler_reset_mem, resample_int_resampler_skip_zeros, diff --git a/gst/audiotestsrc/gstaudiotestsrc.c b/gst/audiotestsrc/gstaudiotestsrc.c index 5b689713fb..ecbb2fbb07 100644 --- a/gst/audiotestsrc/gstaudiotestsrc.c +++ b/gst/audiotestsrc/gstaudiotestsrc.c @@ -140,6 +140,8 @@ gst_audiostestsrc_wave_get_type (void) return audiostestsrc_wave_type; } +static void gst_audio_test_src_finalize (GObject * object); + static void gst_audio_test_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_audio_test_src_get_property (GObject * object, @@ -190,6 +192,7 @@ gst_audio_test_src_class_init (GstAudioTestSrcClass * klass) gobject_class->set_property = gst_audio_test_src_set_property; gobject_class->get_property = gst_audio_test_src_get_property; + gobject_class->finalize = gst_audio_test_src_finalize; g_object_class_install_property (gobject_class, PROP_SAMPLES_PER_BUFFER, g_param_spec_int ("samplesperbuffer", "Samples per buffer", @@ -263,10 +266,24 @@ gst_audio_test_src_init (GstAudioTestSrc * src, GstAudioTestSrcClass * g_class) src->timestamp_offset = DEFAULT_TIMESTAMP_OFFSET; src->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL; + src->gen = NULL; + src->wave = DEFAULT_WAVE; gst_base_src_set_blocksize (GST_BASE_SRC (src), -1); } +static void +gst_audio_test_src_finalize (GObject * object) +{ + GstAudioTestSrc *src = GST_AUDIO_TEST_SRC (object); + + if (src->gen) + g_rand_free (src->gen); + src->gen = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + static void gst_audio_test_src_src_fixate (GstPad * pad, GstCaps * caps) { @@ -584,7 +601,7 @@ gst_audio_test_src_create_white_noise_##type (GstAudioTestSrc * src, g##type * s i = 0; \ while (i < (src->generate_samples_per_buffer * src->channels)) { \ for (c = 0; c < src->channels; ++c) \ - samples[i++] = (g##type) (amp * g_random_double_range (-1.0, 1.0)); \ + samples[i++] = (g##type) (amp * g_rand_double_range (src->gen, -1.0, 1.0)); \ } \ } @@ -626,8 +643,9 @@ gst_audio_test_src_init_pink_noise (GstAudioTestSrc * src) /* Generate Pink noise values between -1.0 and +1.0 */ static gdouble -gst_audio_test_src_generate_pink_noise_value (GstPinkNoise * pink) +gst_audio_test_src_generate_pink_noise_value (GstAudioTestSrc * src) { + GstPinkNoise *pink = &src->pink; glong new_random; glong sum; @@ -651,13 +669,15 @@ gst_audio_test_src_generate_pink_noise_value (GstPinkNoise * pink) * values together. Only one changes each time. */ pink->running_sum -= pink->rows[num_zeros]; - new_random = 32768.0 - (65536.0 * (gulong) rand () / (RAND_MAX + 1.0)); + new_random = 32768.0 - (65536.0 * (gulong) g_rand_int (src->gen) + / (G_MAXUINT32 + 1.0)); pink->running_sum += new_random; pink->rows[num_zeros] = new_random; } /* Add extra white noise value. */ - new_random = 32768.0 - (65536.0 * (gulong) rand () / (RAND_MAX + 1.0)); + new_random = 32768.0 - (65536.0 * (gulong) g_rand_int (src->gen) + / (G_MAXUINT32 + 1.0)); sum = pink->running_sum + new_random; /* Scale to range of -1.0 to 0.9999. */ @@ -677,7 +697,7 @@ gst_audio_test_src_create_pink_noise_##type (GstAudioTestSrc * src, g##type * sa while (i < (src->generate_samples_per_buffer * src->channels)) { \ for (c = 0; c < src->channels; ++c) { \ samples[i++] = \ - (g##type) (gst_audio_test_src_generate_pink_noise_value (&src->pink) * \ + (g##type) (gst_audio_test_src_generate_pink_noise_value (src) * \ amp); \ } \ } \ @@ -794,8 +814,8 @@ gst_audio_test_src_create_gaussian_white_noise_##type (GstAudioTestSrc * src, g# \ for (i = 0; i < src->generate_samples_per_buffer * src->channels; ) { \ for (c = 0; c < src->channels; ++c) { \ - gdouble mag = sqrt (-2 * log (1.0 - g_random_double ())); \ - gdouble phs = g_random_double_range (0.0, M_PI_M2); \ + gdouble mag = sqrt (-2 * log (1.0 - g_rand_double (src->gen))); \ + gdouble phs = g_rand_double_range (src->gen, 0.0, M_PI_M2); \ \ samples[i++] = (g##type) (amp * mag * cos (phs)); \ if (++c >= src->channels) \ @@ -846,9 +866,13 @@ gst_audio_test_src_change_wave (GstAudioTestSrc * src) src->process = silence_funcs[src->format]; break; case GST_AUDIO_TEST_SRC_WAVE_WHITE_NOISE: + if (!(src->gen)) + src->gen = g_rand_new (); src->process = white_noise_funcs[src->format]; break; case GST_AUDIO_TEST_SRC_WAVE_PINK_NOISE: + if (!(src->gen)) + src->gen = g_rand_new (); gst_audio_test_src_init_pink_noise (src); src->process = pink_noise_funcs[src->format]; break; @@ -861,6 +885,8 @@ gst_audio_test_src_change_wave (GstAudioTestSrc * src) src->process = tick_funcs[src->format]; break; case GST_AUDIO_TEST_SRC_WAVE_GAUSSIAN_WHITE_NOISE: + if (!(src->gen)) + src->gen = g_rand_new (); src->process = gaussian_white_noise_funcs[src->format]; break; default: diff --git a/gst/audiotestsrc/gstaudiotestsrc.h b/gst/audiotestsrc/gstaudiotestsrc.h index 860c5c01b1..8c76594822 100644 --- a/gst/audiotestsrc/gstaudiotestsrc.h +++ b/gst/audiotestsrc/gstaudiotestsrc.h @@ -127,6 +127,7 @@ struct _GstAudioTestSrc { gboolean reverse; /* play backwards */ /* waveform specific context data */ + GRand *gen; /* random number generator */ gdouble accumulator; /* phase angle */ GstPinkNoise pink; gdouble wave_table[1024]; diff --git a/gst/encoding/.gitignore b/gst/encoding/.gitignore new file mode 100644 index 0000000000..ff44545891 --- /dev/null +++ b/gst/encoding/.gitignore @@ -0,0 +1 @@ +gstencode-marshal.[ch] diff --git a/gst/encoding/Makefile.am b/gst/encoding/Makefile.am new file mode 100644 index 0000000000..0e287fad13 --- /dev/null +++ b/gst/encoding/Makefile.am @@ -0,0 +1,41 @@ +# variables used for enum/marshal generation +glib_enum_define = GST_ENCODE +glib_gen_prefix = gst_encode +glib_gen_basename = gstencode + +built_sources = gstencode-marshal.c +built_headers = gstencode-marshal.h + +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +plugin_LTLIBRARIES = libgstencodebin.la + +libgstencodebin_la_SOURCES = \ + gstencodebin.c \ + gstsmartencoder.c \ + gststreamcombiner.c \ + gststreamsplitter.c + +nodist_libgstencodebin_la_SOURCES = $(built_sources) +libgstencodebin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstencodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstencodebin_la_LIBADD = \ + $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \ + $(GST_LIBS) +libgstencodebin_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = \ + gstencodebin.h \ + gststreamcombiner.h \ + gststreamsplitter.h \ + gstsmartencoder.h + + +BUILT_SOURCES = $(built_headers) $(built_sources) + +EXTRA_DIST = gstencode-marshal.list + +CLEANFILES = $(BUILT_SOURCES) + +include $(top_srcdir)/common/gst-glib-gen.mak + diff --git a/gst/encoding/gstencode-marshal.list b/gst/encoding/gstencode-marshal.list new file mode 100644 index 0000000000..00f26ed2bc --- /dev/null +++ b/gst/encoding/gstencode-marshal.list @@ -0,0 +1 @@ +OBJECT:BOXED diff --git a/gst/encoding/gstencodebin.c b/gst/encoding/gstencodebin.c new file mode 100644 index 0000000000..c1a0e8bbc0 --- /dev/null +++ b/gst/encoding/gstencodebin.c @@ -0,0 +1,1783 @@ +/* GStreamer encoding bin + * Copyright (C) 2009 Edward Hervey + * (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 +#include "gstencodebin.h" +#include "gstsmartencoder.h" +#include "gststreamsplitter.h" +#include "gststreamcombiner.h" +#include + +/** + * SECTION:element-encodebin + * + * EncodeBin provides a bin for encoding/muxing various streams according to + * a specified #GstEncodingProfile. + * + * Based on the profile that was set (via the #GstEncodeBin:profile property), + * EncodeBin will internally select and configure the required elements + * (encoders, muxers, but also audio and video converters) so that you can + * provide it raw or pre-encoded streams of data in input and have your + * encoded/muxed/converted stream in output. + * + * + * Features + * + * + * Automatic encoder and muxer selection based on elements available on the + * system. + * + * + * Conversion of raw audio/video streams (scaling, framerate conversion, + * colorspace conversion, samplerate conversion) to conform to the profile + * output format. + * + * + * Variable number of streams. If the presence property for a stream encoding + * profile is 0, you can request any number of sink pads for it via the + * standard request pad gstreamer API or the #GstEncodeBin::request-pad action + * signal. + * + * + * Avoid reencoding (passthrough). If the input stream is already encoded and is + * compatible with what the #GstEncodingProfile expects, then the stream won't + * be re-encoded but just passed through downstream to the muxer or the output. + * + * + * Mix pre-encoded and raw streams as input. In addition to the passthrough + * feature above, you can feed both raw audio/video *AND* already-encoded data + * to a pad. #GstEncodeBin will take care of passing through the compatible + * segments and re-encoding the segments of media that need encoding. + * + * + * Standard behaviour is to use a #GstEncodingContainerProfile to have both + * encoding and muxing performed. But you can also provide a single stream + * profile (like #GstEncodingAudioProfile) to only have the encoding done and + * handle the encoded output yourself. + * + * + * Audio imperfection corrections. Incoming audio streams can have non perfect + * timestamps (jitter), like the streams coming from ASF files. #GstEncodeBin + * will automatically fix those imperfections for you. See + * #GstEncodeBin:audio-jitter-tolerance for more details. + * + * + * Variable or Constant video framerate. If your #GstEncodingVideoProfile has + * the variableframerate property deactivated (default), then the incoming + * raw video stream will be retimestampped in order to produce a constant + * framerate. + * + * + * Cross-boundary re-encoding. When feeding compatible pre-encoded streams that + * fall on segment boundaries, and for supported formats (right now only H263), + * the GOP will be decoded/reencoded when needed to produce an encoded output + * that fits exactly within the request GstSegment. + * + * + * Missing plugin support. If a #GstElement is missing to encode/mux to the + * request profile formats, a missing-plugin #GstMessage will be posted on the + * #GstBus, allowing systems that support the missing-plugin system to offer the + * user a way to install the missing element. + * + * + * + */ + + +/* 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; + /* Ghostpad with changing target */ + 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, const GstCaps * caps); +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 */ + + /** + * GstEncodeBin:profile: + * + * The #GstEncodingProfile to use. This property must be set before going + * to %GST_STATE_PAUSED or higher. + */ + 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 */ + /** + * GstEncodeBin::request-pad + * @encodebin: a #GstEncodeBin instance + * @caps: a #GstCaps + * + * Use this method to request an unused sink request #GstPad that can take the + * provided @caps as input. You must release the pad with + * gst_element_release_request_pad() when you are done with it. + * + * Returns: A compatible #GstPad, or %NULL if no compatible #GstPad could be + * created or is available. + */ + 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_full = + 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 "); +} + +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; + GList *formatters; + + encode_bin->muxers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER, + GST_RANK_MARGINAL); + formatters = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_FORMATTER, + GST_RANK_SECONDARY); + encode_bin->muxers = g_list_concat (encode_bin->muxers, formatters); + + 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:%s, caps:%" GST_PTR_FORMAT, + g_type_name (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 %s", + g_type_name (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_input_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 * +request_pad_for_stream (GstEncodeBin * encodebin, GType ptype, + const gchar * name, GstCaps * caps) +{ + StreamGroup *sgroup; + GstEncodingProfile *sprof; + + GST_DEBUG_OBJECT (encodebin, "name:%s caps:%" GST_PTR_FORMAT, name, caps); + + /* Figure out if we have a unused GstEncodingProfile we can use for + * these caps */ + sprof = next_unused_stream_profile (encodebin, ptype, caps); + + if (G_UNLIKELY (sprof == NULL)) + goto no_stream_profile; + + sgroup = _create_stream_group (encodebin, sprof, name, 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 GstPad * +gst_encode_bin_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps) +{ + GstEncodeBin *ebin = (GstEncodeBin *) element; + GstPad *res = NULL; + + GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name); + + /* Identify the stream group */ + if (caps != NULL) { + res = request_pad_for_stream (ebin, G_TYPE_NONE, name, (GstCaps *) caps); + } + + if (res == NULL) { + GType ptype = G_TYPE_NONE; + + 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; */ + + /* 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 */ + + res = request_pad_for_stream (ebin, ptype, name, NULL); + } + + return res; +} + +static GstPad * +gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, GstCaps * caps) +{ + GstPad *pad = request_pad_for_stream (encodebin, G_TYPE_NONE, NULL, caps); + + return pad ? GST_PAD_CAST (gst_object_ref (pad)) : 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 * +local_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, 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 = NULL; + /* 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. + * If we are handling a container profile, figure out if the muxer has a + * sinkpad compatible with the selected profile */ + if (ebin->muxer) { + 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 (muxerpad) { + if (G_UNLIKELY (fast_pad_link (srcpad, muxerpad) != GST_PAD_LINK_OK)) { + goto muxer_link_failure; + } + gst_object_unref (muxerpad); + } else { + gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), srcpad); + } + gst_object_unref (srcpad); + + /* 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 = + local_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 = local_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 = local_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, *aconv2; + + 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); + aconv2 = gst_element_factory_make ("audioconvert", NULL); + ares = gst_element_factory_make ("audioresample", NULL); + + gst_bin_add_many ((GstBin *) ebin, arate, aconv, ares, aconv2, NULL); + tosync = g_list_append (tosync, arate); + tosync = g_list_append (tosync, aconv); + tosync = g_list_append (tosync, ares); + tosync = g_list_append (tosync, aconv2); + if (!fast_element_link (arate, aconv) || + !fast_element_link (aconv, ares) || + !fast_element_link (ares, aconv2) || !fast_element_link (aconv2, 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); + sgroup->converters = g_list_prepend (sgroup->converters, aconv2); + + last = arate; + } + + /* Link to stream splitter */ + sinkpad = gst_element_get_static_pad (last, "sink"); + srcpad = local_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); + /* missing plugin support */ + gst_element_post_message (GST_ELEMENT_CAST (ebin), + gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format)); + GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, (NULL), + ("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); + + 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 = NULL; + GstPad *muxerpad; + const GList *tmp, *profiles; + GstEncodingProfile *sprof; + + GST_DEBUG ("Current profile : %s", + gst_encoding_profile_get_name (ebin->profile)); + + if (GST_IS_ENCODING_CONTAINER_PROFILE (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; + } + } + } else { + if (G_UNLIKELY (_create_stream_group (ebin, ebin->profile, 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)); + /* missing plugin support */ + gst_element_post_message (GST_ELEMENT_CAST (ebin), + gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), + gst_encoding_profile_get_format (ebin->profile))); + GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, (NULL), + ("No available muxer for format %" 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"); + if (muxer) + gst_bin_remove (GST_BIN (ebin), muxer); + ebin->muxer = 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); + + if (ebin->muxer) { + /* 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); + + /* Set ghostpad target to NULL */ + gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), NULL); + + /* Remove muxer if present */ + if (ebin->muxer) { + gst_bin_remove (GST_BIN (ebin), ebin->muxer); + ebin->muxer = NULL; + } + + /* 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 (type:%s)", + gst_encoding_profile_get_name (profile), + gst_encoding_profile_get_type_nick (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) +{ + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + GST_DEBUG_OBJECT (ebin, "profile : %s", + gst_encoding_profile_get_name (profile)); + + if (G_UNLIKELY (ebin->active)) { + GST_WARNING_OBJECT (ebin, "Element already active, can't change profile"); + return FALSE; + } + + /* If we're not active, we can deactivate the previous profile */ + if (ebin->profile) + gst_encoding_profile_unref (ebin->profile); + ebin->profile = NULL; + + return gst_encode_bin_setup_profile (ebin, profile); +} + +static inline gboolean +gst_encode_bin_activate (GstEncodeBin * ebin) +{ + ebin->active = ebin->profile != NULL; + return ebin->active; +} + +static void +gst_encode_bin_deactivate (GstEncodeBin * ebin) +{ + ebin->active = FALSE; +} + +static GstStateChangeReturn +gst_encode_bin_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstEncodeBin *ebin = (GstEncodeBin *) element; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + if (!gst_encode_bin_activate (ebin)) { + ret = GST_STATE_CHANGE_FAILURE; + goto beach; + } + break; + default: + break; + } + + ret = + GST_ELEMENT_CLASS (gst_encode_bin_parent_class)->change_state (element, + transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto beach; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_encode_bin_deactivate (ebin); + break; + default: + break; + } + +beach: + return ret; +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean res; + + GST_DEBUG_CATEGORY_INIT (gst_encode_bin_debug, "encodebin", 0, "encoder bin"); + +#ifdef ENABLE_NLS + GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, + LOCALEDIR); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif /* ENABLE_NLS */ + + + res = gst_element_register (plugin, "encodebin", GST_RANK_NONE, + GST_TYPE_ENCODE_BIN); + + return res; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "encoding", + "various encoding-related elements", plugin_init, VERSION, GST_LICENSE, + GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/encoding/gstencodebin.h b/gst/encoding/gstencodebin.h new file mode 100644 index 0000000000..2d594b0f6d --- /dev/null +++ b/gst/encoding/gstencodebin.h @@ -0,0 +1,39 @@ +/* GStreamer encoding bin + * Copyright (C) 2009 Edward Hervey + * (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 +#include +#include "gstencode-marshal.h" + +#define GST_TYPE_ENCODE_BIN (gst_encode_bin_get_type()) +#define GST_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODE_BIN,GstEncodeBin)) +#define GST_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ENCODE_BIN,GstEncodeBinClass)) +#define GST_IS_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODE_BIN)) +#define GST_IS_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ENCODE_BIN)) + +typedef struct _GstEncodeBin GstEncodeBin; +typedef struct _GstEncodeBinClass GstEncodeBinClass; + +GType gst_encode_bin_get_type(void); + +#endif /* __GST_ENCODEBIN_H__ */ diff --git a/gst/encoding/gstsmartencoder.c b/gst/encoding/gstsmartencoder.c new file mode 100644 index 0000000000..ed0e42bd5c --- /dev/null +++ b/gst/encoding/gstsmartencoder.c @@ -0,0 +1,701 @@ +/* GStreamer Smart Video Encoder element + * Copyright (C) <2010> Edward Hervey + * + * 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 +#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 "); + + gobject_class->dispose = (GObjectFinalizeFunc) (gst_smart_encoder_dispose); + element_class->change_state = gst_smart_encoder_change_state; + + GST_DEBUG_CATEGORY_INIT (smart_encoder_debug, "smartencoder", 0, + "Smart Encoder"); +} + +static void +smart_encoder_reset (GstSmartEncoder * smart_encoder) +{ + gst_segment_init (smart_encoder->segment, GST_FORMAT_UNDEFINED); + + if (smart_encoder->encoder) { + /* Clean up/remove elements */ + gst_element_set_state (smart_encoder->encoder, GST_STATE_NULL); + gst_element_set_state (smart_encoder->decoder, GST_STATE_NULL); + gst_element_set_bus (smart_encoder->encoder, NULL); + gst_element_set_bus (smart_encoder->decoder, NULL); + gst_pad_set_active (smart_encoder->internal_srcpad, FALSE); + gst_pad_set_active (smart_encoder->internal_sinkpad, FALSE); + gst_object_unref (smart_encoder->encoder); + gst_object_unref (smart_encoder->decoder); + gst_object_unref (smart_encoder->internal_srcpad); + gst_object_unref (smart_encoder->internal_sinkpad); + + smart_encoder->encoder = NULL; + smart_encoder->decoder = NULL; + smart_encoder->internal_sinkpad = NULL; + smart_encoder->internal_srcpad = NULL; + } + + if (smart_encoder->newsegment) { + gst_event_unref (smart_encoder->newsegment); + smart_encoder->newsegment = NULL; + } +} + + +static void +gst_smart_encoder_init (GstSmartEncoder * smart_encoder) +{ + smart_encoder->sinkpad = + gst_pad_new_from_static_template (&sink_template, "sink"); + gst_pad_set_chain_function (smart_encoder->sinkpad, gst_smart_encoder_chain); + gst_pad_set_event_function (smart_encoder->sinkpad, smart_encoder_sink_event); + gst_pad_set_getcaps_function (smart_encoder->sinkpad, + smart_encoder_sink_getcaps); + gst_element_add_pad (GST_ELEMENT (smart_encoder), smart_encoder->sinkpad); + + smart_encoder->srcpad = + gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_use_fixed_caps (smart_encoder->srcpad); + gst_element_add_pad (GST_ELEMENT (smart_encoder), smart_encoder->srcpad); + + smart_encoder->segment = gst_segment_new (); + + smart_encoder_reset (smart_encoder); +} + +void +gst_smart_encoder_dispose (GObject * object) +{ + GstSmartEncoder *smart_encoder = (GstSmartEncoder *) object; + + if (smart_encoder->segment) + gst_segment_free (smart_encoder->segment); + smart_encoder->segment = NULL; + if (smart_encoder->available_caps) + gst_caps_unref (smart_encoder->available_caps); + smart_encoder->available_caps = NULL; + G_OBJECT_CLASS (gst_smart_encoder_parent_class)->dispose (object); +} + +static GstFlowReturn +gst_smart_encoder_reencode_gop (GstSmartEncoder * smart_encoder) +{ + GstFlowReturn res = GST_FLOW_OK; + GList *tmp; + + if (smart_encoder->encoder == NULL) { + if (!setup_recoder_pipeline (smart_encoder)) + return GST_FLOW_ERROR; + } + + /* Activate elements */ + /* Set elements to PAUSED */ + gst_element_set_state (smart_encoder->encoder, GST_STATE_PAUSED); + gst_element_set_state (smart_encoder->decoder, GST_STATE_PAUSED); + + GST_INFO ("Pushing Flush start/stop to clean decoder/encoder"); + gst_pad_push_event (smart_encoder->internal_srcpad, + gst_event_new_flush_start ()); + gst_pad_push_event (smart_encoder->internal_srcpad, + gst_event_new_flush_stop ()); + + /* push newsegment */ + GST_INFO ("Pushing newsegment %" GST_PTR_FORMAT, smart_encoder->newsegment); + gst_pad_push_event (smart_encoder->internal_srcpad, + gst_event_ref (smart_encoder->newsegment)); + + /* Push buffers through our pads */ + GST_DEBUG ("Pushing pending buffers"); + + for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { + GstBuffer *buf = (GstBuffer *) tmp->data; + + res = gst_pad_push (smart_encoder->internal_srcpad, buf); + if (G_UNLIKELY (res != GST_FLOW_OK)) + break; + } + + if (G_UNLIKELY (res != GST_FLOW_OK)) { + GST_WARNING ("Error pushing pending buffers : %s", gst_flow_get_name (res)); + /* Remove pending bfufers */ + for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { + gst_buffer_unref ((GstBuffer *) tmp->data); + } + } else { + GST_INFO ("Pushing out EOS to flush out decoder/encoder"); + gst_pad_push_event (smart_encoder->internal_srcpad, gst_event_new_eos ()); + } + + /* Activate elements */ + /* Set elements to PAUSED */ + gst_element_set_state (smart_encoder->encoder, GST_STATE_NULL); + gst_element_set_state (smart_encoder->decoder, GST_STATE_NULL); + + g_list_free (smart_encoder->pending_gop); + smart_encoder->pending_gop = NULL; + + return res; +} + +static GstFlowReturn +gst_smart_encoder_push_pending_gop (GstSmartEncoder * smart_encoder) +{ + gint64 cstart, cstop; + GList *tmp; + GstFlowReturn res = GST_FLOW_OK; + + GST_DEBUG ("Pushing pending GOP (%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT + ")", GST_TIME_ARGS (smart_encoder->gop_start), + GST_TIME_ARGS (smart_encoder->gop_stop)); + + /* If GOP is entirely within segment, just push downstream */ + if (gst_segment_clip (smart_encoder->segment, GST_FORMAT_TIME, + smart_encoder->gop_start, smart_encoder->gop_stop, &cstart, &cstop)) { + if ((cstart != smart_encoder->gop_start) + || (cstop != smart_encoder->gop_stop)) { + GST_DEBUG ("GOP needs to be re-encoded from %" GST_TIME_FORMAT " to %" + GST_TIME_FORMAT, GST_TIME_ARGS (cstart), GST_TIME_ARGS (cstop)); + res = gst_smart_encoder_reencode_gop (smart_encoder); + } else { + /* The whole GOP is within the segment, push all pending buffers downstream */ + GST_DEBUG ("GOP doesn't need to be modified, pushing downstream"); + for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { + GstBuffer *buf = (GstBuffer *) tmp->data; + res = gst_pad_push (smart_encoder->srcpad, buf); + if (G_UNLIKELY (res != GST_FLOW_OK)) + break; + } + } + } else { + /* The whole GOP is outside the segment, there's most likely + * a bug somewhere. */ + GST_WARNING + ("GOP is entirely outside of the segment, upstream gave us too much data"); + for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { + gst_buffer_unref ((GstBuffer *) tmp->data); + } + } + + if (smart_encoder->pending_gop) { + g_list_free (smart_encoder->pending_gop); + smart_encoder->pending_gop = NULL; + } + smart_encoder->gop_start = GST_CLOCK_TIME_NONE; + smart_encoder->gop_stop = GST_CLOCK_TIME_NONE; + + return res; +} + +static GstFlowReturn +gst_smart_encoder_chain (GstPad * pad, GstBuffer * buf) +{ + GstSmartEncoder *smart_encoder; + GstFlowReturn res = GST_FLOW_OK; + gboolean discont, keyframe; + + smart_encoder = GST_SMART_ENCODER (gst_object_get_parent (GST_OBJECT (pad))); + + discont = GST_BUFFER_IS_DISCONT (buf); + keyframe = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + + GST_DEBUG ("New buffer %s %s %" GST_TIME_FORMAT, + discont ? "discont" : "", + keyframe ? "keyframe" : "", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + if (keyframe) { + GST_DEBUG ("Got a keyframe"); + + /* If there's a pending GOP, flush it out */ + if (smart_encoder->pending_gop) { + /* Mark gop_stop */ + smart_encoder->gop_stop = GST_BUFFER_TIMESTAMP (buf); + + /* flush pending */ + res = gst_smart_encoder_push_pending_gop (smart_encoder); + if (G_UNLIKELY (res != GST_FLOW_OK)) + goto beach; + } + + /* Mark gop_start for new gop */ + smart_encoder->gop_start = GST_BUFFER_TIMESTAMP (buf); + } + + /* Store buffer */ + smart_encoder->pending_gop = g_list_append (smart_encoder->pending_gop, buf); + /* Update GOP stop position */ + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + smart_encoder->gop_stop = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) + smart_encoder->gop_stop += GST_BUFFER_DURATION (buf); + } + + GST_DEBUG ("Buffer stored , Current GOP : %" GST_TIME_FORMAT " -- %" + GST_TIME_FORMAT, GST_TIME_ARGS (smart_encoder->gop_start), + GST_TIME_ARGS (smart_encoder->gop_stop)); + +beach: + gst_object_unref (smart_encoder); + return res; +} + +static gboolean +smart_encoder_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + GstSmartEncoder *smart_encoder = GST_SMART_ENCODER (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + smart_encoder_reset (smart_encoder); + break; + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + GST_DEBUG_OBJECT (smart_encoder, + "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT + ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, + update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), + GST_TIME_ARGS (time)); + if (format != GST_FORMAT_TIME) + GST_ERROR + ("smart_encoder can not handle streams not specified in GST_FORMAT_TIME"); + + /* now configure the values */ + gst_segment_set_newsegment_full (smart_encoder->segment, update, + rate, arate, format, start, stop, time); + + /* And keep a copy for further usage */ + if (smart_encoder->newsegment) + gst_event_unref (smart_encoder->newsegment); + smart_encoder->newsegment = gst_event_ref (event); + } + break; + case GST_EVENT_EOS: + GST_DEBUG ("Eos, flushing remaining data"); + gst_smart_encoder_push_pending_gop (smart_encoder); + break; + default: + break; + } + + res = gst_pad_push_event (smart_encoder->srcpad, event); + + gst_object_unref (smart_encoder); + return res; +} + +static GstCaps * +smart_encoder_sink_getcaps (GstPad * pad) +{ + GstCaps *peer, *tmpl, *res; + GstSmartEncoder *smart_encoder = GST_SMART_ENCODER (gst_pad_get_parent (pad)); + + /* Try getting it from downstream */ + peer = gst_pad_peer_get_caps_reffed (smart_encoder->srcpad); + + /* Use computed caps */ + if (smart_encoder->available_caps) + tmpl = gst_caps_ref (smart_encoder->available_caps); + else + tmpl = gst_static_pad_template_get_caps (&src_template); + + if (peer == NULL) { + res = tmpl; + } else { + res = gst_caps_intersect (peer, tmpl); + gst_caps_unref (peer); + gst_caps_unref (tmpl); + } + + gst_object_unref (smart_encoder); + return res; +} + +/***************************************** + * Internal encoder/decoder pipeline * + ******************************************/ + +static GstElementFactory * +get_decoder_factory (GstCaps * caps) +{ + GstElementFactory *fact = NULL; + GList *decoders, *tmp; + + tmp = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER, + GST_RANK_MARGINAL); + decoders = gst_element_factory_list_filter (tmp, caps, GST_PAD_SINK, FALSE); + gst_plugin_feature_list_free (tmp); + + for (tmp = decoders; tmp; tmp = tmp->next) { + /* We just pick the first one */ + fact = (GstElementFactory *) tmp->data; + gst_object_ref (fact); + break; + } + + gst_plugin_feature_list_free (decoders); + + return fact; +} + +static GstElementFactory * +get_encoder_factory (GstCaps * caps) +{ + GstElementFactory *fact = NULL; + GList *encoders, *tmp; + + tmp = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER, + GST_RANK_MARGINAL); + encoders = gst_element_factory_list_filter (tmp, caps, GST_PAD_SRC, FALSE); + gst_plugin_feature_list_free (tmp); + + for (tmp = encoders; tmp; tmp = tmp->next) { + /* We just pick the first one */ + fact = (GstElementFactory *) tmp->data; + gst_object_ref (fact); + break; + } + + gst_plugin_feature_list_free (encoders); + + return fact; +} + +static GstElement * +get_decoder (GstCaps * caps) +{ + GstElementFactory *fact = get_decoder_factory (caps); + GstElement *res = NULL; + + if (fact) { + res = gst_element_factory_create (fact, "internal-decoder"); + gst_object_unref (fact); + } + return res; +} + +static GstElement * +get_encoder (GstCaps * caps) +{ + GstElementFactory *fact = get_encoder_factory (caps); + GstElement *res = NULL; + + if (fact) { + res = gst_element_factory_create (fact, "internal-encoder"); + gst_object_unref (fact); + } + return res; +} + +static GstFlowReturn +internal_chain (GstPad * pad, GstBuffer * buf) +{ + GstSmartEncoder *smart_encoder = + g_object_get_qdata ((GObject *) pad, INTERNAL_ELEMENT); + + return gst_pad_push (smart_encoder->srcpad, buf); +} + +static gboolean +setup_recoder_pipeline (GstSmartEncoder * smart_encoder) +{ + GstPad *tmppad; + + /* Fast path */ + if (G_UNLIKELY (smart_encoder->encoder)) + return TRUE; + + GST_DEBUG ("Creating internal decoder and encoder"); + + /* Create decoder/encoder */ + smart_encoder->decoder = get_decoder (GST_PAD_CAPS (smart_encoder->sinkpad)); + if (G_UNLIKELY (smart_encoder->decoder == NULL)) + goto no_decoder; + gst_element_set_bus (smart_encoder->decoder, GST_ELEMENT_BUS (smart_encoder)); + + smart_encoder->encoder = get_encoder (GST_PAD_CAPS (smart_encoder->sinkpad)); + if (G_UNLIKELY (smart_encoder->encoder == NULL)) + goto no_encoder; + gst_element_set_bus (smart_encoder->encoder, GST_ELEMENT_BUS (smart_encoder)); + + GST_DEBUG ("Creating internal pads"); + + /* Create internal pads */ + + /* Source pad which we'll use to feed data to decoders */ + smart_encoder->internal_srcpad = gst_pad_new ("internal_src", GST_PAD_SRC); + g_object_set_qdata ((GObject *) smart_encoder->internal_srcpad, + INTERNAL_ELEMENT, smart_encoder); + gst_pad_set_caps (smart_encoder->internal_srcpad, + GST_PAD_CAPS (smart_encoder->sinkpad)); + gst_pad_set_active (smart_encoder->internal_srcpad, TRUE); + + /* Sink pad which will get the buffers from the encoder. + * Note: We don't need an event function since we'll be discarding all + * of them. */ + smart_encoder->internal_sinkpad = gst_pad_new ("internal_sink", GST_PAD_SINK); + g_object_set_qdata ((GObject *) smart_encoder->internal_sinkpad, + INTERNAL_ELEMENT, smart_encoder); + gst_pad_set_caps (smart_encoder->internal_sinkpad, + GST_PAD_CAPS (smart_encoder->sinkpad)); + gst_pad_set_chain_function (smart_encoder->internal_sinkpad, internal_chain); + gst_pad_set_active (smart_encoder->internal_sinkpad, TRUE); + + GST_DEBUG ("Linking pads to elements"); + + /* Link everything */ + tmppad = gst_element_get_static_pad (smart_encoder->encoder, "src"); + if (GST_PAD_LINK_FAILED (gst_pad_link (tmppad, + smart_encoder->internal_sinkpad))) + goto sinkpad_link_fail; + gst_object_unref (tmppad); + + if (!gst_element_link (smart_encoder->decoder, smart_encoder->encoder)) + goto encoder_decoder_link_fail; + + tmppad = gst_element_get_static_pad (smart_encoder->decoder, "sink"); + if (GST_PAD_LINK_FAILED (gst_pad_link (smart_encoder->internal_srcpad, + tmppad))) + goto srcpad_link_fail; + gst_object_unref (tmppad); + + GST_DEBUG ("Done creating internal elements/pads"); + + return TRUE; + +no_decoder: + { + GST_WARNING ("Couldn't find a decoder for %" GST_PTR_FORMAT, + GST_PAD_CAPS (smart_encoder->sinkpad)); + return FALSE; + } + +no_encoder: + { + GST_WARNING ("Couldn't find an encoder for %" GST_PTR_FORMAT, + GST_PAD_CAPS (smart_encoder->sinkpad)); + return FALSE; + } + +srcpad_link_fail: + { + gst_object_unref (tmppad); + GST_WARNING ("Couldn't link internal srcpad to decoder"); + return FALSE; + } + +sinkpad_link_fail: + { + gst_object_unref (tmppad); + GST_WARNING ("Couldn't link encoder to internal sinkpad"); + return FALSE; + } + +encoder_decoder_link_fail: + { + GST_WARNING ("Couldn't link decoder to encoder"); + return FALSE; + } +} + +static GstStateChangeReturn +gst_smart_encoder_find_elements (GstSmartEncoder * smart_encoder) +{ + guint i, n; + GstCaps *tmpl, *st, *res; + GstElementFactory *dec, *enc; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + if (G_UNLIKELY (smart_encoder->available_caps)) + goto beach; + + /* Iterate over all pad template caps and see if we have both an + * encoder and a decoder for those media types */ + tmpl = gst_static_pad_template_get_caps (&src_template); + res = gst_caps_new_empty (); + n = gst_caps_get_size (tmpl); + + for (i = 0; i < n; i++) { + st = gst_caps_copy_nth (tmpl, i); + GST_DEBUG_OBJECT (smart_encoder, + "Checking for available decoder and encoder for %" GST_PTR_FORMAT, st); + if (!(dec = get_decoder_factory (st))) { + gst_caps_unref (st); + continue; + } + gst_object_unref (dec); + if (!(enc = get_encoder_factory (st))) { + gst_caps_unref (st); + continue; + } + gst_object_unref (enc); + GST_DEBUG_OBJECT (smart_encoder, "OK"); + gst_caps_append (res, st); + } + + gst_caps_unref (tmpl); + + if (gst_caps_is_empty (res)) + ret = GST_STATE_CHANGE_FAILURE; + else + smart_encoder->available_caps = res; + + GST_DEBUG_OBJECT (smart_encoder, "Done, available_caps:%" GST_PTR_FORMAT, + smart_encoder->available_caps); + +beach: + return ret; +} + +/****************************************** + * GstElement vmethod implementations * + ******************************************/ + +static GstStateChangeReturn +gst_smart_encoder_change_state (GstElement * element, GstStateChange transition) +{ + GstSmartEncoder *smart_encoder; + GstStateChangeReturn ret; + + g_return_val_if_fail (GST_IS_SMART_ENCODER (element), + GST_STATE_CHANGE_FAILURE); + + smart_encoder = GST_SMART_ENCODER (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + /* Figure out which elements are available */ + if ((ret = + gst_smart_encoder_find_elements (smart_encoder)) == + GST_STATE_CHANGE_FAILURE) + goto beach; + break; + default: + break; + } + + ret = + GST_ELEMENT_CLASS (gst_smart_encoder_parent_class)->change_state (element, + transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + smart_encoder_reset (smart_encoder); + break; + default: + break; + } + +beach: + return ret; +} diff --git a/gst/encoding/gstsmartencoder.h b/gst/encoding/gstsmartencoder.h new file mode 100644 index 0000000000..15366269c8 --- /dev/null +++ b/gst/encoding/gstsmartencoder.h @@ -0,0 +1,71 @@ +/* GStreamer video re-encoder element + * Copyright (C) <2010> Edward Hervey + * + * 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 + +G_BEGIN_DECLS + +#define GST_TYPE_SMART_ENCODER \ + (gst_smart_encoder_get_type()) +#define GST_SMART_ENCODER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SMART_ENCODER,GstSmartEncoder)) +#define GST_SMART_ENCODER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SMART_ENCODER,GstSmartEncoderClass)) +#define GST_IS_SMART_ENCODER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SMART_ENCODER)) +#define GST_IS_SMART_ENCODER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SMART_ENCODER)) + +typedef struct _GstSmartEncoder GstSmartEncoder; +typedef struct _GstSmartEncoderClass GstSmartEncoderClass; + +struct _GstSmartEncoder { + GstElement element; + + GstPad *sinkpad, *srcpad; + + GstSegment *segment; + GstEvent *newsegment; + + /* Pending GOP to be checked */ + GList *pending_gop; + guint64 gop_start; /* GOP start in running time */ + guint64 gop_stop; /* GOP end in running time */ + + /* Internal recoding elements */ + GstPad *internal_sinkpad; + GstPad *internal_srcpad; + GstElement *decoder; + GstElement *encoder; + + /* Available caps at runtime */ + GstCaps *available_caps; +}; + +struct _GstSmartEncoderClass { + GstElementClass parent_class; +}; + +GType gst_smart_encoder_get_type(void); + +G_END_DECLS + +#endif /* __SMART_ENCODER_H__ */ diff --git a/gst/encoding/gststreamcombiner.c b/gst/encoding/gststreamcombiner.c new file mode 100644 index 0000000000..72d40fe756 --- /dev/null +++ b/gst/encoding/gststreamcombiner.c @@ -0,0 +1,276 @@ +/* GStreamer Stream Combiner + * Copyright (C) 2010 Edward Hervey + * (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 "); +} + +static void +gst_stream_combiner_dispose (GObject * object) +{ + GstStreamCombiner *stream_combiner = (GstStreamCombiner *) object; + + if (stream_combiner->lock) { + g_mutex_free (stream_combiner->lock); + stream_combiner->lock = NULL; + } + + G_OBJECT_CLASS (gst_stream_combiner_parent_class)->dispose (object); +} + +static GstFlowReturn +gst_stream_combiner_chain (GstPad * pad, GstBuffer * buf) +{ + GstStreamCombiner *stream_combiner = + (GstStreamCombiner *) GST_PAD_PARENT (pad); + /* FIXME : IMPLEMENT */ + + /* with lock taken, check if we're the active stream, if not drop */ + return gst_pad_push (stream_combiner->srcpad, buf); +} + +static gboolean +gst_stream_combiner_sink_event (GstPad * pad, GstEvent * event) +{ + GstStreamCombiner *stream_combiner = + (GstStreamCombiner *) GST_PAD_PARENT (pad); + /* FIXME : IMPLEMENT */ + + GST_DEBUG_OBJECT (pad, "Got event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM: + if (gst_event_has_name (event, "stream-switching-eos")) { + gst_event_unref (event); + event = gst_event_new_eos (); + } + break; + default: + break; + } + + /* NEW_SEGMENT : lock, wait for other stream to EOS, select stream, unlock, push */ + /* EOS : lock, mark pad as unused, unlock , drop event */ + /* CUSTOM_REAL_EOS : push EOS downstream */ + /* FLUSH_START : lock, mark as flushing, unlock. if wasn't flushing forward */ + /* FLUSH_STOP : lock, unmark as flushing, unlock, if was flushing forward */ + /* OTHER : if selected pad forward */ + return gst_pad_push_event (stream_combiner->srcpad, event); +} + +static GstCaps * +gst_stream_combiner_sink_getcaps (GstPad * pad) +{ + GstStreamCombiner *stream_combiner = + (GstStreamCombiner *) GST_PAD_PARENT (pad); + + return gst_pad_peer_get_caps_reffed (stream_combiner->srcpad); +} + +static gboolean +gst_stream_combiner_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStreamCombiner *stream_combiner = + (GstStreamCombiner *) GST_PAD_PARENT (pad); + GstPad *peer; + gboolean res = FALSE; + + GST_DEBUG_OBJECT (pad, "caps:%" GST_PTR_FORMAT, caps); + + peer = gst_pad_get_peer (stream_combiner->srcpad); + if (peer) { + GST_DEBUG_OBJECT (peer, "Setting caps"); + res = gst_pad_set_caps (peer, caps); + gst_object_unref (peer); + } else + GST_WARNING_OBJECT (stream_combiner, "sourcepad has no peer !"); + return res; +} + +static gboolean +gst_stream_combiner_src_event (GstPad * pad, GstEvent * event) +{ + GstStreamCombiner *stream_combiner = + (GstStreamCombiner *) GST_PAD_PARENT (pad); + GstPad *sinkpad = NULL; + + STREAMS_LOCK (stream_combiner); + if (stream_combiner->current) + sinkpad = stream_combiner->current; + else if (stream_combiner->sinkpads) + sinkpad = (GstPad *) stream_combiner->sinkpads->data; + STREAMS_UNLOCK (stream_combiner); + + if (sinkpad) + /* Forward upstream as is */ + return gst_pad_push_event (sinkpad, event); + return FALSE; +} + +static gboolean +gst_stream_combiner_src_query (GstPad * pad, GstQuery * query) +{ + GstStreamCombiner *stream_combiner = + (GstStreamCombiner *) GST_PAD_PARENT (pad); + + GstPad *sinkpad = NULL; + + STREAMS_LOCK (stream_combiner); + if (stream_combiner->current) + sinkpad = stream_combiner->current; + else if (stream_combiner->sinkpads) + sinkpad = (GstPad *) stream_combiner->sinkpads->data; + STREAMS_UNLOCK (stream_combiner); + + if (sinkpad) + /* Forward upstream as is */ + return gst_pad_peer_query (sinkpad, query); + return FALSE; +} + +static void +gst_stream_combiner_init (GstStreamCombiner * stream_combiner) +{ + stream_combiner->srcpad = + gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_set_event_function (stream_combiner->srcpad, + gst_stream_combiner_src_event); + gst_pad_set_query_function (stream_combiner->srcpad, + gst_stream_combiner_src_query); + gst_element_add_pad (GST_ELEMENT (stream_combiner), stream_combiner->srcpad); + + stream_combiner->lock = g_mutex_new (); +} + +static GstPad * +gst_stream_combiner_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name) +{ + GstStreamCombiner *stream_combiner = (GstStreamCombiner *) element; + GstPad *sinkpad; + + GST_DEBUG_OBJECT (element, "templ:%p, name:%s", templ, name); + + sinkpad = gst_pad_new_from_static_template (&sink_template, name); + /* FIXME : No buffer alloc for the time being, it will resort to the fallback */ + /* gst_pad_set_bufferalloc_function (sinkpad, gst_stream_combiner_buffer_alloc); */ + gst_pad_set_chain_function (sinkpad, gst_stream_combiner_chain); + gst_pad_set_event_function (sinkpad, gst_stream_combiner_sink_event); + gst_pad_set_getcaps_function (sinkpad, gst_stream_combiner_sink_getcaps); + gst_pad_set_setcaps_function (sinkpad, gst_stream_combiner_sink_setcaps); + + STREAMS_LOCK (stream_combiner); + stream_combiner->sinkpads = + g_list_append (stream_combiner->sinkpads, sinkpad); + gst_pad_set_active (sinkpad, TRUE); + gst_element_add_pad (element, sinkpad); + stream_combiner->cookie++; + STREAMS_UNLOCK (stream_combiner); + + GST_DEBUG_OBJECT (element, "Returning pad %p", sinkpad); + + return sinkpad; +} + +static void +gst_stream_combiner_release_pad (GstElement * element, GstPad * pad) +{ + GstStreamCombiner *stream_combiner = (GstStreamCombiner *) element; + GList *tmp; + + GST_DEBUG_OBJECT (element, "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + STREAMS_LOCK (stream_combiner); + tmp = g_list_find (stream_combiner->sinkpads, pad); + if (tmp) { + GstPad *pad = (GstPad *) tmp->data; + + stream_combiner->sinkpads = + g_list_delete_link (stream_combiner->sinkpads, tmp); + stream_combiner->cookie++; + + if (pad == stream_combiner->current) { + /* Deactivate current flow */ + GST_DEBUG_OBJECT (element, "Removed pad was the current one"); + stream_combiner->current = NULL; + } + GST_DEBUG_OBJECT (element, "Removing pad from ourself"); + gst_element_remove_pad (element, pad); + } + STREAMS_UNLOCK (stream_combiner); + + return; +} diff --git a/gst/encoding/gststreamcombiner.h b/gst/encoding/gststreamcombiner.h new file mode 100644 index 0000000000..ce67277d72 --- /dev/null +++ b/gst/encoding/gststreamcombiner.h @@ -0,0 +1,60 @@ +/* GStreamer Stream Combiner + * Copyright (C) 2010 Edward Hervey + * (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 + +#define GST_TYPE_STREAM_COMBINER (gst_stream_combiner_get_type()) +#define GST_STREAM_COMBINER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STREAM_COMBINER,GstStreamCombiner)) +#define GST_STREAM_COMBINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STREAM_COMBINER,GstStreamCombinerClass)) +#define GST_IS_STREAM_COMBINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STREAM_COMBINER)) +#define GST_IS_STREAM_COMBINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STREAM_COMBINER)) + +typedef struct _GstStreamCombiner GstStreamCombiner; +typedef struct _GstStreamCombinerClass GstStreamCombinerClass; + +struct _GstStreamCombiner { + GstElement parent; + + GstPad *srcpad; + + /* lock protects: + * * the current pad + * * the list of srcpads + */ + GMutex *lock; + /* Currently activated srcpad */ + GstPad *current; + GList *sinkpads; + guint32 cookie; + +}; + +struct _GstStreamCombinerClass { + GstElementClass parent; +}; + +GType gst_stream_combiner_get_type(void); + +GstElement *gst_stream_combiner_new (gchar *name); + +#endif /* __GST_STREAMCOMBINER_H__ */ diff --git a/gst/encoding/gststreamsplitter.c b/gst/encoding/gststreamsplitter.c new file mode 100644 index 0000000000..c473386e56 --- /dev/null +++ b/gst/encoding/gststreamsplitter.c @@ -0,0 +1,435 @@ +/* GStreamer Stream Splitter + * Copyright (C) 2010 Edward Hervey + * (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 "); +} + +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_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref, + NULL); + g_list_free (stream_splitter->pending_events); + stream_splitter->pending_events = 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) { + g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref, + NULL); + g_list_free (stream_splitter->pending_events); + stream_splitter->pending_events = NULL; + } + + if (store) { + stream_splitter->pending_events = + g_list_append (stream_splitter->pending_events, event); + } else if (toall || eos) { + GList *tmp; + guint32 cookie; + + /* Send to all pads */ + STREAMS_LOCK (stream_splitter); + resync: + if (G_UNLIKELY (stream_splitter->srcpads == NULL)) { + STREAMS_UNLOCK (stream_splitter); + /* No source pads */ + gst_event_unref (event); + res = FALSE; + goto beach; + } + tmp = stream_splitter->srcpads; + cookie = stream_splitter->cookie; + while (tmp) { + GstPad *srcpad = (GstPad *) tmp->data; + STREAMS_UNLOCK (stream_splitter); + /* In case of EOS, we first push out the real one to flush out + * each streams (but which will be discarded in the streamcombiner) + * before our custom one (which will be converted back to and EOS + * in the streamcombiner) */ + if (eos) + gst_pad_push_event (srcpad, gst_event_new_eos ()); + gst_event_ref (event); + res = gst_pad_push_event (srcpad, event); + STREAMS_LOCK (stream_splitter); + if (G_UNLIKELY (cookie != stream_splitter->cookie)) + goto resync; + tmp = tmp->next; + } + STREAMS_UNLOCK (stream_splitter); + gst_event_unref (event); + } else { + GstPad *pad; + + /* Only send to current pad */ + + STREAMS_LOCK (stream_splitter); + pad = stream_splitter->current; + STREAMS_UNLOCK (stream_splitter); + if (pad) + res = gst_pad_push_event (pad, event); + else { + gst_event_unref (event); + res = FALSE; + } + } + +beach: + return res; +} + +static GstCaps * +gst_stream_splitter_sink_getcaps (GstPad * pad) +{ + GstStreamSplitter *stream_splitter = + (GstStreamSplitter *) GST_PAD_PARENT (pad); + guint32 cookie; + GList *tmp; + GstCaps *res = NULL; + + /* Return the combination of all downstream caps */ + + STREAMS_LOCK (stream_splitter); + +resync: + if (G_UNLIKELY (stream_splitter->srcpads == NULL)) { + res = gst_caps_new_any (); + goto beach; + } + + res = NULL; + cookie = stream_splitter->cookie; + tmp = stream_splitter->srcpads; + + while (tmp) { + GstPad *srcpad = (GstPad *) tmp->data; + + STREAMS_UNLOCK (stream_splitter); + if (res) + gst_caps_merge (res, gst_pad_peer_get_caps_reffed (srcpad)); + else + res = gst_pad_peer_get_caps (srcpad); + STREAMS_LOCK (stream_splitter); + + if (G_UNLIKELY (cookie != stream_splitter->cookie)) { + if (res) + gst_caps_unref (res); + goto resync; + } + tmp = tmp->next; + } + +beach: + STREAMS_UNLOCK (stream_splitter); + return res; +} + +static gboolean +gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStreamSplitter *stream_splitter = + (GstStreamSplitter *) GST_PAD_PARENT (pad); + guint32 cookie; + GList *tmp; + gboolean res; + + GST_DEBUG_OBJECT (stream_splitter, "caps %" GST_PTR_FORMAT, caps); + + /* Try on all pads, choose the one that succeeds as the current stream */ + STREAMS_LOCK (stream_splitter); + +resync: + if (G_UNLIKELY (stream_splitter->srcpads == NULL)) { + res = FALSE; + goto beach; + } + + res = FALSE; + tmp = stream_splitter->srcpads; + cookie = stream_splitter->cookie; + + while (tmp) { + GstPad *srcpad = (GstPad *) tmp->data; + GstCaps *peercaps; + + STREAMS_UNLOCK (stream_splitter); + peercaps = gst_pad_peer_get_caps_reffed (srcpad); + if (peercaps) { + res = gst_caps_can_intersect (caps, peercaps); + gst_caps_unref (peercaps); + } + STREAMS_LOCK (stream_splitter); + + if (G_UNLIKELY (cookie != stream_splitter->cookie)) + goto resync; + + if (res) { + /* FIXME : we need to switch properly */ + GST_DEBUG_OBJECT (srcpad, "Setting caps on this pad was succesfull"); + stream_splitter->current = srcpad; + goto beach; + } + tmp = tmp->next; + } + +beach: + STREAMS_UNLOCK (stream_splitter); + return res; +} + +static gboolean +gst_stream_splitter_src_event (GstPad * pad, GstEvent * event) +{ + GstStreamSplitter *stream_splitter = + (GstStreamSplitter *) GST_PAD_PARENT (pad); + + GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event)); + + /* Forward upstream as is */ + return gst_pad_push_event (stream_splitter->sinkpad, event); +} + +static gboolean +gst_stream_splitter_src_query (GstPad * pad, GstQuery * query) +{ + GstStreamSplitter *stream_splitter = + (GstStreamSplitter *) GST_PAD_PARENT (pad); + + GST_DEBUG_OBJECT (pad, "%s", GST_QUERY_TYPE_NAME (query)); + + /* Forward upstream as is */ + return gst_pad_peer_query (stream_splitter->sinkpad, query); +} + +static void +gst_stream_splitter_init (GstStreamSplitter * stream_splitter) +{ + stream_splitter->sinkpad = + gst_pad_new_from_static_template (&sink_template, "sink"); + /* FIXME : No buffer alloc for the time being, it will resort to the fallback */ + /* gst_pad_set_bufferalloc_function (stream_splitter->sinkpad, */ + /* gst_stream_splitter_buffer_alloc); */ + gst_pad_set_chain_function (stream_splitter->sinkpad, + gst_stream_splitter_chain); + gst_pad_set_event_function (stream_splitter->sinkpad, + gst_stream_splitter_sink_event); + gst_pad_set_getcaps_function (stream_splitter->sinkpad, + gst_stream_splitter_sink_getcaps); + gst_pad_set_setcaps_function (stream_splitter->sinkpad, + gst_stream_splitter_sink_setcaps); + gst_element_add_pad (GST_ELEMENT (stream_splitter), stream_splitter->sinkpad); + + stream_splitter->lock = g_mutex_new (); +} + +static GstPad * +gst_stream_splitter_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name) +{ + GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element; + GstPad *srcpad; + + srcpad = gst_pad_new_from_static_template (&src_template, name); + gst_pad_set_event_function (srcpad, gst_stream_splitter_src_event); + gst_pad_set_query_function (srcpad, gst_stream_splitter_src_query); + + STREAMS_LOCK (stream_splitter); + stream_splitter->srcpads = g_list_append (stream_splitter->srcpads, srcpad); + gst_pad_set_active (srcpad, TRUE); + gst_element_add_pad (element, srcpad); + stream_splitter->cookie++; + STREAMS_UNLOCK (stream_splitter); + + return srcpad; +} + +static void +gst_stream_splitter_release_pad (GstElement * element, GstPad * pad) +{ + GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element; + GList *tmp; + + STREAMS_LOCK (stream_splitter); + tmp = g_list_find (stream_splitter->srcpads, pad); + if (tmp) { + GstPad *pad = (GstPad *) tmp->data; + + stream_splitter->srcpads = + g_list_delete_link (stream_splitter->srcpads, tmp); + stream_splitter->cookie++; + + if (pad == stream_splitter->current) { + /* Deactivate current flow */ + GST_DEBUG_OBJECT (element, "Removed pad was the current one"); + stream_splitter->current = NULL; + } + + gst_element_remove_pad (element, pad); + } + STREAMS_UNLOCK (stream_splitter); + + return; +} diff --git a/gst/encoding/gststreamsplitter.h b/gst/encoding/gststreamsplitter.h new file mode 100644 index 0000000000..b503c00fc9 --- /dev/null +++ b/gst/encoding/gststreamsplitter.h @@ -0,0 +1,62 @@ +/* GStreamer Stream Splitter + * Copyright (C) 2010 Edward Hervey + * (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 + +#define GST_TYPE_STREAM_SPLITTER (gst_stream_splitter_get_type()) +#define GST_STREAM_SPLITTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STREAM_SPLITTER,GstStreamSplitter)) +#define GST_STREAM_SPLITTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STREAM_SPLITTER,GstStreamSplitterClass)) +#define GST_IS_STREAM_SPLITTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STREAM_SPLITTER)) +#define GST_IS_STREAM_SPLITTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STREAM_SPLITTER)) + +typedef struct _GstStreamSplitter GstStreamSplitter; +typedef struct _GstStreamSplitterClass GstStreamSplitterClass; + +struct _GstStreamSplitter { + GstElement parent; + + GstPad *sinkpad; + + /* lock protects: + * * the current pad + * * the list of srcpads + */ + GMutex *lock; + /* Currently activated srcpad */ + GstPad *current; + GList *srcpads; + guint32 cookie; + + /* List of pending in-band events */ + GList *pending_events; +}; + +struct _GstStreamSplitterClass { + GstElementClass parent; +}; + +GType gst_stream_splitter_get_type(void); + +GstElement *gst_stream_splitter_new (gchar *name); + +#endif /* __GST_STREAMSPLITTER_H__ */ diff --git a/gst/gdp/gstgdppay.c b/gst/gdp/gstgdppay.c index ea8595b283..6da4967f7a 100644 --- a/gst/gdp/gstgdppay.c +++ b/gst/gdp/gstgdppay.c @@ -474,7 +474,10 @@ gst_gdp_pay_reset_streamheader (GstGDPPay * this) GST_DEBUG_OBJECT (this, "Setting caps on src pad %" GST_PTR_FORMAT, caps); gst_pad_set_caps (this->srcpad, caps); + this->caps_buf = gst_buffer_make_metadata_writable (this->caps_buf); gst_buffer_set_caps (this->caps_buf, caps); + this->new_segment_buf = + gst_buffer_make_metadata_writable (this->new_segment_buf); gst_buffer_set_caps (this->new_segment_buf, caps); /* if these are our first ever buffers, send out new_segment first */ diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index 01b3d206dc..6e7130e4ce 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -17,7 +17,6 @@ libgstplaybin_la_SOURCES = \ gstplaysink.c \ gstplaybasebin.c \ gstplay-enum.c \ - gstinputselector.c \ gststreaminfo.c \ gststreamselector.c \ gstsubtitleoverlay.c \ @@ -56,7 +55,6 @@ noinst_HEADERS = \ gstplaybasebin.h \ gstplaysink.h \ gststreaminfo.h \ - gstinputselector.h \ gstplay-enum.h \ gststreamselector.h \ gstrawcaps.h \ diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c index 8c27476bae..f9bdf23568 100644 --- a/gst/playback/gstdecodebin2.c +++ b/gst/playback/gstdecodebin2.c @@ -590,12 +590,15 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::new-decoded-pad: - * @bin: The decodebin - * @pad: The newly created pad + * @bin: The decodebin. + * @pad: The newly created pad. * @islast: #TRUE if this is the last pad to be added. Deprecated. * * This signal gets emitted as soon as a new pad of the same type as one of * the valid 'raw' types is added. + * + * Deprecated: Use GstElement::pad-added instead of this signal. + * */ gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] = g_signal_new ("new-decoded-pad", G_TYPE_FROM_CLASS (klass), @@ -606,10 +609,13 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::removed-decoded-pad: - * @bin: The decodebin - * @pad: The pad that was removed + * @bin: The decodebin. + * @pad: The pad that was removed. * * This signal is emitted when a 'final' caps pad has been removed. + * + * Deprecated: Use GstElement::pad-removed instead of this signal. + * */ gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD] = g_signal_new ("removed-decoded-pad", G_TYPE_FROM_CLASS (klass), @@ -619,7 +625,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::unknown-type: - * @bin: The decodebin + * @bin: The decodebin. * @pad: The new pad containing caps that cannot be resolved to a 'final' * stream type. * @caps: The #GstCaps of the pad that cannot be resolved. @@ -635,13 +641,19 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::autoplug-continue: - * @bin: The decodebin + * @bin: The decodebin. * @pad: The #GstPad. * @caps: The #GstCaps found. * * This signal is emitted whenever decodebin2 finds a new stream. It is * emitted before looking for any elements that can handle that stream. * + * + * Invocation of signal handlers stops after the first signal handler + * returns #FALSE. Signal handlers are invoked in the order they were + * connected in. + * + * * Returns: #TRUE if you wish decodebin2 to look for elements that can * handle the given @caps. If #FALSE, those caps will be considered as * final and the pad will be exposed as such (see 'new-decoded-pad' @@ -655,18 +667,24 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::autoplug-factories: - * @bin: The decodebin + * @bin: The decodebin. * @pad: The #GstPad. * @caps: The #GstCaps found. * * This function is emited when an array of possible factories for @caps on * @pad is needed. Decodebin2 will by default return an array with all - * compatible factories, sorted by rank. + * compatible factories, sorted by rank. * * If this function returns NULL, @pad will be exposed as a final caps. * - * If this function returns an empty array, the pad will be considered as - * having an unhandled type media type. + * If this function returns an empty array, the pad will be considered as + * having an unhandled type media type. + * + * + * Only the signal handler that is connected first will ever by invoked. + * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this + * signal, they will never be invoked! + * * * Returns: a #GValueArray* with a list of factories to try. The factories are * by default tried in the returned order or based on the index returned by @@ -681,7 +699,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::autoplug-sort: - * @bin: The decodebin + * @bin: The decodebin. * @pad: The #GstPad. * @caps: The #GstCaps. * @factories: A #GValueArray of possible #GstElementFactory to use. @@ -691,7 +709,16 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) * the application to perform additional sorting or filtering on the element * factory array. * - * The callee should copy and modify @factories. + * The callee should copy and modify @factories or return #NULL if the + * order should not change. + * + * + * Invocation of signal handlers stops after one signal handler has + * returned something else than #NULL. Signal handlers are invoked in + * the order they were connected in. + * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this + * signal, they will never be invoked! + * * * Returns: A new sorted array of #GstElementFactory objects. */ @@ -704,10 +731,10 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) /** * GstDecodeBin2::autoplug-select: - * @bin: The decodebin + * @bin: The decodebin. * @pad: The #GstPad. * @caps: The #GstCaps. - * @factory: A #GstElementFactory to use + * @factory: A #GstElementFactory to use. * * This signal is emitted once decodebin2 has found all the possible * #GstElementFactory that can be used to handle the given @caps. For each of @@ -725,6 +752,12 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the * next factory. * + * + * Only the signal handler that is connected first will ever by invoked. + * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this + * signal, they will never be invoked! + * + * * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required * operation. the default handler will always return * #GST_AUTOPLUG_SELECT_TRY. @@ -861,7 +894,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) * If set to %FALSE, then only the streams that can be decoded to the final * caps (see 'caps' property) will have a pad exposed. Streams that do not * match those caps but could have been decoded will not have decoder plugged - * in internally and will not have a pad exposed. + * in internally and will not have a pad exposed. * * Since: 0.10.30 */ @@ -1275,14 +1308,7 @@ static GValueArray * gst_decode_bin_autoplug_sort (GstElement * element, GstPad * pad, GstCaps * caps, GValueArray * factories) { - GValueArray *result; - - result = g_value_array_copy (factories); - - GST_DEBUG_OBJECT (element, "autoplug-sort returns %p", result); - - /* return input */ - return result; + return NULL; } static GstAutoplugSelectResult @@ -1356,6 +1382,8 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, GstDecodeGroup *group; GstDecodeChain *oldchain = chain; + /* we are adding a new pad for a demuxer (see is_demuxer_element(), + * start a new chain for it */ CHAIN_MUTEX_LOCK (oldchain); group = gst_decode_chain_get_current_group (chain); if (group) { @@ -1426,21 +1454,22 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_AUTOPLUG_SORT], 0, dpad, caps, factories, &result); - g_value_array_free (factories); - factories = result; + if (result) { + g_value_array_free (factories); + factories = result; + } /* At this point we have a potential decoder, but we might not need it * if it doesn't match the output caps */ if (!dbin->expose_allstreams) { guint i; - GstCaps *rawcaps = gst_static_caps_get (&default_raw_caps); const GList *tmps; gboolean dontuse = FALSE; GST_DEBUG ("Checking if we can abort early"); /* 1.e Do an early check to see if the candidates are potential decoders, but - * due to the fact that they decode to a mediatype that is not final we don't + * due to the fact that they decode to a mediatype that is not final we don't * need them */ for (i = 0; i < factories->n_values && !dontuse; i++) { @@ -1462,13 +1491,21 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, if (st->direction != GST_PAD_SRC) continue; tcaps = gst_static_pad_template_get_caps (st); - if (!gst_caps_can_intersect (tcaps, dbin->caps)) + + apcontinue = TRUE; + + /* Emit autoplug-continue to see if the caps are considered to be raw caps */ + g_signal_emit (G_OBJECT (dbin), + gst_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE], 0, dpad, tcaps, + &apcontinue); + + /* If autoplug-continue returns TRUE and the caps are not final, don't use them */ + if (apcontinue && !are_final_caps (dbin, tcaps)) dontuse = TRUE; gst_caps_unref (tcaps); } } } - gst_caps_unref (rawcaps); if (dontuse) { gst_object_unref (dpad); @@ -1641,6 +1678,37 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad, /* Remove selected factory from the list. */ g_value_array_remove (factories, 0); + /* If the factory is for a parser we first check if the factory + * was already used for the current chain. If it was used already + * we would otherwise create an infinite loop here because the + * parser apparently accepts its own output as input. + * This is only done for parsers because it's perfectly valid + * to have other element classes after each other because a + * parser is the only one that does not change the data. A + * valid example for this would be multiple id3demux in a row. + */ + if (strstr (gst_element_factory_get_klass (factory), "Parser")) { + gboolean skip = FALSE; + GList *l; + + CHAIN_MUTEX_LOCK (chain); + for (l = chain->elements; l; l = l->next) { + GstElement *otherelement = GST_ELEMENT_CAST (l->data); + + if (gst_element_get_factory (otherelement) == factory) { + skip = TRUE; + break; + } + } + CHAIN_MUTEX_UNLOCK (chain); + if (skip) { + GST_DEBUG_OBJECT (dbin, + "Skipping factory '%s' because it was already used in this chain", + gst_plugin_feature_get_name (GST_PLUGIN_FEATURE_CAST (factory))); + continue; + } + } + /* emit autoplug-select to see what we should do with it. */ g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_AUTOPLUG_SELECT], @@ -1750,9 +1818,41 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad, * other thread could've added elements in the meantime */ CHAIN_MUTEX_LOCK (chain); do { + GList *l; + tmp = chain->elements->data; - gst_element_set_state (tmp, GST_STATE_NULL); + + /* Disconnect any signal handlers that might be connected + * in connect_element() or analyze_pad() */ + g_signal_handlers_disconnect_by_func (tmp, pad_added_cb, chain); + g_signal_handlers_disconnect_by_func (tmp, pad_removed_cb, chain); + g_signal_handlers_disconnect_by_func (tmp, no_more_pads_cb, chain); + + for (l = chain->pending_pads; l;) { + GstPendingPad *pp = l->data; + GList *n; + + if (GST_PAD_PARENT (pp->pad) != tmp) { + l = l->next; + continue; + } + + g_signal_handlers_disconnect_by_func (pp->pad, caps_notify_cb, chain); + gst_pad_remove_event_probe (pp->pad, pp->event_probe_id); + gst_object_unref (pp->pad); + g_slice_free (GstPendingPad, pp); + + /* Remove element from the list, update list head and go to the + * next element in the list */ + n = l->next; + chain->pending_pads = g_list_delete_link (chain->pending_pads, l); + l = n; + } + gst_bin_remove (GST_BIN (dbin), tmp); + gst_element_set_state (tmp, GST_STATE_NULL); + + gst_object_unref (tmp); chain->elements = g_list_delete_link (chain->elements, chain->elements); } while (tmp != element); CHAIN_MUTEX_UNLOCK (chain); @@ -2144,7 +2244,7 @@ caps_notify_cb (GstPad * pad, GParamSpec * unused, GstDecodeChain * chain) gst_object_unref (element); } -/* Decide whether an element is a demuxer based on the +/* Decide whether an element is a demuxer based on the * klass and number/type of src pad templates it has */ static gboolean is_demuxer_element (GstElement * srcelement) @@ -2196,7 +2296,7 @@ is_demuxer_element (GstElement * srcelement) /* Returns TRUE if the caps are compatible with the caps specified in the 'caps' * property (which by default are the raw caps) - * + * * The decodebin_lock should be taken ! */ static gboolean @@ -2346,9 +2446,12 @@ gst_decode_chain_free_internal (GstDecodeChain * chain, gboolean hide) } if (chain->endpad) { - if (chain->endpad->exposed) + if (chain->endpad->exposed) { gst_element_remove_pad (GST_ELEMENT_CAST (chain->dbin), GST_PAD_CAST (chain->endpad)); + g_signal_emit (G_OBJECT (chain->dbin), + gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD], 0, chain->endpad); + } gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->endpad), NULL); chain->endpad->exposed = FALSE; @@ -2422,7 +2525,7 @@ gst_decode_chain_new (GstDecodeBin * dbin, GstDecodeGroup * parent, /* The overrun callback is used to expose groups that have not yet had their * no_more_pads called while the (large) multiqueue overflowed. When this * happens we must assume that the no_more_pads will not arrive anymore and we - * must expose the pads that we have. + * must expose the pads that we have. */ static void multi_queue_overrun_cb (GstElement * queue, GstDecodeGroup * group) @@ -2527,7 +2630,7 @@ gst_decode_group_free (GstDecodeGroup * group) * unrefed here. * * Can be called from streaming threads. - * + * * Not MT-safe, call with parent's chain lock! */ static void @@ -2751,7 +2854,7 @@ out: return complete; } -/* check if the group is drained, meaning all pads have seen an EOS +/* check if the group is drained, meaning all pads have seen an EOS * event. */ static void gst_decode_pad_handle_eos (GstDecodePad * pad) @@ -2764,7 +2867,7 @@ gst_decode_pad_handle_eos (GstDecodePad * pad) } /* gst_decode_chain_handle_eos: - * + * * Checks if there are next groups in any parent chain * to which we can switch or if everything is drained. * @@ -3053,13 +3156,15 @@ gst_decode_chain_get_topology (GstDecodeChain * chain) for (; l && l->next; l = l->next) { GstCaps *caps = _gst_element_get_linked_caps (l->next->data, l->data); - s = gst_structure_id_empty_new (topology_structure_name); - gst_structure_id_set (u, topology_caps, GST_TYPE_CAPS, caps, NULL); - gst_caps_unref (caps); + if (caps) { + s = gst_structure_id_empty_new (topology_structure_name); + gst_structure_id_set (u, topology_caps, GST_TYPE_CAPS, caps, NULL); + gst_caps_unref (caps); - gst_structure_id_set (s, topology_next, GST_TYPE_STRUCTURE, u, NULL); - gst_structure_free (u); - u = s; + gst_structure_id_set (s, topology_next, GST_TYPE_STRUCTURE, u, NULL); + gst_structure_free (u); + u = s; + } } /* Caps that resulted in this chain */ diff --git a/gst/playback/gstinputselector.c b/gst/playback/gstinputselector.c deleted file mode 100644 index d561d95731..0000000000 --- a/gst/playback/gstinputselector.c +++ /dev/null @@ -1,1459 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Julien Moutte - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2005 Jan Schmidt - * Copyright (C) 2007 Wim Taymans - * Copyright (C) 2007 Andy Wingo - * Copyright (C) 2008 Nokia Corporation. (contact ) - * - * 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. - */ - -/** - * SECTION:element-input-selector - * @see_also: #GstOutputSelector - * - * Direct one out of N input streams to the output pad. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "gstinputselector.h" -#include "gstplay-marshal.h" - -GST_DEBUG_CATEGORY_STATIC (input_selector_debug); -#define GST_CAT_DEFAULT input_selector_debug - -static GstStaticPadTemplate gst_input_selector_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate gst_input_selector_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -enum -{ - PROP_0, - PROP_N_PADS, - PROP_ACTIVE_PAD, - PROP_SELECT_ALL, - PROP_LAST -}; - -#define DEFAULT_PAD_ALWAYS_OK TRUE - -enum -{ - PROP_PAD_0, - PROP_PAD_RUNNING_TIME, - PROP_PAD_TAGS, - PROP_PAD_ACTIVE, - PROP_PAD_ALWAYS_OK, - PROP_PAD_LAST -}; - -enum -{ - /* methods */ - SIGNAL_BLOCK, - SIGNAL_SWITCH, - LAST_SIGNAL -}; -static guint gst_input_selector_signals[LAST_SIGNAL] = { 0 }; - -static inline gboolean gst_input_selector_is_active_sinkpad (GstInputSelector * - sel, GstPad * pad); -static GstPad *gst_input_selector_activate_sinkpad (GstInputSelector * sel, - GstPad * pad); -static GstPad *gst_input_selector_get_linked_pad (GstPad * pad, - gboolean strict); -static gboolean gst_input_selector_check_eos (GstElement * selector); - -#define GST_TYPE_SELECTOR_PAD \ - (gst_selector_pad_get_type()) -#define GST_SELECTOR_PAD(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SELECTOR_PAD, GstSelectorPad)) -#define GST_SELECTOR_PAD_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SELECTOR_PAD, GstSelectorPadClass)) -#define GST_IS_SELECTOR_PAD(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SELECTOR_PAD)) -#define GST_IS_SELECTOR_PAD_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SELECTOR_PAD)) -#define GST_SELECTOR_PAD_CAST(obj) \ - ((GstSelectorPad *)(obj)) - -typedef struct _GstSelectorPad GstSelectorPad; -typedef struct _GstSelectorPadClass GstSelectorPadClass; - -struct _GstSelectorPad -{ - GstPad parent; - - gboolean active; /* when buffer have passed the pad */ - gboolean eos; /* when EOS has been received */ - gboolean discont; /* after switching we create a discont */ - gboolean always_ok; - GstSegment segment; /* the current segment on the pad */ - GstTagList *tags; /* last tags received on the pad */ - - gboolean segment_pending; -}; - -struct _GstSelectorPadClass -{ - GstPadClass parent; -}; - -static void gst_selector_pad_class_init (GstSelectorPadClass * klass); -static void gst_selector_pad_init (GstSelectorPad * pad); -static void gst_selector_pad_finalize (GObject * object); -static void gst_selector_pad_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_selector_pad_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); - -static GstPadClass *selector_pad_parent_class = NULL; - -static gint64 gst_selector_pad_get_running_time (GstSelectorPad * pad); -static void gst_selector_pad_reset (GstSelectorPad * pad); -static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event); -static GstCaps *gst_selector_pad_getcaps (GstPad * pad); -static gboolean gst_selector_pad_acceptcaps (GstPad * pad, GstCaps * caps); -static GstIterator *gst_selector_pad_iterate_linked_pads (GstPad * pad); -static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf); -static GstFlowReturn gst_selector_pad_bufferalloc (GstPad * pad, - guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); - -static GType -gst_selector_pad_get_type (void) -{ - static GType selector_pad_type = 0; - - if (!selector_pad_type) { - static const GTypeInfo selector_pad_info = { - sizeof (GstSelectorPadClass), - NULL, - NULL, - (GClassInitFunc) gst_selector_pad_class_init, - NULL, - NULL, - sizeof (GstSelectorPad), - 0, - (GInstanceInitFunc) gst_selector_pad_init, - }; - - selector_pad_type = - g_type_register_static (GST_TYPE_PAD, "GstPlaybin2SelectorPad", - &selector_pad_info, 0); - } - return selector_pad_type; -} - -static void -gst_selector_pad_class_init (GstSelectorPadClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - - selector_pad_parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_selector_pad_finalize; - - gobject_class->get_property = gst_selector_pad_get_property; - gobject_class->set_property = gst_selector_pad_set_property; - - g_object_class_install_property (gobject_class, PROP_PAD_RUNNING_TIME, - g_param_spec_int64 ("running-time", "Running time", - "Running time of stream on pad", 0, G_MAXINT64, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_PAD_TAGS, - g_param_spec_boxed ("tags", "Tags", - "The currently active tags on the pad", GST_TYPE_TAG_LIST, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE, - g_param_spec_boolean ("active", "Active", - "If the pad is currently active", FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_PAD_ALWAYS_OK, - g_param_spec_boolean ("always-ok", "Always OK", - "Make an inactive pad return OK instead of NOT_LINKED", - DEFAULT_PAD_ALWAYS_OK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_selector_pad_init (GstSelectorPad * pad) -{ - pad->always_ok = DEFAULT_PAD_ALWAYS_OK; - gst_selector_pad_reset (pad); -} - -static void -gst_selector_pad_finalize (GObject * object) -{ - GstSelectorPad *pad; - - pad = GST_SELECTOR_PAD_CAST (object); - - if (pad->tags) - gst_tag_list_free (pad->tags); - - G_OBJECT_CLASS (selector_pad_parent_class)->finalize (object); -} - -static void -gst_selector_pad_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstSelectorPad *spad = GST_SELECTOR_PAD_CAST (object); - - switch (prop_id) { - case PROP_PAD_ALWAYS_OK: - GST_OBJECT_LOCK (object); - spad->always_ok = g_value_get_boolean (value); - GST_OBJECT_UNLOCK (object); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_selector_pad_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstSelectorPad *spad = GST_SELECTOR_PAD_CAST (object); - - switch (prop_id) { - case PROP_PAD_RUNNING_TIME: - g_value_set_int64 (value, gst_selector_pad_get_running_time (spad)); - break; - case PROP_PAD_TAGS: - GST_OBJECT_LOCK (object); - g_value_set_boxed (value, spad->tags); - GST_OBJECT_UNLOCK (object); - break; - case PROP_PAD_ACTIVE: - { - GstInputSelector *sel; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (spad)); - g_value_set_boolean (value, gst_input_selector_is_active_sinkpad (sel, - GST_PAD_CAST (spad))); - gst_object_unref (sel); - break; - } - case PROP_PAD_ALWAYS_OK: - GST_OBJECT_LOCK (object); - g_value_set_boolean (value, spad->always_ok); - GST_OBJECT_UNLOCK (object); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gint64 -gst_selector_pad_get_running_time (GstSelectorPad * pad) -{ - gint64 ret = 0; - - GST_OBJECT_LOCK (pad); - if (pad->active) { - gint64 last_stop = pad->segment.last_stop; - - if (last_stop >= 0) - ret = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, - last_stop); - } - GST_OBJECT_UNLOCK (pad); - - GST_DEBUG_OBJECT (pad, "running time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (ret)); - - return ret; -} - -static void -gst_selector_pad_reset (GstSelectorPad * pad) -{ - GST_OBJECT_LOCK (pad); - pad->active = FALSE; - pad->eos = FALSE; - pad->segment_pending = FALSE; - pad->discont = FALSE; - gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED); - GST_OBJECT_UNLOCK (pad); -} - -/* strictly get the linked pad from the sinkpad. If the pad is active we return - * the srcpad else we return NULL */ -static GstIterator * -gst_selector_pad_iterate_linked_pads (GstPad * pad) -{ - GstInputSelector *sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - GstPad *otherpad; - GstIterator *it; - - otherpad = gst_input_selector_get_linked_pad (pad, TRUE); - it = gst_iterator_new_single (GST_TYPE_PAD, otherpad, - (GstCopyFunction) gst_object_ref, (GFreeFunc) gst_object_unref); - - if (otherpad) - gst_object_unref (otherpad); - gst_object_unref (sel); - - return it; -} - -static gboolean -gst_selector_pad_event (GstPad * pad, GstEvent * event) -{ - gboolean res = TRUE; - gboolean forward = TRUE; - GstInputSelector *sel; - GstSelectorPad *selpad; - GstPad *prev_active_sinkpad; - GstPad *active_sinkpad; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - selpad = GST_SELECTOR_PAD_CAST (pad); - - GST_INPUT_SELECTOR_LOCK (sel); - prev_active_sinkpad = sel->active_sinkpad; - active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); - - /* only forward if we are dealing with the active sinkpad or if select_all - * is enabled */ - if (pad != active_sinkpad && !sel->select_all) - forward = FALSE; - GST_INPUT_SELECTOR_UNLOCK (sel); - - if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) - g_object_notify (G_OBJECT (sel), "active-pad"); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - /* FIXME, flush out the waiter */ - break; - case GST_EVENT_FLUSH_STOP: - GST_INPUT_SELECTOR_LOCK (sel); - gst_selector_pad_reset (selpad); - sel->pending_close = FALSE; - GST_INPUT_SELECTOR_UNLOCK (sel); - break; - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - GST_DEBUG_OBJECT (pad, - "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " - "format %d, " - "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" - G_GINT64_FORMAT, update, rate, arate, format, start, stop, time); - - GST_INPUT_SELECTOR_LOCK (sel); - GST_OBJECT_LOCK (selpad); - gst_segment_set_newsegment_full (&selpad->segment, update, - rate, arate, format, start, stop, time); - GST_OBJECT_UNLOCK (selpad); - - /* If we aren't forwarding the event (because the pad is not the - * active_sinkpad, and select_all is not set, then set the flag on the - * that says a segment needs sending if/when that pad is activated. - * For all other cases, we send the event immediately, which makes - * sparse streams and other segment updates work correctly downstream. - */ - if (!forward) - selpad->segment_pending = TRUE; - - GST_INPUT_SELECTOR_UNLOCK (sel); - break; - } - case GST_EVENT_TAG: - { - GstTagList *tags, *oldtags, *newtags; - - gst_event_parse_tag (event, &tags); - - GST_OBJECT_LOCK (selpad); - oldtags = selpad->tags; - - newtags = gst_tag_list_merge (oldtags, tags, GST_TAG_MERGE_REPLACE); - selpad->tags = newtags; - if (oldtags) - gst_tag_list_free (oldtags); - GST_DEBUG_OBJECT (pad, "received tags %" GST_PTR_FORMAT, newtags); - GST_OBJECT_UNLOCK (selpad); - - g_object_notify (G_OBJECT (selpad), "tags"); - break; - } - case GST_EVENT_EOS: - selpad->eos = TRUE; - GST_DEBUG_OBJECT (pad, "received EOS"); - /* don't forward eos in select_all mode until all sink pads have eos */ - if (sel->select_all && !gst_input_selector_check_eos (GST_ELEMENT (sel))) { - forward = FALSE; - } - break; - default: - break; - } - if (forward) { - GST_DEBUG_OBJECT (pad, "forwarding event"); - res = gst_pad_push_event (sel->srcpad, event); - } else - gst_event_unref (event); - - gst_object_unref (sel); - - return res; -} - -static GstCaps * -gst_selector_pad_getcaps (GstPad * pad) -{ - GstInputSelector *sel; - GstCaps *caps; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer"); - caps = gst_pad_peer_get_caps_reffed (sel->srcpad); - if (caps == NULL) - caps = gst_caps_new_any (); - - gst_object_unref (sel); - - return caps; -} - -static gboolean -gst_selector_pad_acceptcaps (GstPad * pad, GstCaps * caps) -{ - GstInputSelector *sel; - gboolean res; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (sel, "Checking acceptcaps of srcpad peer"); - res = gst_pad_peer_accept_caps (sel->srcpad, caps); - gst_object_unref (sel); - - return res; -} - -static GstFlowReturn -gst_selector_pad_bufferalloc (GstPad * pad, guint64 offset, - guint size, GstCaps * caps, GstBuffer ** buf) -{ - GstInputSelector *sel; - GstFlowReturn result; - GstPad *active_sinkpad; - GstPad *prev_active_sinkpad; - GstSelectorPad *selpad; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - selpad = GST_SELECTOR_PAD_CAST (pad); - - GST_LOG_OBJECT (pad, "received alloc"); - - GST_INPUT_SELECTOR_LOCK (sel); - prev_active_sinkpad = sel->active_sinkpad; - active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); - - if (pad != active_sinkpad) - goto not_active; - - GST_INPUT_SELECTOR_UNLOCK (sel); - - if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) - g_object_notify (G_OBJECT (sel), "active-pad"); - - result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf); - -done: - gst_object_unref (sel); - - return result; - - /* ERRORS */ -not_active: - { - GST_INPUT_SELECTOR_UNLOCK (sel); - - /* unselected pad, perform fallback alloc or return unlinked when - * asked */ - GST_OBJECT_LOCK (selpad); - if (selpad->always_ok) { - GST_DEBUG_OBJECT (pad, "Not selected, performing fallback allocation"); - *buf = NULL; - result = GST_FLOW_OK; - } else { - GST_DEBUG_OBJECT (pad, "Not selected, return NOT_LINKED"); - result = GST_FLOW_NOT_LINKED; - } - GST_OBJECT_UNLOCK (selpad); - - goto done; - } -} - -/* must be called with the SELECTOR_LOCK, will block while the pad is blocked - * or return TRUE when flushing */ -static gboolean -gst_input_selector_wait (GstInputSelector * self, GstPad * pad) -{ - while (self->blocked && !self->flushing) { - /* we can be unlocked here when we are shutting down (flushing) or when we - * get unblocked */ - GST_INPUT_SELECTOR_WAIT (self); - } - return self->flushing; -} - -static GstFlowReturn -gst_selector_pad_chain (GstPad * pad, GstBuffer * buf) -{ - GstInputSelector *sel; - GstFlowReturn res; - GstPad *active_sinkpad; - GstPad *prev_active_sinkpad; - GstSelectorPad *selpad; - GstClockTime start_time; - GstSegment *seg; - GstEvent *close_event = NULL, *start_event = NULL; - GstCaps *caps; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - selpad = GST_SELECTOR_PAD_CAST (pad); - seg = &selpad->segment; - - GST_INPUT_SELECTOR_LOCK (sel); - /* wait or check for flushing */ - if (gst_input_selector_wait (sel, pad)) - goto flushing; - - GST_LOG_OBJECT (pad, "getting active pad"); - - prev_active_sinkpad = sel->active_sinkpad; - active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); - - /* update the segment on the srcpad */ - start_time = GST_BUFFER_TIMESTAMP (buf); - if (GST_CLOCK_TIME_IS_VALID (start_time)) { - GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT, - GST_TIME_ARGS (start_time)); - if (GST_BUFFER_DURATION_IS_VALID (buf)) - GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT, - GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf))); - - GST_OBJECT_LOCK (pad); - gst_segment_set_last_stop (seg, seg->format, start_time); - GST_OBJECT_UNLOCK (pad); - } - - /* Ignore buffers from pads except the selected one */ - if (pad != active_sinkpad) - goto ignore; - - if (G_UNLIKELY (sel->pending_close)) { - GstSegment *cseg = &sel->segment; - - GST_DEBUG_OBJECT (sel, - "pushing close NEWSEGMENT update %d, rate %lf, applied rate %lf, " - "format %d, " - "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" - G_GINT64_FORMAT, TRUE, cseg->rate, cseg->applied_rate, cseg->format, - cseg->start, cseg->stop, cseg->time); - - /* create update segment */ - close_event = gst_event_new_new_segment_full (TRUE, cseg->rate, - cseg->applied_rate, cseg->format, cseg->start, cseg->stop, cseg->time); - - sel->pending_close = FALSE; - } - /* if we have a pending segment, push it out now */ - if (G_UNLIKELY (selpad->segment_pending)) { - GST_DEBUG_OBJECT (pad, - "pushing pending NEWSEGMENT update %d, rate %lf, applied rate %lf, " - "format %d, " - "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" - G_GINT64_FORMAT, FALSE, seg->rate, seg->applied_rate, seg->format, - seg->start, seg->stop, seg->time); - - start_event = gst_event_new_new_segment_full (FALSE, seg->rate, - seg->applied_rate, seg->format, seg->start, seg->stop, seg->time); - - selpad->segment_pending = FALSE; - } - GST_INPUT_SELECTOR_UNLOCK (sel); - - if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) - g_object_notify (G_OBJECT (sel), "active-pad"); - - if (close_event) - gst_pad_push_event (sel->srcpad, close_event); - - if (start_event) - gst_pad_push_event (sel->srcpad, start_event); - - if (selpad->discont) { - buf = gst_buffer_make_metadata_writable (buf); - - GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - selpad->discont = FALSE; - } - - /* forward */ - GST_LOG_OBJECT (pad, "Forwarding buffer %p", buf); - - if ((caps = GST_BUFFER_CAPS (buf))) { - if (GST_PAD_CAPS (sel->srcpad) != caps) - gst_pad_set_caps (sel->srcpad, caps); - } - - res = gst_pad_push (sel->srcpad, buf); - -done: - gst_object_unref (sel); - return res; - - /* dropped buffers */ -ignore: - { - GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf); - /* when we drop a buffer, we're creating a discont on this pad */ - selpad->discont = TRUE; - GST_INPUT_SELECTOR_UNLOCK (sel); - gst_buffer_unref (buf); - - /* figure out what to return upstream */ - GST_OBJECT_LOCK (selpad); - if (selpad->always_ok) - res = GST_FLOW_OK; - else - res = GST_FLOW_NOT_LINKED; - GST_OBJECT_UNLOCK (selpad); - - goto done; - } -flushing: - { - GST_DEBUG_OBJECT (pad, "We are flushing, discard buffer %p", buf); - GST_INPUT_SELECTOR_UNLOCK (sel); - gst_buffer_unref (buf); - res = GST_FLOW_WRONG_STATE; - goto done; - } -} - -static void gst_input_selector_init (GstInputSelector * sel); -static void gst_input_selector_base_init (GstInputSelectorClass * klass); -static void gst_input_selector_class_init (GstInputSelectorClass * klass); - -static void gst_input_selector_dispose (GObject * object); - -static void gst_input_selector_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_input_selector_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static GstPad *gst_input_selector_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * unused); -static void gst_input_selector_release_pad (GstElement * element, GstPad * pad); - -static GstStateChangeReturn gst_input_selector_change_state (GstElement * - element, GstStateChange transition); - -static GstCaps *gst_input_selector_getcaps (GstPad * pad); -static gboolean gst_input_selector_event (GstPad * pad, GstEvent * event); -static gboolean gst_input_selector_query (GstPad * pad, GstQuery * query); -static gint64 gst_input_selector_block (GstInputSelector * self); -static void gst_input_selector_switch (GstInputSelector * self, - GstPad * pad, gint64 stop_time, gint64 start_time); - -static GstElementClass *parent_class = NULL; - -GType -gst_input_selector_get_type (void) -{ - static GType input_selector_type = 0; - - if (!input_selector_type) { - static const GTypeInfo input_selector_info = { - sizeof (GstInputSelectorClass), - (GBaseInitFunc) gst_input_selector_base_init, - NULL, - (GClassInitFunc) gst_input_selector_class_init, - NULL, - NULL, - sizeof (GstInputSelector), - 0, - (GInstanceInitFunc) gst_input_selector_init, - }; - input_selector_type = - g_type_register_static (GST_TYPE_ELEMENT, - "GstPlaybin2InputSelector", &input_selector_info, 0); - GST_DEBUG_CATEGORY_INIT (input_selector_debug, - "playbin2-input-selector", 0, "Playbin2 input stream selector element"); - } - - return input_selector_type; -} - -static void -gst_input_selector_base_init (GstInputSelectorClass * klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - gst_element_class_set_details_simple (element_class, - "Input selector", "Generic", - "N-to-1 input stream selectoring", - "Julien Moutte , " - "Jan Schmidt , " - "Wim Taymans "); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_input_selector_sink_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_input_selector_src_factory)); -} - -static void -gst_input_selector_class_init (GstInputSelectorClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - /* FIXME: remove after confirming it is safe now */ - g_type_class_ref (gst_selector_pad_get_type ()); - - gobject_class->dispose = gst_input_selector_dispose; - - gobject_class->set_property = gst_input_selector_set_property; - gobject_class->get_property = gst_input_selector_get_property; - - g_object_class_install_property (gobject_class, PROP_N_PADS, - g_param_spec_uint ("n-pads", "Number of Pads", - "The number of sink pads", 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD, - g_param_spec_object ("active-pad", "Active pad", - "The currently active sink pad", GST_TYPE_PAD, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_SELECT_ALL, - g_param_spec_boolean ("select-all", "Select all mode", - "Forwards data from all input pads", FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstInputSelector::block: - * @inputselector: the #GstInputSelector - * - * Block all sink pads in preparation for a switch. Returns the stop time of - * the current switch segment, as a running time, or 0 if there is no current - * active pad or the current active pad never received data. - */ - gst_input_selector_signals[SIGNAL_BLOCK] = - g_signal_new ("block", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (GstInputSelectorClass, block), NULL, NULL, - gst_play_marshal_INT64__VOID, G_TYPE_INT64, 0); - /** - * GstInputSelector::switch: - * @inputselector: the #GstInputSelector - * @pad: the pad to switch to - * @stop_time: running time at which to close the previous segment, or -1 - * to use the running time of the previously active sink pad - * @start_time: running time at which to start the new segment, or -1 to - * use the running time of the newly active sink pad - * - * Switch to a new feed. The segment opened by the previously active pad, if - * any, will be closed, and a new segment opened before data flows again. - * - * This signal must be emitted when the element has been blocked via the block signal. - * - * If you have a stream with only one switch element, such as an audio-only - * stream, a stream switch should be performed by first emitting the block - * signal, and then emitting the switch signal with -1 for the stop and start - * time values. - * - * The intention of the @stop_time and @start_time arguments is to allow - * multiple switch elements to switch and maintain stream synchronization. - * When switching a stream with multiple feeds, you will need as many switch - * elements as you have feeds. For example, a feed with audio and video will - * have one switch element between the audio feeds and one for video. - * - * A switch over multiple switch elements should be performed as follows: - * First, emit the block - * signal, collecting the returned values. The maximum running time returned - * by block should then be used as the time at which to close the previous - * segment. - * - * Then, query the running times of the new audio and video pads that you will - * switch to. Naturally, these pads are on separate switch elements. Take the - * minimum running time for those streams and use it for the time at which to - * open the new segment. - * - * If @pad is the same as the current active pad, the element will cancel any - * previous block without adjusting segments. - * - * - * the signal changed from accepting the pad name to the pad object. - * - * - * Since: 0.10.7 - */ - gst_input_selector_signals[SIGNAL_SWITCH] = - g_signal_new ("switch", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstInputSelectorClass, switch_), - NULL, NULL, gst_play_marshal_VOID__OBJECT_INT64_INT64, - G_TYPE_NONE, 3, GST_TYPE_PAD, G_TYPE_INT64, G_TYPE_INT64); - - gstelement_class->request_new_pad = gst_input_selector_request_new_pad; - gstelement_class->release_pad = gst_input_selector_release_pad; - gstelement_class->change_state = gst_input_selector_change_state; - - klass->block = GST_DEBUG_FUNCPTR (gst_input_selector_block); - /* note the underscore because switch is a keyword otherwise */ - klass->switch_ = GST_DEBUG_FUNCPTR (gst_input_selector_switch); -} - -static void -gst_input_selector_init (GstInputSelector * sel) -{ - sel->srcpad = gst_pad_new ("src", GST_PAD_SRC); - gst_pad_set_iterate_internal_links_function (sel->srcpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads)); - gst_pad_set_getcaps_function (sel->srcpad, - GST_DEBUG_FUNCPTR (gst_input_selector_getcaps)); - gst_pad_set_query_function (sel->srcpad, - GST_DEBUG_FUNCPTR (gst_input_selector_query)); - gst_pad_set_event_function (sel->srcpad, - GST_DEBUG_FUNCPTR (gst_input_selector_event)); - gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad); - /* sinkpad management */ - sel->active_sinkpad = NULL; - sel->padcount = 0; - gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED); - - sel->lock = g_mutex_new (); - sel->cond = g_cond_new (); - sel->blocked = FALSE; - - sel->select_all = FALSE; -} - -static void -gst_input_selector_dispose (GObject * object) -{ - GstInputSelector *sel = GST_INPUT_SELECTOR (object); - - if (sel->active_sinkpad) { - gst_object_unref (sel->active_sinkpad); - sel->active_sinkpad = NULL; - } - if (sel->lock) { - g_mutex_free (sel->lock); - sel->lock = NULL; - } - if (sel->cond) { - g_cond_free (sel->cond); - sel->cond = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -/* Solve the following equation for B.timestamp, and set that as the segment - * stop: - * B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum - */ -static gint64 -gst_segment_get_timestamp (GstSegment * segment, gint64 running_time) -{ - if (running_time <= segment->accum) - return segment->start; - else - return (running_time - segment->accum) * segment->abs_rate + segment->start; -} - -static void -gst_segment_set_stop (GstSegment * segment, gint64 running_time) -{ - segment->stop = gst_segment_get_timestamp (segment, running_time); - segment->last_stop = -1; -} - -static void -gst_segment_set_start (GstSegment * segment, gint64 running_time) -{ - gint64 new_start, duration; - - new_start = gst_segment_get_timestamp (segment, running_time); - - /* this is the duration we skipped */ - duration = new_start - segment->start; - /* add the duration to the accumulated segment time */ - segment->accum += duration; - /* move position in the segment */ - segment->time += duration; - segment->start += duration; -} - -/* this function must be called with the SELECTOR_LOCK. It returns TRUE when the - * active pad changed. */ -static gboolean -gst_input_selector_set_active_pad (GstInputSelector * self, - GstPad * pad, gint64 stop_time, gint64 start_time) -{ - GstSelectorPad *old, *new; - GstPad **active_pad_p; - - if (pad == self->active_sinkpad) - return FALSE; - - old = GST_SELECTOR_PAD_CAST (self->active_sinkpad); - new = GST_SELECTOR_PAD_CAST (pad); - - GST_DEBUG_OBJECT (self, "setting active pad to %s:%s", - GST_DEBUG_PAD_NAME (new)); - - if (!GST_CLOCK_TIME_IS_VALID (stop_time) && old) { - /* no stop time given, get the latest running_time on the active pad to - * close and open the new segment */ - stop_time = start_time = gst_selector_pad_get_running_time (old); - GST_DEBUG_OBJECT (self, "using start/stop of %" GST_TIME_FORMAT, - GST_TIME_ARGS (start_time)); - } - - if (old && old->active && !self->pending_close && stop_time >= 0) { - /* schedule a last_stop update if one isn't already scheduled, and a - segment has been pushed before. */ - memcpy (&self->segment, &old->segment, sizeof (self->segment)); - - GST_DEBUG_OBJECT (self, "setting stop_time to %" GST_TIME_FORMAT, - GST_TIME_ARGS (stop_time)); - gst_segment_set_stop (&self->segment, stop_time); - self->pending_close = TRUE; - } - - if (new && new->active && start_time >= 0) { - GST_DEBUG_OBJECT (self, "setting start_time to %" GST_TIME_FORMAT, - GST_TIME_ARGS (start_time)); - /* schedule a new segment push */ - gst_segment_set_start (&new->segment, start_time); - new->segment_pending = TRUE; - } - - active_pad_p = &self->active_sinkpad; - gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad)); - GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT, - self->active_sinkpad); - - return TRUE; -} - -static void -gst_input_selector_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstInputSelector *sel = GST_INPUT_SELECTOR (object); - - switch (prop_id) { - case PROP_ACTIVE_PAD: - { - GstPad *pad; - - pad = g_value_get_object (value); - - GST_INPUT_SELECTOR_LOCK (sel); - gst_input_selector_set_active_pad (sel, pad, - GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE); - GST_INPUT_SELECTOR_UNLOCK (sel); - break; - } - case PROP_SELECT_ALL: - GST_INPUT_SELECTOR_LOCK (object); - sel->select_all = g_value_get_boolean (value); - GST_INPUT_SELECTOR_UNLOCK (object); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_input_selector_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstInputSelector *sel = GST_INPUT_SELECTOR (object); - - switch (prop_id) { - case PROP_N_PADS: - GST_INPUT_SELECTOR_LOCK (object); - g_value_set_uint (value, sel->n_pads); - GST_INPUT_SELECTOR_UNLOCK (object); - break; - case PROP_ACTIVE_PAD: - GST_INPUT_SELECTOR_LOCK (object); - g_value_set_object (value, sel->active_sinkpad); - GST_INPUT_SELECTOR_UNLOCK (object); - break; - case PROP_SELECT_ALL: - GST_INPUT_SELECTOR_LOCK (object); - g_value_set_boolean (value, sel->select_all); - GST_INPUT_SELECTOR_UNLOCK (object); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstPad * -gst_input_selector_get_linked_pad (GstPad * pad, gboolean strict) -{ - GstInputSelector *sel; - GstPad *otherpad = NULL; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - - GST_INPUT_SELECTOR_LOCK (sel); - if (pad == sel->srcpad) - otherpad = sel->active_sinkpad; - else if (pad == sel->active_sinkpad || !strict) - otherpad = sel->srcpad; - if (otherpad) - gst_object_ref (otherpad); - GST_INPUT_SELECTOR_UNLOCK (sel); - - gst_object_unref (sel); - - return otherpad; -} - -static gboolean -gst_input_selector_event (GstPad * pad, GstEvent * event) -{ - gboolean res = FALSE; - GstPad *otherpad; - - otherpad = gst_input_selector_get_linked_pad (pad, TRUE); - - if (otherpad) { - res = gst_pad_push_event (otherpad, event); - - gst_object_unref (otherpad); - } else - gst_event_unref (event); - return res; -} - -/* query on the srcpad. We override this function because by default it will - * only forward the query to one random sinkpad */ -static gboolean -gst_input_selector_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - GstInputSelector *sel; - GstPad *otherpad; - - sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); - - otherpad = gst_input_selector_get_linked_pad (pad, TRUE); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - { - GList *walk; - GstClockTime resmin, resmax; - gboolean reslive; - - resmin = 0; - resmax = -1; - reslive = FALSE; - - /* assume FALSE, we become TRUE if one query succeeds */ - res = FALSE; - - /* perform the query on all sinkpads and combine the results. We take the - * max of min and the min of max for the result latency. */ - GST_INPUT_SELECTOR_LOCK (sel); - for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; - walk = g_list_next (walk)) { - GstPad *sinkpad = GST_PAD_CAST (walk->data); - - if (gst_pad_peer_query (sinkpad, query)) { - GstClockTime min, max; - gboolean live; - - /* one query succeeded, we succeed too */ - res = TRUE; - - gst_query_parse_latency (query, &live, &min, &max); - - GST_DEBUG_OBJECT (sinkpad, - "peer latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT - ", live %d", GST_TIME_ARGS (min), GST_TIME_ARGS (max), live); - - if (live) { - if (min > resmin) - resmin = min; - if (resmax == -1) - resmax = max; - else if (max < resmax) - resmax = max; - if (reslive == FALSE) - reslive = live; - } - } - } - GST_INPUT_SELECTOR_UNLOCK (sel); - if (res) { - gst_query_set_latency (query, reslive, resmin, resmax); - - GST_DEBUG_OBJECT (sel, - "total latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT - ", live %d", GST_TIME_ARGS (resmin), GST_TIME_ARGS (resmax), - reslive); - } - - break; - } - default: - if (otherpad) - res = gst_pad_peer_query (otherpad, query); - break; - } - if (otherpad) - gst_object_unref (otherpad); - gst_object_unref (sel); - - return res; -} - -static GstCaps * -gst_input_selector_getcaps (GstPad * pad) -{ - GstPad *otherpad; - GstObject *parent; - GstCaps *caps; - - parent = gst_object_get_parent (GST_OBJECT (pad)); - - otherpad = gst_input_selector_get_linked_pad (pad, FALSE); - - if (!otherpad) { - if (GST_INPUT_SELECTOR (parent)->select_all) { - GST_DEBUG_OBJECT (parent, - "Pad %s:%s not linked, returning merge of caps", - GST_DEBUG_PAD_NAME (pad)); - caps = gst_pad_proxy_getcaps (pad); - } else { - GST_DEBUG_OBJECT (parent, - "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad)); - caps = gst_caps_new_any (); - } - } else { - GST_DEBUG_OBJECT (parent, - "Pad %s:%s is linked (to %s:%s), returning peer caps", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad)); - /* if the peer has caps, use those. If the pad is not linked, this function - * returns NULL and we return ANY */ - if (!(caps = gst_pad_peer_get_caps_reffed (otherpad))) - caps = gst_caps_new_any (); - gst_object_unref (otherpad); - } - - gst_object_unref (parent); - return caps; -} - -/* check if the pad is the active sinkpad */ -static gboolean -gst_input_selector_is_active_sinkpad (GstInputSelector * sel, GstPad * pad) -{ - gboolean res; - - GST_INPUT_SELECTOR_LOCK (sel); - res = (pad == sel->active_sinkpad); - GST_INPUT_SELECTOR_UNLOCK (sel); - - return res; -} - -/* Get or create the active sinkpad, must be called with SELECTOR_LOCK */ -static GstPad * -gst_input_selector_activate_sinkpad (GstInputSelector * sel, GstPad * pad) -{ - GstPad *active_sinkpad; - GstSelectorPad *selpad; - - selpad = GST_SELECTOR_PAD_CAST (pad); - - selpad->active = TRUE; - active_sinkpad = sel->active_sinkpad; - if (active_sinkpad == NULL || sel->select_all) { - /* first pad we get activity on becomes the activated pad by default, if we - * select all, we also remember the last used pad. */ - if (sel->active_sinkpad) - gst_object_unref (sel->active_sinkpad); - active_sinkpad = sel->active_sinkpad = gst_object_ref (pad); - GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - } - - return active_sinkpad; -} - -static GstPad * -gst_input_selector_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * unused) -{ - GstInputSelector *sel; - gchar *name = NULL; - GstPad *sinkpad = NULL; - - g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL); - - sel = GST_INPUT_SELECTOR (element); - - GST_INPUT_SELECTOR_LOCK (sel); - - GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount); - name = g_strdup_printf ("sink%d", sel->padcount++); - sinkpad = g_object_new (GST_TYPE_SELECTOR_PAD, - "name", name, "direction", templ->direction, "template", templ, NULL); - g_free (name); - - sel->n_pads++; - - gst_pad_set_event_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_event)); - gst_pad_set_getcaps_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps)); - gst_pad_set_acceptcaps_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_acceptcaps)); - gst_pad_set_chain_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_chain)); - gst_pad_set_iterate_internal_links_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads)); - gst_pad_set_bufferalloc_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_bufferalloc)); - - gst_pad_set_active (sinkpad, TRUE); - gst_element_add_pad (GST_ELEMENT (sel), sinkpad); - GST_INPUT_SELECTOR_UNLOCK (sel); - - return sinkpad; -} - -static void -gst_input_selector_release_pad (GstElement * element, GstPad * pad) -{ - GstInputSelector *sel; - - sel = GST_INPUT_SELECTOR (element); - GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - GST_INPUT_SELECTOR_LOCK (sel); - /* if the pad was the active pad, makes us select a new one */ - if (sel->active_sinkpad == pad) { - GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - gst_object_unref (sel->active_sinkpad); - sel->active_sinkpad = NULL; - } - sel->n_pads--; - - gst_pad_set_active (pad, FALSE); - gst_element_remove_pad (GST_ELEMENT (sel), pad); - GST_INPUT_SELECTOR_UNLOCK (sel); -} - -static void -gst_input_selector_reset (GstInputSelector * sel) -{ - GList *walk; - - GST_INPUT_SELECTOR_LOCK (sel); - /* clear active pad */ - if (sel->active_sinkpad) { - gst_object_unref (sel->active_sinkpad); - sel->active_sinkpad = NULL; - } - /* reset segment */ - gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED); - sel->pending_close = FALSE; - /* reset each of our sinkpads state */ - for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) { - GstSelectorPad *selpad = GST_SELECTOR_PAD_CAST (walk->data); - - gst_selector_pad_reset (selpad); - - if (selpad->tags) { - gst_tag_list_free (selpad->tags); - selpad->tags = NULL; - } - } - GST_INPUT_SELECTOR_UNLOCK (sel); -} - -static GstStateChangeReturn -gst_input_selector_change_state (GstElement * element, - GstStateChange transition) -{ - GstInputSelector *self = GST_INPUT_SELECTOR (element); - GstStateChangeReturn result; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_INPUT_SELECTOR_LOCK (self); - self->blocked = FALSE; - self->flushing = FALSE; - GST_INPUT_SELECTOR_UNLOCK (self); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* first unlock before we call the parent state change function, which - * tries to acquire the stream lock when going to ready. */ - GST_INPUT_SELECTOR_LOCK (self); - self->blocked = FALSE; - self->flushing = TRUE; - GST_INPUT_SELECTOR_BROADCAST (self); - GST_INPUT_SELECTOR_UNLOCK (self); - break; - default: - break; - } - - result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_input_selector_reset (self); - break; - default: - break; - } - - return result; -} - -static gint64 -gst_input_selector_block (GstInputSelector * self) -{ - gint64 ret = 0; - GstSelectorPad *spad; - - GST_INPUT_SELECTOR_LOCK (self); - - if (self->blocked) - GST_WARNING_OBJECT (self, "switch already blocked"); - - self->blocked = TRUE; - spad = GST_SELECTOR_PAD_CAST (self->active_sinkpad); - - if (spad) - ret = gst_selector_pad_get_running_time (spad); - else - GST_DEBUG_OBJECT (self, "no active pad while blocking"); - - GST_INPUT_SELECTOR_UNLOCK (self); - - return ret; -} - -/* stop_time and start_time are running times */ -static void -gst_input_selector_switch (GstInputSelector * self, GstPad * pad, - gint64 stop_time, gint64 start_time) -{ - gboolean changed; - - g_return_if_fail (self->blocked == TRUE); - - GST_INPUT_SELECTOR_LOCK (self); - changed = - gst_input_selector_set_active_pad (self, pad, stop_time, start_time); - - self->blocked = FALSE; - GST_INPUT_SELECTOR_BROADCAST (self); - GST_INPUT_SELECTOR_UNLOCK (self); - - if (changed) - g_object_notify (G_OBJECT (self), "active-pad"); -} - -static gboolean -gst_input_selector_check_eos (GstElement * selector) -{ - GstIterator *it = gst_element_iterate_sink_pads (selector); - GstIteratorResult ires; - gpointer item; - gboolean done = FALSE, is_eos = FALSE; - GstSelectorPad *pad; - - while (!done) { - ires = gst_iterator_next (it, &item); - switch (ires) { - case GST_ITERATOR_DONE: - GST_INFO_OBJECT (selector, "all sink pads have eos"); - done = TRUE; - is_eos = TRUE; - break; - case GST_ITERATOR_OK: - pad = GST_SELECTOR_PAD_CAST (item); - if (!pad->eos) { - done = TRUE; - } - gst_object_unref (pad); - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (it); - break; - default: - done = TRUE; - break; - } - } - gst_iterator_free (it); - - return is_eos; -} diff --git a/gst/playback/gstinputselector.h b/gst/playback/gstinputselector.h deleted file mode 100644 index 58a671d50c..0000000000 --- a/gst/playback/gstinputselector.h +++ /dev/null @@ -1,84 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Julien Moutte - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2008 Nokia Corporation. (contact ) - * - * 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_INPUT_SELECTOR_H__ -#define __GST_INPUT_SELECTOR_H__ - -#include - -G_BEGIN_DECLS - -#define GST_TYPE_INPUT_SELECTOR \ - (gst_input_selector_get_type()) -#define GST_INPUT_SELECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INPUT_SELECTOR, GstInputSelector)) -#define GST_INPUT_SELECTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INPUT_SELECTOR, GstInputSelectorClass)) -#define GST_IS_INPUT_SELECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INPUT_SELECTOR)) -#define GST_IS_INPUT_SELECTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INPUT_SELECTOR)) - -typedef struct _GstInputSelector GstInputSelector; -typedef struct _GstInputSelectorClass GstInputSelectorClass; - -#define GST_INPUT_SELECTOR_GET_LOCK(sel) (((GstInputSelector*)(sel))->lock) -#define GST_INPUT_SELECTOR_GET_COND(sel) (((GstInputSelector*)(sel))->cond) -#define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel))) -#define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel))) -#define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \ - GST_INPUT_SELECTOR_GET_LOCK(sel))) -#define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel))) - -struct _GstInputSelector { - GstElement element; - - GstPad *srcpad; - - GstPad *active_sinkpad; - guint n_pads; - guint padcount; - - GstSegment segment; /* the output segment */ - gboolean pending_close; /* if we should push a close first */ - - GMutex *lock; - GCond *cond; - gboolean blocked; - gboolean flushing; - - /* select all mode, send data from all input pads forward */ - gboolean select_all; -}; - -struct _GstInputSelectorClass { - GstElementClass parent_class; - - gint64 (*block) (GstInputSelector *self); - void (*switch_) (GstInputSelector *self, GstPad *pad, - gint64 stop_time, gint64 start_time); -}; - -GType gst_input_selector_get_type (void); - -G_END_DECLS - -#endif /* __GST_INPUT_SELECTOR_H__ */ diff --git a/gst/playback/gstplay-marshal.list b/gst/playback/gstplay-marshal.list index c07fdce7a2..d7eddf445b 100644 --- a/gst/playback/gstplay-marshal.list +++ b/gst/playback/gstplay-marshal.list @@ -8,5 +8,3 @@ BOXED:OBJECT,BOXED,BOXED BOXED:INT OBJECT:BOXED OBJECT:INT -INT64:VOID -VOID:OBJECT,INT64,INT64 diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index 75f19ab987..30aa71618b 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -230,7 +230,6 @@ #include "gstplay-marshal.h" #include "gstplayback.h" #include "gstplaysink.h" -#include "gstinputselector.h" #include "gstsubtitleoverlay.h" GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug); @@ -264,6 +263,8 @@ struct _GstSourceSelect GstPad *sinkpad; /* the sinkpad of the sink when the selector * is linked */ + GstEvent *sinkpad_delayed_event; + gulong sinkpad_data_probe; }; #define GST_SOURCE_GROUP_GET_LOCK(group) (((GstSourceGroup*)(group))->lock) @@ -321,9 +322,9 @@ struct _GstSourceGroup GstSourceSelect selector[GST_PLAY_SINK_TYPE_LAST]; }; -#define GST_PLAY_BIN_GET_LOCK(bin) (((GstPlayBin*)(bin))->lock) -#define GST_PLAY_BIN_LOCK(bin) (g_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin))) -#define GST_PLAY_BIN_UNLOCK(bin) (g_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin))) +#define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock) +#define GST_PLAY_BIN_LOCK(bin) (g_static_rec_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin))) +#define GST_PLAY_BIN_UNLOCK(bin) (g_static_rec_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin))) /* lock to protect dynamic callbacks, like no-more-pads */ #define GST_PLAY_BIN_DYN_LOCK(bin) g_mutex_lock ((bin)->dyn_lock) @@ -354,7 +355,7 @@ struct _GstPlayBin { GstPipeline parent; - GMutex *lock; /* to protect group switching */ + GStaticRecMutex lock; /* to protect group switching */ /* the groups, we use a double buffer to switch between current and next */ GstSourceGroup groups[2]; /* array with group info */ @@ -508,6 +509,7 @@ enum SIGNAL_GET_VIDEO_PAD, SIGNAL_GET_AUDIO_PAD, SIGNAL_GET_TEXT_PAD, + SIGNAL_SOURCE_SETUP, LAST_SIGNAL }; @@ -909,6 +911,24 @@ gst_play_bin_class_init (GstPlayBinClass * klass) G_STRUCT_OFFSET (GstPlayBinClass, text_tags_changed), NULL, NULL, gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + /** + * GstPlayBin2::source-setup: + * @playbin: a #GstPlayBin2 + * @source: source element + * + * This signal is emitted after the source element has been created, so + * it can be configured by setting additional properties (e.g. set a + * proxy server for an http source, or set the device and read speed for + * an audio cd source). This is functionally equivalent to connecting to + * the notify::source signal, but more convenient. + * + * Since: 0.10.33 + */ + gst_play_bin_signals[SIGNAL_SOURCE_SETUP] = + g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); + /** * GstPlayBin2::get-video-tags * @playbin: a #GstPlayBin2 @@ -1056,6 +1076,8 @@ gst_play_bin_class_init (GstPlayBinClass * klass) static void init_group (GstPlayBin * playbin, GstSourceGroup * group) { + int n; + /* store the array for the different channels */ group->video_channels = g_ptr_array_new (); group->audio_channels = g_ptr_array_new (); @@ -1089,11 +1111,27 @@ init_group (GstPlayBin * playbin, GstSourceGroup * group) group->selector[4].media_list[0] = "video/"; group->selector[4].type = GST_PLAY_SINK_TYPE_VIDEO; group->selector[4].channels = group->video_channels; + + for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) { + GstSourceSelect *select = &group->selector[n]; + select->sinkpad_delayed_event = NULL; + select->sinkpad_data_probe = 0; + } } static void free_group (GstPlayBin * playbin, GstSourceGroup * group) { + int n; + + for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) { + GstSourceSelect *select = &group->selector[n]; + if (select->sinkpad && select->sinkpad_data_probe) + gst_pad_remove_data_probe (select->sinkpad, select->sinkpad_data_probe); + if (select->sinkpad_delayed_event) + gst_event_unref (select->sinkpad_delayed_event); + } + g_free (group->uri); g_free (group->suburi); g_ptr_array_free (group->video_channels, TRUE); @@ -1155,7 +1193,7 @@ gst_play_bin_update_elements_list (GstPlayBin * playbin) static void gst_play_bin_init (GstPlayBin * playbin) { - playbin->lock = g_mutex_new (); + g_static_rec_mutex_init (&playbin->lock); playbin->dyn_lock = g_mutex_new (); /* assume we can create a selector */ @@ -1210,7 +1248,8 @@ gst_play_bin_finalize (GObject * object) if (playbin->elements) gst_plugin_feature_list_free (playbin->elements); - g_mutex_free (playbin->lock); + + g_static_rec_mutex_free (&playbin->lock); g_mutex_free (playbin->dyn_lock); g_mutex_free (playbin->elements_lock); @@ -2301,6 +2340,39 @@ selector_blocked (GstPad * pad, gboolean blocked, gpointer user_data) GST_DEBUG_OBJECT (pad, "blocked callback, blocked: %d", blocked); } +/* this callback sends a delayed event once the pad becomes unblocked */ +static gboolean +stream_changed_data_probe (GstPad * pad, GstMiniObject * object, gpointer data) +{ + GstSourceSelect *select = (GstSourceSelect *) data; + GstEvent *e; + + /* we need do this just once, so cleanup first */ + gst_pad_remove_data_probe (pad, select->sinkpad_data_probe); + select->sinkpad_data_probe = 0; + e = select->sinkpad_delayed_event; + select->sinkpad_delayed_event = NULL; + + /* really, this should not happen */ + if (!e) { + GST_WARNING ("Data probed called, but no delayed event"); + return TRUE; + } + + if (GST_IS_EVENT (object) + && GST_EVENT_TYPE (GST_EVENT_CAST (object)) == GST_EVENT_NEWSEGMENT) { + /* push the event first, then send the delayed one */ + gst_event_ref (GST_EVENT_CAST (object)); + gst_pad_send_event (pad, GST_EVENT_CAST (object)); + gst_pad_send_event (pad, e); + return FALSE; + } else { + /* send delayed event, then allow the caller to go on */ + gst_pad_send_event (pad, e); + return TRUE; + } +} + /* helper function to lookup stuff in lists */ static gboolean array_has_value (const gchar * values[], const gchar * value) @@ -2403,11 +2475,8 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group) GST_SOURCE_GROUP_LOCK (group); if (select->selector == NULL && playbin->have_selector) { /* no selector, create one */ - GST_DEBUG_OBJECT (playbin, "creating new selector"); - select->selector = g_object_new (GST_TYPE_INPUT_SELECTOR, NULL); - /* the above can't fail, but we keep the error handling around for when - * the selector plugin has moved to -base or -good and we stop using an - * internal copy of input-selector */ + GST_DEBUG_OBJECT (playbin, "creating new input selector"); + select->selector = gst_element_factory_make ("input-selector", NULL); if (select->selector == NULL) { /* post the missing selector message only once */ playbin->have_selector = FALSE; @@ -2742,8 +2811,25 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group) group->stream_changed_pending = g_list_prepend (group->stream_changed_pending, GUINT_TO_POINTER (seqnum)); + + /* remove any data probe we might have, and replace */ + if (select->sinkpad_delayed_event) + gst_event_unref (select->sinkpad_delayed_event); + select->sinkpad_delayed_event = event; + if (select->sinkpad_data_probe) + gst_pad_remove_data_probe (select->sinkpad, + select->sinkpad_data_probe); + + /* we go to the trouble of setting a probe on the pad to send + the playbin2-stream-changed event as sending it here might + find that the pad is blocked, so we'd block here, and the + pad might not be linked yet. Additionally, sending it here + apparently would be on the wrong thread */ + select->sinkpad_data_probe = + gst_pad_add_data_probe (select->sinkpad, + (GCallback) stream_changed_data_probe, (gpointer) select); + g_mutex_unlock (group->stream_changed_pending_lock); - gst_pad_send_event (select->sinkpad, event); gst_message_unref (msg); } @@ -2854,48 +2940,70 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad, return result; } -static GstStaticCaps sub_plaintext_caps = - GST_STATIC_CAPS ("text/x-pango-markup; text/plain"); - /* autoplug-continue decides, if a pad has raw caps that can be exposed * directly or if further decoding is necessary. We use this to expose * supported subtitles directly */ + +/* FIXME 0.11: Remove the checks for ANY caps, a sink should specify + * explicitely the caps it supports and if it claims to support ANY + * caps it really should support everything */ static gboolean autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps, GstSourceGroup * group) { - GstCaps *subcaps = NULL; - gboolean ret = FALSE; - GstElement *text_sink; - GstPad *text_sinkpad = NULL; + gboolean ret = TRUE; + GstElement *sink; + GstPad *sinkpad = NULL; - text_sink = - (group->playbin->text_sink) ? gst_object_ref (group->playbin-> - text_sink) : NULL; - if (text_sink) - text_sinkpad = gst_element_get_static_pad (text_sink, "sink"); + GST_PLAY_BIN_LOCK (group->playbin); + GST_SOURCE_GROUP_LOCK (group); - if (text_sinkpad) { - subcaps = gst_pad_get_caps_reffed (text_sinkpad); - gst_object_unref (text_sinkpad); + if ((sink = group->playbin->text_sink)) + sinkpad = gst_element_get_static_pad (sink, "sink"); + if (sinkpad) { + GstCaps *sinkcaps = gst_pad_get_caps_reffed (sinkpad); - /* If the textsink claims to support ANY subcaps, - * go the save way and only use the plaintext caps */ - if (gst_caps_is_any (subcaps)) { - GST_WARNING_OBJECT (group->playbin, "Text sink '%s' accepts ANY caps", - GST_OBJECT_NAME (text_sink)); - gst_caps_unref (subcaps); - subcaps = gst_static_caps_get (&sub_plaintext_caps); - } + if (!gst_caps_is_any (sinkcaps)) + ret = !gst_pad_accept_caps (sinkpad, caps); + gst_caps_unref (sinkcaps); + gst_object_unref (sinkpad); } else { - subcaps = gst_subtitle_overlay_create_factory_caps (); + GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps (); + ret = !gst_caps_can_intersect (caps, subcaps); + gst_caps_unref (subcaps); + } + if (!ret) + goto done; + + if ((sink = group->playbin->audio_sink)) { + sinkpad = gst_element_get_static_pad (sink, "sink"); + if (sinkpad) { + GstCaps *sinkcaps = gst_pad_get_caps_reffed (sinkpad); + + if (!gst_caps_is_any (sinkcaps)) + ret = !gst_pad_accept_caps (sinkpad, caps); + gst_caps_unref (sinkcaps); + gst_object_unref (sinkpad); + } + } + if (!ret) + goto done; + + if ((sink = group->playbin->video_sink)) { + sinkpad = gst_element_get_static_pad (sink, "sink"); + if (sinkpad) { + GstCaps *sinkcaps = gst_pad_get_caps_reffed (sinkpad); + + if (!gst_caps_is_any (sinkcaps)) + ret = !gst_pad_accept_caps (sinkpad, caps); + gst_caps_unref (sinkcaps); + gst_object_unref (sinkpad); + } } - if (text_sink) - gst_object_unref (text_sink); - - ret = !gst_caps_can_intersect (subcaps, caps); - gst_caps_unref (subcaps); +done: + GST_SOURCE_GROUP_UNLOCK (group); + GST_PLAY_BIN_UNLOCK (group->playbin); GST_DEBUG_OBJECT (group->playbin, "continue autoplugging group %p for %s:%s, %" GST_PTR_FORMAT ": %d", @@ -3032,6 +3140,9 @@ notify_source_cb (GstElement * uridecodebin, GParamSpec * pspec, GST_OBJECT_UNLOCK (playbin); g_object_notify (G_OBJECT (playbin), "source"); + + g_signal_emit (playbin, gst_play_bin_signals[SIGNAL_SOURCE_SETUP], + 0, playbin->source); } /* must be called with the group lock */ @@ -3551,8 +3662,6 @@ gst_play_bin2_plugin_init (GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin2", 0, "play bin"); - g_type_class_ref (gst_input_selector_get_type ()); - return gst_element_register (plugin, "playbin2", GST_RANK_NONE, GST_TYPE_PLAY_BIN); } diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index bb41a036a8..5ba637ac40 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -1961,6 +1961,8 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw) * re-generate the chain */ if (chain->volume == NULL) { GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use"); + /* undo background state change done earlier */ + gst_element_set_state (chain->sink, GST_STATE_NULL); return FALSE; } @@ -2217,6 +2219,8 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) if (!playsink->videochain) playsink->videochain = gen_video_chain (playsink, raw, async); + if (!playsink->videochain) + goto no_chain; if (!playsink->video_sinkpad_stream_synchronizer) { GstIterator *it; @@ -2241,6 +2245,8 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) if (!playsink->videodeinterlacechain) playsink->videodeinterlacechain = gen_video_deinterlace_chain (playsink); + if (!playsink->videodeinterlacechain) + goto no_chain; GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain"); @@ -2574,6 +2580,15 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) GST_PLAY_SINK_UNLOCK (playsink); return TRUE; + + /* ERRORS */ +no_chain: + { + /* gen_ chain already posted error */ + GST_DEBUG_OBJECT (playsink, "failed to setup chain"); + GST_PLAY_SINK_UNLOCK (playsink); + return FALSE; + } } /** @@ -3256,6 +3271,21 @@ gst_play_sink_change_state (GstElement * element, GstStateChange transition) add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE); } do_async_done (playsink); + /* when going to READY, keep elements around as long as possible, + * so they may be re-used faster next time/url around. + * when really going to NULL, clean up everything completely. */ + if (transition == GST_STATE_CHANGE_READY_TO_NULL) { + free_chain ((GstPlayChain *) playsink->videodeinterlacechain); + playsink->videodeinterlacechain = NULL; + free_chain ((GstPlayChain *) playsink->videochain); + playsink->videochain = NULL; + free_chain ((GstPlayChain *) playsink->audiochain); + playsink->audiochain = NULL; + free_chain ((GstPlayChain *) playsink->vischain); + playsink->vischain = NULL; + free_chain ((GstPlayChain *) playsink->textchain); + playsink->textchain = NULL; + } break; default: break; diff --git a/gst/playback/gsturidecodebin.c b/gst/playback/gsturidecodebin.c index 7151801397..f4551e3b4f 100644 --- a/gst/playback/gsturidecodebin.c +++ b/gst/playback/gsturidecodebin.c @@ -119,9 +119,12 @@ struct _GstURIDecodeBinClass /* signal fired to get a list of factories to try to autoplug */ GValueArray *(*autoplug_factories) (GstElement * element, GstPad * pad, GstCaps * caps); + /* signal fired to sort the factories */ + GValueArray *(*autoplug_sort) (GstElement * element, GstPad * pad, + GstCaps * caps, GValueArray * factories); /* signal fired to select from the proposed list of factories */ GstAutoplugSelectResult (*autoplug_select) (GstElement * element, - GstPad * pad, GstCaps * caps, GValueArray * factories); + GstPad * pad, GstCaps * caps, GstElementFactory * factory); /* emited when all data is decoded */ void (*drained) (GstElement * element); @@ -145,6 +148,8 @@ enum SIGNAL_AUTOPLUG_FACTORIES, SIGNAL_AUTOPLUG_SELECT, SIGNAL_DRAINED, + SIGNAL_AUTOPLUG_SORT, + SIGNAL_SOURCE_SETUP, LAST_SIGNAL }; @@ -250,6 +255,22 @@ _gst_select_accumulator (GSignalInvocationHint * ihint, return FALSE; } +static gboolean +_gst_array_hasvalue_accumulator (GSignalInvocationHint * ihint, + GValue * return_accu, const GValue * handler_return, gpointer dummy) +{ + gpointer array; + + array = g_value_get_boxed (handler_return); + if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) + g_value_set_boxed (return_accu, array); + + if (array != NULL) + return FALSE; + + return TRUE; +} + static gboolean gst_uri_decode_bin_autoplug_continue (GstElement * element, GstPad * pad, GstCaps * caps) @@ -309,6 +330,22 @@ gst_uri_decode_bin_autoplug_factories (GstElement * element, GstPad * pad, return result; } +static GValueArray * +gst_uri_decode_bin_autoplug_sort (GstElement * element, GstPad * pad, + GstCaps * caps, GValueArray * factories) +{ + return NULL; +} + +static GstAutoplugSelectResult +gst_uri_decode_bin_autoplug_select (GstElement * element, GstPad * pad, + GstCaps * caps, GstElementFactory * factory) +{ + GST_DEBUG_OBJECT (element, "default autoplug-select returns TRY"); + + /* Try factory. */ + return GST_AUTOPLUG_SELECT_TRY; +} static void gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) @@ -407,7 +444,7 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstQueue2:ring-buffer-max-size + * GstURIDecodeBin::ring-buffer-max-size * * The maximum size of the ring buffer in kilobytes. If set to 0, the ring * buffer is disabled. Default is 0. @@ -423,8 +460,8 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) /** * GstURIDecodeBin::unknown-type: - * @bin: The uridecodebin - * @pad: the new pad containing caps that cannot be resolved to a 'final' + * @bin: The uridecodebin. + * @pad: the new pad containing caps that cannot be resolved to a 'final'. * stream type. * @caps: the #GstCaps of the pad that cannot be resolved. * @@ -439,13 +476,19 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) /** * GstURIDecodeBin::autoplug-continue: - * @bin: The uridecodebin + * @bin: The uridecodebin. * @pad: The #GstPad. * @caps: The #GstCaps found. * * This signal is emitted whenever uridecodebin finds a new stream. It is * emitted before looking for any elements that can handle that stream. * + * + * Invocation of signal handlers stops after the first signal handler + * returns #FALSE. Signal handlers are invoked in the order they were + * connected in. + * + * * Returns: #TRUE if you wish uridecodebin to look for elements that can * handle the given @caps. If #FALSE, those caps will be considered as * final and the pad will be exposed as such (see 'new-decoded-pad' @@ -460,7 +503,7 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) /** * GstURIDecodeBin::autoplug-factories: - * @bin: The decodebin + * @bin: The uridecodebin. * @pad: The #GstPad. * @caps: The #GstCaps found. * @@ -473,6 +516,12 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) * If this function returns an empty array, the pad will be considered as * having an unhandled type media type. * + * + * Only the signal handler that is connected first will ever by invoked. + * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this + * signal, they will never be invoked! + * + * * Returns: a #GValueArray* with a list of factories to try. The factories are * by default tried in the returned order or based on the index returned by * "autoplug-select". @@ -485,10 +534,45 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) GST_TYPE_PAD, GST_TYPE_CAPS); /** - * GstURIDecodeBin::autoplug-select: + * GstURIDecodeBin::autoplug-sort: + * @bin: The uridecodebin. * @pad: The #GstPad. * @caps: The #GstCaps. - * @factory: A #GstElementFactory to use + * @factories: A #GValueArray of possible #GstElementFactory to use. + * + * Once decodebin2 has found the possible #GstElementFactory objects to try + * for @caps on @pad, this signal is emited. The purpose of the signal is for + * the application to perform additional sorting or filtering on the element + * factory array. + * + * The callee should copy and modify @factories or return #NULL if the + * order should not change. + * + * + * Invocation of signal handlers stops after one signal handler has + * returned something else than #NULL. Signal handlers are invoked in + * the order they were connected in. + * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this + * signal, they will never be invoked! + * + * + * Returns: A new sorted array of #GstElementFactory objects. + * + * Since: 0.10.33 + */ + gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_SORT] = + g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBinClass, autoplug_sort), + _gst_array_hasvalue_accumulator, NULL, + gst_play_marshal_BOXED__OBJECT_BOXED_BOXED, G_TYPE_VALUE_ARRAY, 3, + GST_TYPE_PAD, GST_TYPE_CAPS, G_TYPE_VALUE_ARRAY); + + /** + * GstURIDecodeBin::autoplug-select: + * @bin: The uridecodebin. + * @pad: The #GstPad. + * @caps: The #GstCaps. + * @factory: A #GstElementFactory to use. * * This signal is emitted once uridecodebin has found all the possible * #GstElementFactory that can be used to handle the given @caps. For each of @@ -506,6 +590,12 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the * next factory. * + * + * Only the signal handler that is connected first will ever by invoked. + * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this + * signal, they will never be invoked! + * + * * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required * operation. The default handler will always return * #GST_AUTOPLUG_SELECT_TRY. @@ -529,6 +619,24 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) G_STRUCT_OFFSET (GstURIDecodeBinClass, drained), NULL, NULL, gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + /** + * GstURIDecodeBin::source-setup: + * @bin: the uridecodebin. + * @source: source element + * + * This signal is emitted after the source element has been created, so + * it can be configured by setting additional properties (e.g. set a + * proxy server for an http source, or set the device and read speed for + * an audio cd source). This is functionally equivalent to connecting to + * the notify::source signal, but more convenient. + * + * Since: 0.10.33 + */ + gst_uri_decode_bin_signals[SIGNAL_SOURCE_SETUP] = + g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); + gstelement_class->query = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_query); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_change_state); @@ -539,6 +647,9 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_continue); klass->autoplug_factories = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_factories); + klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_sort); + klass->autoplug_select = + GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_select); } static void @@ -942,8 +1053,9 @@ array_has_uri_value (const gchar * values[], const gchar * value) /* list of URIs that we consider to be streams and that need buffering. * We have no mechanism yet to figure this out with a query. */ -static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://", - "mmsu://", "mmst://", "fd://", "myth://", "ssh://", "ftp://", "sftp://", +static const gchar *stream_uris[] = { "http://", "https://", "mms://", + "mmsh://", "mmsu://", "mmst://", "fd://", "myth://", "ssh://", + "ftp://", "sftp://", NULL }; @@ -1368,6 +1480,21 @@ proxy_autoplug_factories_signal (GstElement * element, GstPad * pad, return result; } +static GValueArray * +proxy_autoplug_sort_signal (GstElement * element, GstPad * pad, + GstCaps * caps, GValueArray * factories, GstURIDecodeBin * dec) +{ + GValueArray *result; + + g_signal_emit (dec, + gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_SORT], 0, pad, caps, + factories, &result); + + GST_DEBUG_OBJECT (dec, "autoplug-sort returned %p", result); + + return result; +} + static GstAutoplugSelectResult proxy_autoplug_select_signal (GstElement * element, GstPad * pad, GstCaps * caps, GstElementFactory * factory, GstURIDecodeBin * dec) @@ -1419,6 +1546,8 @@ make_decoder (GstURIDecodeBin * decoder) G_CALLBACK (proxy_autoplug_continue_signal), decoder); g_signal_connect (decodebin, "autoplug-factories", G_CALLBACK (proxy_autoplug_factories_signal), decoder); + g_signal_connect (decodebin, "autoplug-sort", + G_CALLBACK (proxy_autoplug_sort_signal), decoder); g_signal_connect (decodebin, "autoplug-select", G_CALLBACK (proxy_autoplug_select_signal), decoder); g_signal_connect (decodebin, "drained", @@ -1767,6 +1896,9 @@ setup_source (GstURIDecodeBin * decoder) /* notify of the new source used */ g_object_notify (G_OBJECT (decoder), "source"); + g_signal_emit (decoder, gst_uri_decode_bin_signals[SIGNAL_SOURCE_SETUP], + 0, decoder->source); + /* remove the old decoders now, if any */ remove_decoders (decoder, FALSE); diff --git a/gst/tcp/gstmultifdsink.c b/gst/tcp/gstmultifdsink.c index b0e87898f5..9532c83e38 100644 --- a/gst/tcp/gstmultifdsink.c +++ b/gst/tcp/gstmultifdsink.c @@ -837,6 +837,8 @@ gst_multi_fd_sink_add_full (GstMultiFdSink * sink, int fd, client->bytes_sent = 0; client->dropped_buffers = 0; client->avg_queue_size = 0; + client->first_buffer_ts = GST_CLOCK_TIME_NONE; + client->last_buffer_ts = GST_CLOCK_TIME_NONE; client->new_connection = TRUE; client->burst_min_unit = min_unit; client->burst_min_value = min_value; @@ -1027,6 +1029,8 @@ restart: * guint64 : time the client is/was connected (in nanoseconds) * guint64 : last activity time (in nanoseconds, since Epoch) * guint64 : buffers dropped due to recovery + * guint64 : timestamp of the first buffer sent (in nanoseconds) + * guint64 : timestamp of the last buffer sent (in nanoseconds) */ GValueArray * gst_multi_fd_sink_get_stats (GstMultiFdSink * sink, int fd) @@ -1045,7 +1049,7 @@ gst_multi_fd_sink_get_stats (GstMultiFdSink * sink, int fd) GValue value = { 0 }; guint64 interval; - result = g_value_array_new (5); + result = g_value_array_new (7); g_value_init (&value, G_TYPE_UINT64); g_value_set_uint64 (&value, client->bytes_sent); @@ -1079,6 +1083,14 @@ gst_multi_fd_sink_get_stats (GstMultiFdSink * sink, int fd) g_value_init (&value, G_TYPE_UINT64); g_value_set_uint64 (&value, client->dropped_buffers); result = g_value_array_append (result, &value); + g_value_unset (&value); + g_value_init (&value, G_TYPE_UINT64); + g_value_set_uint64 (&value, client->first_buffer_ts); + result = g_value_array_append (result, &value); + g_value_unset (&value); + g_value_init (&value, G_TYPE_UINT64); + g_value_set_uint64 (&value, client->last_buffer_ts); + result = g_value_array_append (result, &value); } noclient: @@ -1916,6 +1928,7 @@ gst_multi_fd_sink_handle_client_write (GstMultiFdSink * sink, } else { /* client can pick a buffer from the global queue */ GstBuffer *buf; + GstClockTime timestamp; /* for new connections, we need to find a good spot in the * bufqueue to start streaming from */ @@ -1941,6 +1954,13 @@ gst_multi_fd_sink_handle_client_write (GstMultiFdSink * sink, buf = g_array_index (sink->bufqueue, GstBuffer *, client->bufpos); client->bufpos--; + /* update stats */ + timestamp = GST_BUFFER_TIMESTAMP (buf); + if (client->first_buffer_ts == GST_CLOCK_TIME_NONE) + client->first_buffer_ts = timestamp; + if (timestamp != -1) + client->last_buffer_ts = timestamp; + /* decrease flushcount */ if (client->flushcount != -1) client->flushcount--; diff --git a/gst/tcp/gstmultifdsink.h b/gst/tcp/gstmultifdsink.h index b0824073a9..b0465afb0a 100644 --- a/gst/tcp/gstmultifdsink.h +++ b/gst/tcp/gstmultifdsink.h @@ -174,6 +174,8 @@ typedef struct { guint64 last_activity_time; guint64 dropped_buffers; guint64 avg_queue_size; + guint64 first_buffer_ts; + guint64 last_buffer_ts; } GstTCPClient; #define CLIENTS_LOCK_INIT(fdsink) (g_static_rec_mutex_init(&fdsink->clientslock)) diff --git a/gst/tcp/gsttcpserversink.c b/gst/tcp/gsttcpserversink.c index c0cb95f73d..fdca0ec755 100644 --- a/gst/tcp/gsttcpserversink.c +++ b/gst/tcp/gsttcpserversink.c @@ -149,7 +149,7 @@ gst_tcp_server_sink_handle_server_read (GstTCPServerSink * sink) /* new client */ int client_sock_fd; struct sockaddr_in client_address; - unsigned int client_address_len; + socklen_t client_address_len; /* For some stupid reason, client_address and client_address_len has to be * zeroed */ diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c index 030f7afdd5..2b35330f52 100644 --- a/gst/typefind/gsttypefindfunctions.c +++ b/gst/typefind/gsttypefindfunctions.c @@ -1252,11 +1252,11 @@ ac3_type_find (GstTypeFind * tf, gpointer unused) break; if (c.data[0] == 0x0b && c.data[1] == 0x77) { - guint bsid = (c.data[5] >> 3) & 0x1F; + guint bsid = c.data[5] >> 3; if (bsid <= 8) { /* ac3 */ - guint fscod = (c.data[4] >> 6) & 0x03; + guint fscod = c.data[4] >> 6; guint frmsizecod = c.data[4] & 0x3f; if (fscod < 3 && frmsizecod < 38) { @@ -1270,13 +1270,14 @@ ac3_type_find (GstTypeFind * tf, gpointer unused) data_scan_ctx_advance (tf, &c_next, frame_size * 2); if (c_next.data[0] == 0x0b && c_next.data[1] == 0x77) { - guint fscod2 = (c_next.data[4] >> 6) & 0x03; - guint frmsizecod2 = c_next.data[4] & 0x3f; + fscod = c_next.data[4] >> 6; + frmsizecod = c_next.data[4] & 0x3f; - if (fscod == fscod2 && frmsizecod == frmsizecod2) { + if (fscod < 3 && frmsizecod < 38) { GstTypeFindProbability prob; - GST_LOG ("found second AC3 frame, looks good"); + GST_LOG ("found second AC3 frame (size=%u), looks good", + ac3_frmsizecod_tbl[frmsizecod].frm_size[fscod]); if (c.offset == 0) prob = GST_TYPE_FIND_MAXIMUM; else @@ -1290,13 +1291,12 @@ ac3_type_find (GstTypeFind * tf, gpointer unused) } } } - } else { + } else if (bsid <= 16 && bsid > 10) { /* eac3 */ DataScanCtx c_next = c; guint frame_size; - frame_size = ((((c.data[2] & 0x07) << 8) + - (c.data[3] & 0xff)) + 1) << 1; + frame_size = (((c.data[2] & 0x07) << 8) + c.data[3]) + 1; GST_LOG ("possible E-AC3 frame sync at offset %" G_GUINT64_FORMAT ", size=%u", c.offset, frame_size); if (data_scan_ctx_ensure_data (tf, &c_next, (frame_size * 2) + 5)) { @@ -1312,10 +1312,13 @@ ac3_type_find (GstTypeFind * tf, gpointer unused) prob = GST_TYPE_FIND_NEARLY_CERTAIN; gst_type_find_suggest (tf, prob, EAC3_CAPS); + return; } else { GST_LOG ("no second E-AC3 frame found, false sync"); } } + } else { + GST_LOG ("invalid AC3 BSID: %u", bsid); } } data_scan_ctx_advance (tf, &c, 1); @@ -1429,10 +1432,11 @@ dts_type_find (GstTypeFind * tf, gpointer unused) if (chans > 0) { gst_type_find_suggest_simple (tf, prob, "audio/x-dts", - "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, NULL); + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, + "framed", G_TYPE_BOOLEAN, FALSE, NULL); } else { gst_type_find_suggest_simple (tf, prob, "audio/x-dts", - "rate", G_TYPE_INT, rate, NULL); + "rate", G_TYPE_INT, rate, "framed", G_TYPE_BOOLEAN, FALSE, NULL); } return; @@ -1892,7 +1896,7 @@ static GstStaticCaps mpegts_caps = GST_STATIC_CAPS ("video/mpegts, " /* Check for sync byte, error_indicator == 0 and packet has payload */ #define IS_MPEGTS_HEADER(data) (((data)[0] == 0x47) && \ (((data)[1] & 0x80) == 0x00) && \ - (((data)[3] & 0x10) == 0x10)) + (((data)[3] & 0x30) != 0x00)) /* Helper function to search ahead at intervals of packet_size for mpegts * headers */ @@ -2121,9 +2125,63 @@ mpeg4_video_type_find (GstTypeFind * tf, gpointer unused) } } +/*** video/x-h263 H263 video stream ***/ +static GstStaticCaps h263_video_caps = GST_STATIC_CAPS ("video/x-h263"); + +#define H263_VIDEO_CAPS gst_static_caps_get(&h263_video_caps) + +#define H263_MAX_PROBE_LENGTH (128 * 1024) + +static void +h263_video_type_find (GstTypeFind * tf, gpointer unused) +{ + DataScanCtx c = { 0, NULL, 0 }; + guint64 data = 0; + guint64 psc = 0; + guint8 tr = 0; + guint format; + guint good = 0; + guint bad = 0; + + while (c.offset < H263_MAX_PROBE_LENGTH) { + if (G_UNLIKELY (!data_scan_ctx_ensure_data (tf, &c, 4))) + break; + + /* Find the picture start code */ + data = (data << 8) + c.data[0]; + psc = data & 0xfffffc0000; + if (psc == 0x800000) { + /* Found PSC */ + /* TR */ + tr = (data & 0x3fc) >> 2; + /* Source Format */ + format = tr & 0x07; + + /* Now that we have a Valid PSC, check if we also have a valid PTYPE and + the Source Format, which should range between 1 and 5 */ + if (((tr >> 6) == 0x2) && (format > 0 && format < 6)) + good++; + else + bad++; + + /* FIXME: maybe bail out early if we get mostly bad syncs ? */ + } + + data_scan_ctx_advance (tf, &c, 1); + } + + if (good > 0 && bad == 0) + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, H263_VIDEO_CAPS); + else if (good > 2 * bad) + gst_type_find_suggest (tf, GST_TYPE_FIND_POSSIBLE, H263_VIDEO_CAPS); + + return; +} + /*** video/x-h264 H264 elementary video stream ***/ -static GstStaticCaps h264_video_caps = GST_STATIC_CAPS ("video/x-h264"); +static GstStaticCaps h264_video_caps = +GST_STATIC_CAPS ("video/x-h264,stream-format=byte-stream"); #define H264_VIDEO_CAPS gst_static_caps_get(&h264_video_caps) @@ -2542,7 +2600,26 @@ qt_type_find (GstTypeFind * tf, gpointer unused) tip = 0; break; } + size = GST_READ_UINT32_BE (data); + /* check compatible brands rather than ever expaning major brands above */ + if ((STRNCMP (&data[4], "ftyp", 4) == 0) && (size >= 16)) { + new_offset = offset + 12; + while (new_offset + 4 <= offset + size) { + data = gst_type_find_peek (tf, new_offset, 4); + if (data == NULL) + goto done; + if (STRNCMP (&data[4], "isom", 4) == 0 || + STRNCMP (&data[4], "avc1", 4) == 0 || + STRNCMP (&data[4], "mp41", 4) == 0 || + STRNCMP (&data[4], "mp42", 4) == 0) { + tip = GST_TYPE_FIND_MAXIMUM; + variant = "iso"; + goto done; + } + new_offset += 4; + } + } if (size == 1) { guint8 *sizedata; @@ -2561,6 +2638,7 @@ qt_type_find (GstTypeFind * tf, gpointer unused) offset = new_offset; } +done: if (tip > 0) { if (variant) { GstCaps *caps = gst_caps_copy (QT_CAPS); @@ -3161,6 +3239,10 @@ ebml_check_header (GstTypeFind * tf, const gchar * doctype, int doctype_len) if (!data) return FALSE; + /* only check doctype if asked to do so */ + if (doctype == NULL || doctype_len == 0) + return TRUE; + /* the header must contain the doctype. For now, we don't parse the * whole header but simply check for the availability of that array * of characters inside the header. Not fully fool-proof, but good @@ -3181,6 +3263,8 @@ matroska_type_find (GstTypeFind * tf, gpointer ununsed) { if (ebml_check_header (tf, "matroska", 8)) gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MATROSKA_CAPS); + else if (ebml_check_header (tf, NULL, 0)) + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, MATROSKA_CAPS); } /*** video/webm ***/ @@ -3838,6 +3922,96 @@ xdgmime_typefind (GstTypeFind * find, gpointer user_data) } #endif /* USE_GIO */ +/*** Windows icon typefinder (to avoid false positives mostly) ***/ + +static void +windows_icon_typefind (GstTypeFind * find, gpointer user_data) +{ + guint8 *data; + gint64 datalen; + guint16 type, nimages; + gint32 size, offset; + + datalen = gst_type_find_get_length (find); + if ((data = gst_type_find_peek (find, 0, 6)) == NULL) + return; + + /* header - simple and not enough to rely on it alone */ + if (GST_READ_UINT16_LE (data) != 0) + return; + type = GST_READ_UINT16_LE (data + 2); + if (type != 1 && type != 2) + return; + nimages = GST_READ_UINT16_LE (data + 4); + if (nimages == 0) /* we can assume we can't have an empty image file ? */ + return; + + /* first image */ + if (data[6 + 3] != 0) + return; + if (type == 1) { + guint16 planes = GST_READ_UINT16_LE (data + 6 + 4); + if (planes > 1) + return; + } + size = GST_READ_UINT32_LE (data + 6 + 8); + offset = GST_READ_UINT32_LE (data + 6 + 12); + if (offset < 0 || size <= 0 || size >= datalen || offset >= datalen + || size + offset > datalen) + return; + + gst_type_find_suggest_simple (find, GST_TYPE_FIND_NEARLY_CERTAIN, + "image/x-icon", NULL); +} + + +/*** DEGAS Atari images (also to avoid false positives, see #625129) ***/ +static void +degas_type_find (GstTypeFind * tf, gpointer private) +{ + /* No magic, but it should have a fixed size and a few invalid values */ + /* http://www.fileformat.info/format/atari/spec/6ecf9f6eb5be494284a47feb8a214687/view.htm */ + gint64 len; + const guint8 *data; + guint16 resolution; + int n; + + len = gst_type_find_get_length (tf); + if (len < 34) /* smallest header of the lot */ + return; + data = gst_type_find_peek (tf, 0, 4); + resolution = GST_READ_UINT16_BE (data); + if (len == 32034) { + /* could be DEGAS */ + if (resolution <= 2) + gst_type_find_suggest_simple (tf, GST_TYPE_FIND_POSSIBLE + 5, + "image/x-degas", NULL); + } else if (len == 32066) { + /* could be DEGAS Elite */ + if (resolution <= 2) { + data = gst_type_find_peek (tf, len - 16, 8); + for (n = 0; n < 4; n++) { + if (GST_READ_UINT16_BE (data + n * 2) > 2) + return; + } + gst_type_find_suggest_simple (tf, GST_TYPE_FIND_POSSIBLE + 5, + "image/x-degas", NULL); + } + } else if (len >= 66 && len < 32066) { + /* could be compressed DEGAS Elite, but it's compressed and so we can't rely on size, + it does have 4 16 bytes values near the end that are 0-2 though. */ + if ((resolution & 0x8000) && (resolution & 0x7fff) <= 2) { + data = gst_type_find_peek (tf, len - 16, 8); + for (n = 0; n < 4; n++) { + if (GST_READ_UINT16_BE (data + n * 2) > 2) + return; + } + gst_type_find_suggest_simple (tf, GST_TYPE_FIND_POSSIBLE + 5, + "image/x-degas", NULL); + } + } +} + /*** generic typefind for streams that have some data at a specific position***/ typedef struct { @@ -4019,6 +4193,7 @@ plugin_init (GstPlugin * plugin) }; static const gchar *flv_exts[] = { "flv", NULL }; static const gchar *m4v_exts[] = { "m4v", NULL }; + static const gchar *h263_exts[] = { "h263", "263", NULL }; static const gchar *h264_exts[] = { "h264", "x264", "264", NULL }; static const gchar *nuv_exts[] = { "nuv", NULL }; static const gchar *vivo_exts[] = { "viv", NULL }; @@ -4040,6 +4215,7 @@ plugin_init (GstPlugin * plugin) static const gchar *msword_exts[] = { "doc", NULL }; static const gchar *dsstore_exts[] = { "DS_Store", NULL }; static const gchar *psd_exts[] = { "psd", NULL }; + static const gchar *y4m_exts[] = { "y4m", NULL }; GST_DEBUG_CATEGORY_INIT (type_find_debug, "typefindfunctions", GST_DEBUG_FG_GREEN | GST_DEBUG_BG_RED, "generic type find functions"); @@ -4105,8 +4281,10 @@ plugin_init (GstPlugin * plugin) NULL); TYPE_FIND_REGISTER (plugin, "video/mpeg4", GST_RANK_PRIMARY, mpeg4_video_type_find, m4v_exts, MPEG_VIDEO_CAPS, NULL, NULL); + TYPE_FIND_REGISTER (plugin, "video/x-h263", GST_RANK_SECONDARY, + h263_video_type_find, h263_exts, H263_VIDEO_CAPS, NULL, NULL); TYPE_FIND_REGISTER (plugin, "video/x-h264", GST_RANK_PRIMARY, - h264_video_type_find, h264_exts, MPEG_VIDEO_CAPS, NULL, NULL); + h264_video_type_find, h264_exts, H264_VIDEO_CAPS, NULL, NULL); TYPE_FIND_REGISTER (plugin, "video/x-nuv", GST_RANK_SECONDARY, nuv_type_find, nuv_exts, NUV_CAPS, NULL, NULL); @@ -4240,7 +4418,7 @@ plugin_init (GstPlugin * plugin) NULL, CMML_CAPS, NULL, NULL); TYPE_FIND_REGISTER_START_WITH (plugin, "application/x-executable", GST_RANK_MARGINAL, NULL, "\177ELF", 4, GST_TYPE_FIND_MAXIMUM); - TYPE_FIND_REGISTER (plugin, "aac", GST_RANK_SECONDARY, + TYPE_FIND_REGISTER (plugin, "audio/aac", GST_RANK_SECONDARY, aac_type_find, aac_exts, AAC_CAPS, NULL, NULL); TYPE_FIND_REGISTER_START_WITH (plugin, "audio/x-spc", GST_RANK_SECONDARY, spc_exts, "SNES-SPC700 Sound File Data", 27, GST_TYPE_FIND_MAXIMUM); @@ -4298,12 +4476,19 @@ plugin_init (GstPlugin * plugin) TYPE_FIND_REGISTER_START_WITH (plugin, "image/vnd.adobe.photoshop", GST_RANK_SECONDARY, psd_exts, "8BPS\000\001\000\000\000\000", 10, GST_TYPE_FIND_LIKELY); + TYPE_FIND_REGISTER_START_WITH (plugin, "application/x-yuv4mpeg", + GST_RANK_SECONDARY, y4m_exts, "YUV4MPEG2 ", 10, GST_TYPE_FIND_LIKELY); + TYPE_FIND_REGISTER (plugin, "image/x-icon", GST_RANK_MARGINAL, + windows_icon_typefind, NULL, NULL, NULL, NULL); #ifdef USE_GIO TYPE_FIND_REGISTER (plugin, "xdgmime-base", GST_RANK_MARGINAL, xdgmime_typefind, NULL, NULL, NULL, NULL); #endif + TYPE_FIND_REGISTER (plugin, "image/x-degas", GST_RANK_MARGINAL, + degas_type_find, NULL, NULL, NULL, NULL); + return TRUE; } diff --git a/gst/videorate/gstvideorate.c b/gst/videorate/gstvideorate.c index 827f0e9290..2797d8ab47 100644 --- a/gst/videorate/gstvideorate.c +++ b/gst/videorate/gstvideorate.c @@ -290,6 +290,16 @@ gst_video_rate_setcaps (GstPad * pad, GstCaps * caps) goto no_framerate; if (pad == videorate->srcpad) { + /* out_frame_count is scaled by the frame rate caps when calculating next_ts. + * when the frame rate caps change, we must update base_ts and reset + * out_frame_count */ + if (videorate->to_rate_numerator) { + videorate->base_ts += + gst_util_uint64_scale (videorate->out_frame_count, + videorate->to_rate_denominator * GST_SECOND, + videorate->to_rate_numerator); + } + videorate->out_frame_count = 0; videorate->to_rate_numerator = rate_numerator; videorate->to_rate_denominator = rate_denominator; otherpad = videorate->sinkpad; @@ -394,7 +404,8 @@ gst_video_rate_reset (GstVideoRate * videorate) videorate->in = 0; videorate->out = 0; - videorate->segment_out = 0; + videorate->base_ts = 0; + videorate->out_frame_count = 0; videorate->drop = 0; videorate->dup = 0; videorate->next_ts = GST_CLOCK_TIME_NONE; @@ -473,11 +484,12 @@ gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate) push_ts = videorate->next_ts; videorate->out++; - videorate->segment_out++; + videorate->out_frame_count++; if (videorate->to_rate_numerator) { /* interpolate next expected timestamp in the segment */ - videorate->next_ts = videorate->segment.accum + videorate->segment.start + - gst_util_uint64_scale (videorate->segment_out, + videorate->next_ts = + videorate->segment.accum + videorate->segment.start + + videorate->base_ts + gst_util_uint64_scale (videorate->out_frame_count, videorate->to_rate_denominator * GST_SECOND, videorate->to_rate_numerator); GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts; @@ -587,7 +599,8 @@ gst_video_rate_event (GstPad * pad, GstEvent * event) gst_video_rate_notify_drop (videorate); } /* clean up for the new one; _chain will resume from the new start */ - videorate->segment_out = 0; + videorate->base_ts = 0; + videorate->out_frame_count = 0; gst_video_rate_swap_prev (videorate, NULL, 0); videorate->next_ts = GST_CLOCK_TIME_NONE; } @@ -782,11 +795,9 @@ gst_video_rate_chain (GstPad * pad, GstBuffer * buffer) /* new buffer, we expect to output a buffer that matches the first * timestamp in the segment */ if (videorate->skip_to_first) { - videorate->next_ts = in_ts; - videorate->segment_out = gst_util_uint64_scale (in_ts, - videorate->to_rate_numerator, - videorate->to_rate_denominator * GST_SECOND) - - (videorate->segment.accum + videorate->segment.start); + videorate->next_ts = intime; + videorate->base_ts = in_ts - videorate->segment.start; + videorate->out_frame_count = 0; } else { videorate->next_ts = videorate->segment.start + videorate->segment.accum; diff --git a/gst/videorate/gstvideorate.h b/gst/videorate/gstvideorate.h index 4d6c0615ac..23e20567e9 100644 --- a/gst/videorate/gstvideorate.h +++ b/gst/videorate/gstvideorate.h @@ -55,7 +55,11 @@ struct _GstVideoRate guint64 next_ts; /* Timestamp of next buffer to output */ GstBuffer *prevbuf; guint64 prev_ts; /* Previous buffer timestamp */ - guint64 segment_out; /* in-segment counting */ + guint64 out_frame_count; /* number of frames output since the beginning + * of the segment or the last frame rate caps + * change, whichever was later */ + guint64 base_ts; /* used in next_ts calculation after a + * frame rate caps change */ gboolean discont; guint64 last_ts; /* Timestamp of last input buffer */ diff --git a/gst/videoscale/gstvideoscale.c b/gst/videoscale/gstvideoscale.c index b0f8f436b8..aaf104bd42 100644 --- a/gst/videoscale/gstvideoscale.c +++ b/gst/videoscale/gstvideoscale.c @@ -129,7 +129,9 @@ static GstStaticCaps gst_video_scale_format_caps[] = { GST_STATIC_CAPS (GST_VIDEO_CAPS_GRAY8), GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y800")), GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y8 ")), - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("GREY")) + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("GREY")), + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AY64")), + GST_STATIC_CAPS (GST_VIDEO_CAPS_ARGB_64) }; #define GST_TYPE_VIDEO_SCALE_METHOD (gst_video_scale_method_get_type()) @@ -219,7 +221,7 @@ gst_video_scale_base_init (gpointer g_class) GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_element_class_set_details_simple (element_class, - "Video scaler", "Filter/Effect/Video", + "Video scaler", "Filter/Converter/Video/Scaler", "Resizes video", "Wim Taymans "); gst_element_class_add_pad_template (element_class, @@ -422,9 +424,7 @@ gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out) if (videoscale->tmp_buf) g_free (videoscale->tmp_buf); - videoscale->tmp_buf = - g_malloc (gst_video_format_get_row_stride (videoscale->format, 0, - videoscale->to_width) * 4); + videoscale->tmp_buf = g_malloc (videoscale->to_width * 8 * 4); gst_base_transform_set_passthrough (trans, (videoscale->from_width == videoscale->to_width @@ -960,6 +960,7 @@ _get_black_for_format (GstVideoFormat format) case GST_VIDEO_FORMAT_ABGR: case GST_VIDEO_FORMAT_xRGB: case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_ARGB64: return black[0]; case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGRA: @@ -967,6 +968,7 @@ _get_black_for_format (GstVideoFormat format) case GST_VIDEO_FORMAT_BGRx: return black[1]; case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_AYUV64: return black[2]; case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_BGR: @@ -1072,6 +1074,24 @@ gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in, goto unknown_mode; } break; + case GST_VIDEO_FORMAT_ARGB64: + case GST_VIDEO_FORMAT_AYUV64: + if (add_borders) + vs_fill_borders_AYUV64 (&dest, black); + switch (method) { + case GST_VIDEO_SCALE_NEAREST: + vs_image_scale_nearest_AYUV64 (&dest, &src, videoscale->tmp_buf); + break; + case GST_VIDEO_SCALE_BILINEAR: + vs_image_scale_linear_AYUV64 (&dest, &src, videoscale->tmp_buf); + break; + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_AYUV64 (&dest, &src, videoscale->tmp_buf); + break; + default: + goto unknown_mode; + } + break; case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_BGR: case GST_VIDEO_FORMAT_v308: diff --git a/gst/videoscale/gstvideoscaleorc-dist.c b/gst/videoscale/gstvideoscaleorc-dist.c index d65ccbca44..d62e29ff44 100644 --- a/gst/videoscale/gstvideoscaleorc-dist.c +++ b/gst/videoscale/gstvideoscaleorc-dist.c @@ -32,6 +32,7 @@ typedef unsigned __int16 orc_uint16; typedef unsigned __int32 orc_uint32; typedef unsigned __int64 orc_uint64; #define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline #else #include typedef signed char orc_int8; @@ -78,6 +79,7 @@ void orc_merge_linear_u16 (orc_uint16 * d1, const orc_uint16 * s1, const orc_uint16 * s2, int p1, int p2, int n); void orc_splat_u16 (orc_uint16 * d1, int p1, int n); void orc_splat_u32 (orc_uint32 * d1, int p1, int n); +void orc_splat_u64 (orc_uint64 * d1, orc_int64 p1, int n); void orc_downsample_u8 (guint8 * d1, const guint8 * s1, int n); void orc_downsample_u16 (guint16 * d1, const guint16 * s1, int n); void gst_videoscale_orc_downsample_u32 (guint8 * d1, const guint8 * s1, int n); @@ -171,7 +173,7 @@ orc_merge_linear_u8 (orc_uint8 * d1, const orc_uint8 * s1, const orc_uint8 * s2, /* 6: loadpw */ var38.i = p1; /* 8: loadpw */ - var39.i = 0x00000080; /* 128 or 6.32404e-322f */ + var39.i = (int) 0x00000080; /* 128 or 6.32404e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ @@ -229,7 +231,7 @@ _backup_orc_merge_linear_u8 (OrcExecutor * ORC_RESTRICT ex) /* 6: loadpw */ var38.i = ex->params[24]; /* 8: loadpw */ - var39.i = 0x00000080; /* 128 or 6.32404e-322f */ + var39.i = (int) 0x00000080; /* 128 or 6.32404e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ @@ -540,6 +542,81 @@ orc_splat_u32 (orc_uint32 * d1, int p1, int n) #endif +/* orc_splat_u64 */ +#ifdef DISABLE_ORC +void +orc_splat_u64 (orc_uint64 * d1, orc_int64 p1, int n) +{ + int i; + orc_union64 *ORC_RESTRICT ptr0; + orc_union64 var32; + orc_union64 var33; + + ptr0 = (orc_union64 *) d1; + + /* 0: loadpq */ + var32.i = p1; + + for (i = 0; i < n; i++) { + /* 1: copyq */ + var33.i = var32.i; + /* 2: storeq */ + ptr0[i] = var33; + } + +} + +#else +static void +_backup_orc_splat_u64 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union64 *ORC_RESTRICT ptr0; + orc_union64 var32; + orc_union64 var33; + + ptr0 = (orc_union64 *) ex->arrays[0]; + + /* 0: loadpq */ + var32.i = + (ex->params[24] & 0xffffffff) | ((orc_uint64) (ex->params[24 + + (ORC_VAR_T1 - ORC_VAR_P1)]) << 32); + + for (i = 0; i < n; i++) { + /* 1: copyq */ + var33.i = var32.i; + /* 2: storeq */ + ptr0[i] = var33; + } + +} + +static OrcProgram *_orc_program_orc_splat_u64; +void +orc_splat_u64 (orc_uint64 * d1, orc_int64 p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + OrcProgram *p = _orc_program_orc_splat_u64; + void (*func) (OrcExecutor *); + + ex->program = p; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + { + orc_union64 tmp; + tmp.i = p1; + ex->params[ORC_VAR_P1] = tmp.x2[0]; + ex->params[ORC_VAR_T1] = tmp.x2[1]; + } + + func = p->code_exec; + func (ex); +} +#endif + + /* orc_downsample_u8 */ #ifdef DISABLE_ORC void @@ -1510,7 +1587,7 @@ gst_videoscale_orc_merge_bicubic_u8 (guint8 * d1, const guint8 * s1, /* 12: loadpb */ var41 = p4; /* 15: loadpw */ - var42.i = 0x00000020; /* 32 or 1.58101e-322f */ + var42.i = (int) 0x00000020; /* 32 or 1.58101e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ @@ -1593,7 +1670,7 @@ _backup_gst_videoscale_orc_merge_bicubic_u8 (OrcExecutor * ORC_RESTRICT ex) /* 12: loadpb */ var41 = ex->params[27]; /* 15: loadpw */ - var42.i = 0x00000020; /* 32 or 1.58101e-322f */ + var42.i = (int) 0x00000020; /* 32 or 1.58101e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ @@ -1770,6 +1847,24 @@ gst_videoscale_orc_init (void) _orc_program_orc_splat_u32 = p; } + { + /* orc_splat_u64 */ + OrcProgram *p; + OrcCompileResult result; + + p = orc_program_new (); + orc_program_set_name (p, "orc_splat_u64"); + orc_program_set_backup_function (p, _backup_orc_splat_u64); + orc_program_add_destination (p, 8, "d1"); + orc_program_add_parameter_int64 (p, 8, "p1"); + + orc_program_append_2 (p, "copyq", 0, ORC_VAR_D1, ORC_VAR_P1, ORC_VAR_D1, + ORC_VAR_D1); + + result = orc_program_compile (p); + + _orc_program_orc_splat_u64 = p; + } { /* orc_downsample_u8 */ OrcProgram *p; diff --git a/gst/videoscale/gstvideoscaleorc-dist.h b/gst/videoscale/gstvideoscaleorc-dist.h index df1de1d2b6..93157f09cb 100644 --- a/gst/videoscale/gstvideoscaleorc-dist.h +++ b/gst/videoscale/gstvideoscaleorc-dist.h @@ -37,6 +37,7 @@ typedef unsigned __int16 orc_uint16; typedef unsigned __int32 orc_uint32; typedef unsigned __int64 orc_uint64; #define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline #else #include typedef signed char orc_int8; @@ -63,6 +64,7 @@ void orc_merge_linear_u8 (orc_uint8 * d1, const orc_uint8 * s1, const orc_uint8 void orc_merge_linear_u16 (orc_uint16 * d1, const orc_uint16 * s1, const orc_uint16 * s2, int p1, int p2, int n); void orc_splat_u16 (orc_uint16 * d1, int p1, int n); void orc_splat_u32 (orc_uint32 * d1, int p1, int n); +void orc_splat_u64 (orc_uint64 * d1, orc_int64 p1, int n); void orc_downsample_u8 (guint8 * d1, const guint8 * s1, int n); void orc_downsample_u16 (guint16 * d1, const guint16 * s1, int n); void gst_videoscale_orc_downsample_u32 (guint8 * d1, const guint8 * s1, int n); diff --git a/gst/videoscale/gstvideoscaleorc.orc b/gst/videoscale/gstvideoscaleorc.orc index 17d9687101..81623e231b 100644 --- a/gst/videoscale/gstvideoscaleorc.orc +++ b/gst/videoscale/gstvideoscaleorc.orc @@ -54,6 +54,13 @@ copyw d1, p1 copyl d1, p1 +.function orc_splat_u64 +.dest 8 d1 +.longparam 8 p1 + +copyq d1, p1 + + .function orc_downsample_u8 .dest 1 d1 guint8 .source 2 s1 guint8 diff --git a/gst/videoscale/vs_4tap.c b/gst/videoscale/vs_4tap.c index a210371d8c..e582f7c304 100644 --- a/gst/videoscale/vs_4tap.c +++ b/gst/videoscale/vs_4tap.c @@ -1324,3 +1324,128 @@ vs_image_scale_4tap_RGB555 (const VSImage * dest, const VSImage * src, yacc += y_increment; } } + +void +vs_scanline_resample_4tap_AYUV64 (uint16_t * dest, uint16_t * src, + int n, int src_width, int *xacc, int increment) +{ + int i; + int j; + int acc; + int x; + int y; + int off; + + acc = *xacc; + for (i = 0; i < n; i++) { + j = acc >> 16; + x = (acc & 0xffff) >> 8; + + for (off = 0; off < 4; off++) { + if (j - 1 >= 0 && j + 2 < src_width) { + y = vs_4tap_taps[x][0] * src[MAX ((j - 1) * 4 + off, 0)]; + y += vs_4tap_taps[x][1] * src[j * 4 + off]; + y += vs_4tap_taps[x][2] * src[(j + 1) * 4 + off]; + y += vs_4tap_taps[x][3] * src[(j + 2) * 4 + off]; + } else { + y = vs_4tap_taps[x][0] * src[CLAMP ((j - 1) * 4 + off, 0, + 4 * (src_width - 1) + off)]; + y += vs_4tap_taps[x][1] * src[CLAMP (j * 4 + off, 0, + 4 * (src_width - 1) + off)]; + y += vs_4tap_taps[x][2] * src[CLAMP ((j + 1) * 4 + off, 0, + 4 * (src_width - 1) + off)]; + y += vs_4tap_taps[x][3] * src[CLAMP ((j + 2) * 4 + off, 0, + 4 * (src_width - 1) + off)]; + } + y += (1 << (SHIFT - 1)); + dest[i * 4 + off] = CLAMP (y >> SHIFT, 0, 255); + } + acc += increment; + } + *xacc = acc; +} + +void +vs_scanline_merge_4tap_AYUV64 (uint16_t * dest, uint16_t * src1, + uint16_t * src2, uint16_t * src3, uint16_t * src4, int n, int acc) +{ + int i; + int y; + int off; + int a, b, c, d; + + acc = (acc >> 8) & 0xff; + a = vs_4tap_taps[acc][0]; + b = vs_4tap_taps[acc][1]; + c = vs_4tap_taps[acc][2]; + d = vs_4tap_taps[acc][3]; + for (i = 0; i < n; i++) { + for (off = 0; off < 4; off++) { + y = a * src1[i * 4 + off]; + y += b * src2[i * 4 + off]; + y += c * src3[i * 4 + off]; + y += d * src4[i * 4 + off]; + y += (1 << (SHIFT - 1)); + dest[i * 4 + off] = CLAMP (y >> SHIFT, 0, 65535); + } + } +} + +void +vs_image_scale_4tap_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf8) +{ + int yacc; + int y_increment; + int x_increment; + int i; + int j; + int xacc; + int k; + guint16 *tmpbuf = (guint16 *) tmpbuf8; + + if (dest->height == 1) + y_increment = 0; + else + y_increment = ((src->height - 1) << 16) / (dest->height - 1); + + if (dest->width == 1) + x_increment = 0; + else + x_increment = ((src->width - 1) << 16) / (dest->width - 1); + + k = 0; + for (i = 0; i < 4; i++) { + xacc = 0; + vs_scanline_resample_4tap_AYUV64 ((guint16 *) (tmpbuf + i * dest->stride), + (guint16 *) (src->pixels + i * src->stride), dest->width, src->width, + &xacc, x_increment); + } + + yacc = 0; + for (i = 0; i < dest->height; i++) { + uint16_t *t0, *t1, *t2, *t3; + + j = yacc >> 16; + + while (j > k) { + k++; + if (k + 3 < src->height) { + xacc = 0; + vs_scanline_resample_4tap_AYUV64 ((guint16 *) (tmpbuf + ((k + + 3) & 3) * dest->stride), + (guint16 *) (src->pixels + (k + 3) * src->stride), dest->width, + src->width, &xacc, x_increment); + } + } + + t0 = tmpbuf + (CLAMP (j - 1, 0, src->height - 1) & 3) * dest->stride; + t1 = tmpbuf + (CLAMP (j, 0, src->height - 1) & 3) * dest->stride; + t2 = tmpbuf + (CLAMP (j + 1, 0, src->height - 1) & 3) * dest->stride; + t3 = tmpbuf + (CLAMP (j + 2, 0, src->height - 1) & 3) * dest->stride; + vs_scanline_merge_4tap_AYUV64 ((guint16 *) (dest->pixels + + i * dest->stride), t0, t1, t2, t3, dest->width, yacc & 0xffff); + + yacc += y_increment; + } +} diff --git a/gst/videoscale/vs_4tap.h b/gst/videoscale/vs_4tap.h index ba1ffee7ee..6b51b52e5e 100644 --- a/gst/videoscale/vs_4tap.h +++ b/gst/videoscale/vs_4tap.h @@ -88,5 +88,12 @@ void vs_scanline_merge_4tap_Y16 (uint8_t *dest, uint8_t *src1, uint8_t *src2, void vs_image_scale_4tap_Y16 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf); +void vs_scanline_resample_4tap_AYUV64 (uint16_t *dest, uint16_t *src, + int n, int src_width, int *xacc, int increment); +void vs_scanline_merge_4tap_AYUV64 (uint16_t *dest, uint16_t *src1, uint16_t *src2, + uint16_t *src3, uint16_t *src4, int n, int acc); +void vs_image_scale_4tap_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf); + #endif diff --git a/gst/videoscale/vs_fill_borders.c b/gst/videoscale/vs_fill_borders.c index b796031dcd..160e137de1 100644 --- a/gst/videoscale/vs_fill_borders.c +++ b/gst/videoscale/vs_fill_borders.c @@ -379,3 +379,44 @@ vs_fill_borders_RGB555 (const VSImage * dest, const uint8_t * val) data += stride; } } + +void +vs_fill_borders_AYUV64 (const VSImage * dest, const uint8_t * val) +{ + int i; + int top = dest->border_top, bottom = dest->border_bottom; + int left = dest->border_left, right = dest->border_right; + int width = dest->width; + int height = dest->height; + int real_width = dest->real_width; + int stride = dest->stride; + int tmp, tmp2; + uint8_t *data; + uint64_t v; + + v = (val[0] << 8) | (val[1] << 24) | (((guint64) val[2]) << 40) | (((guint64) + val[3]) << 56); + + data = dest->real_pixels; + for (i = 0; i < top; i++) { + orc_splat_u64 ((uint64_t *) data, v, real_width); + data += stride; + } + + if (left || right) { + tmp = height; + tmp2 = (left + width) * 8; + for (i = 0; i < tmp; i++) { + orc_splat_u64 ((uint64_t *) data, v, left); + orc_splat_u64 ((uint64_t *) (data + tmp2), v, right); + data += stride; + } + } else { + data += stride * height; + } + + for (i = 0; i < bottom; i++) { + orc_splat_u64 ((uint64_t *) data, v, real_width); + data += stride; + } +} diff --git a/gst/videoscale/vs_fill_borders.h b/gst/videoscale/vs_fill_borders.h index 84d0be32cd..faf00db6c3 100644 --- a/gst/videoscale/vs_fill_borders.h +++ b/gst/videoscale/vs_fill_borders.h @@ -39,5 +39,6 @@ void vs_fill_borders_Y (const VSImage *dest, const uint8_t *val); void vs_fill_borders_Y16 (const VSImage *dest, const uint16_t val); void vs_fill_borders_RGB565 (const VSImage *dest, const uint8_t *val); void vs_fill_borders_RGB555 (const VSImage *dest, const uint8_t *val); +void vs_fill_borders_AYUV64 (const VSImage *dest, const uint8_t *val); #endif /* __VS_FILL_BORDERS_H__ */ diff --git a/gst/videoscale/vs_image.c b/gst/videoscale/vs_image.c index 1cc2211141..bff8ac767c 100644 --- a/gst/videoscale/vs_image.c +++ b/gst/videoscale/vs_image.c @@ -1047,3 +1047,121 @@ vs_image_scale_linear_RGB555 (const VSImage * dest, const VSImage * src, acc += y_increment; } } + +void +vs_image_scale_nearest_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf8) +{ + int acc; + int y_increment; + int x_increment; + int i; + int j; + int prev_j; + + if (dest->height == 1) + y_increment = 0; + else + y_increment = ((src->height - 1) << 16) / (dest->height - 1); + + if (dest->width == 1) + x_increment = 0; + else + x_increment = ((src->width - 1) << 16) / (dest->width - 1); + + + acc = 0; + prev_j = -1; + for (i = 0; i < dest->height; i++) { + j = acc >> 16; + + if (j == prev_j) { + memcpy (dest->pixels + i * dest->stride, + dest->pixels + (i - 1) * dest->stride, dest->width * 8); + } else { + int xacc = 0; + vs_scanline_resample_nearest_AYUV64 (dest->pixels + i * dest->stride, + src->pixels + j * src->stride, src->width, dest->width, &xacc, + x_increment); + } + + prev_j = j; + acc += y_increment; + } +} + +void +vs_image_scale_linear_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf) +{ + int acc; + int y_increment; + int x_increment; + int y1; + int y2; + int i; + int j; + int x; + int dest_size; + int xacc; + + if (dest->height == 1) + y_increment = 0; + else + y_increment = ((src->height - 1) << 16) / (dest->height - 1); + + if (dest->width == 1) + x_increment = 0; + else + x_increment = ((src->width - 1) << 16) / (dest->width - 1); + + dest_size = dest->width * 8; + +#undef LINE +#define LINE(x) ((guint16 *)((tmpbuf) + (dest_size)*((x)&1))) + + acc = 0; + y2 = -1; + //gst_videoscale_orc_resample_bilinear_u64 (LINE (0), src->pixels, + // 0, x_increment, dest->width); + xacc = 0; + vs_scanline_resample_linear_AYUV64 ((guint8 *) LINE (0), + src->pixels, src->width, dest->width, &xacc, x_increment); + y1 = 0; + for (i = 0; i < dest->height; i++) { + j = acc >> 16; + x = acc & 0xffff; + + if (x == 0) { + memcpy (dest->pixels + i * dest->stride, LINE (j), dest_size); + } else { + if (j > y1) { + xacc = 0; + vs_scanline_resample_linear_AYUV64 ((guint8 *) LINE (j), + src->pixels + j * src->stride, src->width, dest->width, &xacc, + x_increment); + //gst_videoscale_orc_resample_bilinear_u64 (LINE (j), + // src->pixels + j * src->stride, 0, x_increment, dest->width); + y1++; + } + if (j >= y1) { + xacc = 0; + vs_scanline_resample_linear_AYUV64 ((guint8 *) LINE (j + 1), + src->pixels + (j + 1) * src->stride, src->width, dest->width, &xacc, + x_increment); + orc_merge_linear_u16 ((guint16 *) (dest->pixels + i * dest->stride), + LINE (j), LINE (j + 1), 65536 - x, x, dest->width * 4); + //gst_videoscale_orc_resample_merge_bilinear_u64 (dest->pixels + + // i * dest->stride, LINE (j + 1), LINE (j), + // src->pixels + (j + 1) * src->stride, (x >> 8), 0, x_increment, + // dest->width); + y1++; + } else { + orc_merge_linear_u16 ((guint16 *) (dest->pixels + i * dest->stride), + LINE (j), LINE (j + 1), 65536 - x, x, dest->width * 4); + } + } + + acc += y_increment; + } +} diff --git a/gst/videoscale/vs_image.h b/gst/videoscale/vs_image.h index 23f12805c9..3a23dd44f5 100644 --- a/gst/videoscale/vs_image.h +++ b/gst/videoscale/vs_image.h @@ -84,5 +84,15 @@ void vs_image_scale_nearest_Y16 (const VSImage *dest, const VSImage *src, void vs_image_scale_linear_Y16 (const VSImage *dest, const VSImage *src, uint8_t *tmpbuf); +void vs_image_scale_nearest_AYUV16 (const VSImage *dest, const VSImage *src, + uint8_t *tmpbuf); +void vs_image_scale_linear_AYUV16 (const VSImage *dest, const VSImage *src, + uint8_t *tmpbuf); + +void vs_image_scale_nearest_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf8); +void vs_image_scale_linear_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf8); + #endif diff --git a/gst/videoscale/vs_scanline.c b/gst/videoscale/vs_scanline.c index cfe02b63a9..822a6b6600 100644 --- a/gst/videoscale/vs_scanline.c +++ b/gst/videoscale/vs_scanline.c @@ -713,3 +713,69 @@ vs_scanline_merge_linear_RGB555 (uint8_t * dest_u8, uint8_t * src1_u8, (RGB555_B (src1[i]) * (65536 - x) + RGB555_B (src2[i]) * x) >> 16); } } + +void +vs_scanline_resample_nearest_AYUV64 (uint8_t * dest8, uint8_t * src8, + int src_width, int n, int *accumulator, int increment) +{ + guint16 *dest = (guint16 *) dest8; + guint16 *src = (guint16 *) src8; + int acc = *accumulator; + int i; + int j; + int x; + + for (i = 0; i < n; i++) { + j = acc >> 16; + x = acc & 0xffff; + dest[i * 4 + 0] = (x < 32768 + || j + 1 >= src_width) ? src[j * 4 + 0] : src[j * 4 + 4]; + dest[i * 4 + 1] = (x < 32768 + || j + 1 >= src_width) ? src[j * 4 + 1] : src[j * 4 + 5]; + dest[i * 4 + 2] = (x < 32768 + || j + 1 >= src_width) ? src[j * 4 + 2] : src[j * 4 + 6]; + dest[i * 4 + 3] = (x < 32768 + || j + 1 >= src_width) ? src[j * 4 + 3] : src[j * 4 + 7]; + + acc += increment; + } + + *accumulator = acc; +} + +void +vs_scanline_resample_linear_AYUV64 (uint8_t * dest8, uint8_t * src8, + int src_width, int n, int *accumulator, int increment) +{ + guint16 *dest = (guint16 *) dest8; + guint16 *src = (guint16 *) src8; + int acc = *accumulator; + int i; + int j; + int x; + + for (i = 0; i < n; i++) { + j = acc >> 16; + x = (acc & 0xffff) >> 1; + + if (j + 1 < src_width) { + dest[i * 4 + 0] = + (src[j * 3 + 0] * (32768 - x) + src[j * 4 + 4] * x) >> 15; + dest[i * 4 + 1] = + (src[j * 4 + 1] * (32768 - x) + src[j * 4 + 5] * x) >> 15; + dest[i * 4 + 2] = + (src[j * 4 + 2] * (32768 - x) + src[j * 4 + 6] * x) >> 15; + dest[i * 4 + 3] = + (src[j * 4 + 3] * (32768 - x) + src[j * 4 + 7] * x) >> 15; + } else { + dest[i * 4 + 0] = src[j * 4 + 0]; + dest[i * 4 + 1] = src[j * 4 + 1]; + dest[i * 4 + 2] = src[j * 4 + 2]; + dest[i * 4 + 3] = src[j * 4 + 3]; + } + + acc += increment; + } + + *accumulator = acc; +} diff --git a/gst/videoscale/vs_scanline.h b/gst/videoscale/vs_scanline.h index e3589696c3..387fc9511b 100644 --- a/gst/videoscale/vs_scanline.h +++ b/gst/videoscale/vs_scanline.h @@ -70,5 +70,10 @@ void vs_scanline_resample_nearest_Y16 (uint8_t *dest, uint8_t *src, int n, int s void vs_scanline_resample_linear_Y16 (uint8_t *dest, uint8_t *src, int n, int src_width, int *accumulator, int increment); void vs_scanline_merge_linear_Y16 (uint8_t *dest, uint8_t *src1, uint8_t *src2, int n, int x); +void vs_scanline_resample_nearest_AYUV64 (uint8_t * dest, uint8_t * src, + int src_width, int n, int *accumulator, int increment); +void vs_scanline_resample_linear_AYUV64 (uint8_t * dest, uint8_t * src, + int src_width, int n, int *accumulator, int increment); + #endif diff --git a/gst/videotestsrc/generate_sine_table.c b/gst/videotestsrc/generate_sine_table.c index 4d8e571505..432e1a56f9 100644 --- a/gst/videotestsrc/generate_sine_table.c +++ b/gst/videotestsrc/generate_sine_table.c @@ -39,4 +39,5 @@ main (int argc, char *argv[]) } printf ("};\n"); + return 0; } diff --git a/gst/videotestsrc/videotestsrc.c b/gst/videotestsrc/videotestsrc.c index c06b35369e..99735075d2 100644 --- a/gst/videotestsrc/videotestsrc.c +++ b/gst/videotestsrc/videotestsrc.c @@ -32,6 +32,9 @@ #include #include +#define TO_16(x) (((x)<<8) | (x)) +#define TO_10(x) (((x)<<2) | ((x)>>6)) + static void paint_tmpline_ARGB (paintinfo * p, int x, int w); static void paint_tmpline_AYUV (paintinfo * p, int x, int w); @@ -144,6 +147,7 @@ static void paint_setup_v410 (paintinfo * p, unsigned char *dest); static void paint_setup_v216 (paintinfo * p, unsigned char *dest); static void paint_setup_v210 (paintinfo * p, unsigned char *dest); static void paint_setup_UYVP (paintinfo * p, unsigned char *dest); +static void paint_setup_AY64 (paintinfo * p, unsigned char *dest); static void paint_setup_YUV9 (paintinfo * p, unsigned char *dest); static void paint_setup_YVU9 (paintinfo * p, unsigned char *dest); @@ -159,6 +163,7 @@ static void paint_setup_RGB888 (paintinfo * p, unsigned char *dest); static void paint_setup_BGR888 (paintinfo * p, unsigned char *dest); static void paint_setup_RGB565 (paintinfo * p, unsigned char *dest); static void paint_setup_xRGB1555 (paintinfo * p, unsigned char *dest); +static void paint_setup_ARGB64 (paintinfo * p, unsigned char *dest); static void paint_setup_bayer_bggr (paintinfo * p, unsigned char *dest); static void paint_setup_bayer_rggb (paintinfo * p, unsigned char *dest); @@ -184,9 +189,11 @@ static void convert_hline_v410 (paintinfo * p, int y); static void convert_hline_v216 (paintinfo * p, int y); static void convert_hline_v210 (paintinfo * p, int y); static void convert_hline_UYVP (paintinfo * p, int y); +static void convert_hline_AY64 (paintinfo * p, int y); static void convert_hline_YUV9 (paintinfo * p, int y); static void convert_hline_astr4 (paintinfo * p, int y); +static void convert_hline_astr8 (paintinfo * p, int y); static void convert_hline_str4 (paintinfo * p, int y); static void convert_hline_str3 (paintinfo * p, int y); static void convert_hline_RGB565 (paintinfo * p, int y); @@ -216,6 +223,7 @@ struct fourcc_list_struct fourcc_list[] = { {VTS_YUV, "v210", "v210", 21, paint_setup_v210, convert_hline_v210}, {VTS_YUV, "v216", "v216", 32, paint_setup_v216, convert_hline_v216}, {VTS_YUV, "UYVP", "UYVP", 20, paint_setup_UYVP, convert_hline_UYVP}, + {VTS_YUV, "AY64", "AY64", 64, paint_setup_AY64, convert_hline_AY64}, #ifdef disabled {VTS_YUV, "IYU2", "IYU2", 24, paint_setup_IYU2, convert_hline_IYU2}, @@ -283,6 +291,9 @@ struct fourcc_list_struct fourcc_list[] = { convert_hline_xRGB1555, 15, 0x00007c00, 0x000003e0, 0x0000001f}, + {VTS_RGB, "RGB ", "ARGB8888", 64, paint_setup_ARGB64, convert_hline_astr8, + 64, + 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}, {VTS_BAYER, "bggr", "Bayer", 8, paint_setup_bayer_bggr, convert_hline_bayer}, {VTS_BAYER, "rggb", "Bayer", 8, paint_setup_bayer_rggb, convert_hline_bayer}, @@ -1838,6 +1849,19 @@ paint_setup_YVYU (paintinfo * p, unsigned char *dest) p->endptr = dest + p->ystride * p->height; } +static void +paint_setup_AY64 (paintinfo * p, unsigned char *dest) +{ + p->ap = dest; + p->yp = dest + 2; + p->up = dest + 4; + p->vp = dest + 6; + p->ystride = p->width * 8; + p->ustride = p->width * 8; + p->vstride = p->width * 8; + p->endptr = dest + p->ystride * p->height; +} + static void convert_hline_v308 (paintinfo * p, int y) { @@ -1872,9 +1896,6 @@ convert_hline_AYUV (paintinfo * p, int y) } } -#define TO_16(x) (((x)<<8) | (x)) -#define TO_10(x) (((x)<<2) | ((x)>>6)) - static void convert_hline_v216 (paintinfo * p, int y) { @@ -2005,6 +2026,21 @@ convert_hline_YUY2 (paintinfo * p, int y) } } +static void +convert_hline_AY64 (paintinfo * p, int y) +{ + int i; + guint16 *ayuv16 = (guint16 *) (p->ap + y * p->ystride); + guint8 *ayuv = p->tmpline; + + for (i = 0; i < p->width; i++) { + GST_WRITE_UINT16_LE (ayuv16 + i * 4 + 0, TO_16 (ayuv[4 * i + 0])); + GST_WRITE_UINT16_LE (ayuv16 + i * 4 + 1, TO_16 (ayuv[4 * i + 1])); + GST_WRITE_UINT16_LE (ayuv16 + i * 4 + 2, TO_16 (ayuv[4 * i + 2])); + GST_WRITE_UINT16_LE (ayuv16 + i * 4 + 3, TO_16 (ayuv[4 * i + 3])); + } +} + #ifdef disabled static void paint_setup_IYU2 (paintinfo * p, unsigned char *dest) @@ -2294,6 +2330,19 @@ paint_setup_BGR888 (paintinfo * p, unsigned char *dest) p->endptr = p->dest + p->ystride * p->height; } +static void +paint_setup_ARGB64 (paintinfo * p, unsigned char *dest) +{ + p->yp = dest + 2; + p->up = dest + 4; + p->vp = dest + 6; + p->ap = dest; + p->ystride = p->width * 8; + p->ustride = p->width * 8; + p->vstride = p->width * 8; + p->endptr = p->dest + p->ystride * p->height; +} + static void convert_hline_str4 (paintinfo * p, int y) { @@ -2330,6 +2379,24 @@ convert_hline_astr4 (paintinfo * p, int y) } } +static void +convert_hline_astr8 (paintinfo * p, int y) +{ + int i; + guint16 *A = (guint16 *) (p->ap + y * p->ystride); + guint16 *R = (guint16 *) (p->yp + y * p->ystride); + guint16 *G = (guint16 *) (p->up + y * p->ustride); + guint16 *B = (guint16 *) (p->vp + y * p->vstride); + guint8 *argb = p->tmpline; + + for (i = 0; i < p->width; i++) { + A[4 * i] = TO_16 (argb[4 * i + 0]); + R[4 * i] = TO_16 (argb[4 * i + 1]); + G[4 * i] = TO_16 (argb[4 * i + 2]); + B[4 * i] = TO_16 (argb[4 * i + 3]); + } +} + static void convert_hline_str3 (paintinfo * p, int y) { diff --git a/pkgconfig/gstreamer-app-uninstalled.pc.in b/pkgconfig/gstreamer-app-uninstalled.pc.in index 015bedcd8b..b989798ee7 100644 --- a/pkgconfig/gstreamer-app-uninstalled.pc.in +++ b/pkgconfig/gstreamer-app-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/app +typelibdir=@abs_top_builddir@/gst-libs/gst/app Name: GStreamer Application Library, Uninstalled Description: Helper functions and base classes for application integration, uninstalled diff --git a/pkgconfig/gstreamer-app.pc.in b/pkgconfig/gstreamer-app.pc.in index ef3d27c38b..8152effe5f 100644 --- a/pkgconfig/gstreamer-app.pc.in +++ b/pkgconfig/gstreamer-app.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Application Library Description: Helper functions and base classes for application integration diff --git a/pkgconfig/gstreamer-audio-uninstalled.pc.in b/pkgconfig/gstreamer-audio-uninstalled.pc.in index da927d3fef..ce55cf94bc 100644 --- a/pkgconfig/gstreamer-audio-uninstalled.pc.in +++ b/pkgconfig/gstreamer-audio-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/audio +typelibdir=@abs_top_builddir@/gst-libs/gst/audio Name: GStreamer Audio Library, Uninstalled Description: Audio helper functions and base classes, uninstalled diff --git a/pkgconfig/gstreamer-audio.pc.in b/pkgconfig/gstreamer-audio.pc.in index 623638144f..5743ccf959 100644 --- a/pkgconfig/gstreamer-audio.pc.in +++ b/pkgconfig/gstreamer-audio.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Audio library Description: Audio helper functions and base classes diff --git a/pkgconfig/gstreamer-cdda-uninstalled.pc.in b/pkgconfig/gstreamer-cdda-uninstalled.pc.in index b3fa27d003..15773e8de3 100644 --- a/pkgconfig/gstreamer-cdda-uninstalled.pc.in +++ b/pkgconfig/gstreamer-cdda-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/cdda +typelibdir=@abs_top_builddir@/gst-libs/gst/cdda Name: GStreamer CDDA Library, Uninstalled Description: CDDA base classes, uninstalled diff --git a/pkgconfig/gstreamer-cdda.pc.in b/pkgconfig/gstreamer-cdda.pc.in index b67eac169f..86f4a2bf0b 100644 --- a/pkgconfig/gstreamer-cdda.pc.in +++ b/pkgconfig/gstreamer-cdda.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer CDDA Library Description: CDDA base classes diff --git a/pkgconfig/gstreamer-fft-uninstalled.pc.in b/pkgconfig/gstreamer-fft-uninstalled.pc.in index 67b0fff9d7..163f3f0653 100644 --- a/pkgconfig/gstreamer-fft-uninstalled.pc.in +++ b/pkgconfig/gstreamer-fft-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/fft +typelibdir=@abs_top_builddir@/gst-libs/gst/fft Name: GStreamer FFT Library, Uninstalled Description: FFT implementation, uninstalled diff --git a/pkgconfig/gstreamer-fft.pc.in b/pkgconfig/gstreamer-fft.pc.in index 20ffdd667f..5680a0c6df 100644 --- a/pkgconfig/gstreamer-fft.pc.in +++ b/pkgconfig/gstreamer-fft.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer FFT Library Description: FFT implementation diff --git a/pkgconfig/gstreamer-floatcast.pc.in b/pkgconfig/gstreamer-floatcast.pc.in index 1714eaafa6..c54b098919 100644 --- a/pkgconfig/gstreamer-floatcast.pc.in +++ b/pkgconfig/gstreamer-floatcast.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Floatcast Library Description: Platform independent floating point macros diff --git a/pkgconfig/gstreamer-interfaces-uninstalled.pc.in b/pkgconfig/gstreamer-interfaces-uninstalled.pc.in index 90dd7405a5..676a23fbf0 100644 --- a/pkgconfig/gstreamer-interfaces-uninstalled.pc.in +++ b/pkgconfig/gstreamer-interfaces-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/interfaces +typelibdir=@abs_top_builddir@/gst-libs/gst/interfaces Name: GStreamer Interfaces Library, Uninstalled Description: Interfaces for GStreamer elements, uninstalled diff --git a/pkgconfig/gstreamer-interfaces.pc.in b/pkgconfig/gstreamer-interfaces.pc.in index 30828e4b4e..24749be89f 100644 --- a/pkgconfig/gstreamer-interfaces.pc.in +++ b/pkgconfig/gstreamer-interfaces.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Interfaces Library Description: Interfaces for GStreamer elements diff --git a/pkgconfig/gstreamer-netbuffer-uninstalled.pc.in b/pkgconfig/gstreamer-netbuffer-uninstalled.pc.in index ecf84d026c..941c728ed1 100644 --- a/pkgconfig/gstreamer-netbuffer-uninstalled.pc.in +++ b/pkgconfig/gstreamer-netbuffer-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/netbuffer +typelibdir=@abs_top_builddir@/gst-libs/gst/netbuffer Name: GStreamer Network Buffer Library, Uninstalled Description: Network buffer for use in network sources/sinks, uninstalled diff --git a/pkgconfig/gstreamer-netbuffer.pc.in b/pkgconfig/gstreamer-netbuffer.pc.in index 44809fb61a..28d7ed27f2 100644 --- a/pkgconfig/gstreamer-netbuffer.pc.in +++ b/pkgconfig/gstreamer-netbuffer.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Network Buffer Library Description: Network buffer for use in network sources/sinks diff --git a/pkgconfig/gstreamer-pbutils-uninstalled.pc.in b/pkgconfig/gstreamer-pbutils-uninstalled.pc.in index 57258328a1..e08758c979 100644 --- a/pkgconfig/gstreamer-pbutils-uninstalled.pc.in +++ b/pkgconfig/gstreamer-pbutils-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/pbutils +typelibdir=@abs_top_builddir@/gst-libs/gst/pbutils Name: GStreamer Base Utils Library, Uninstalled Description: General utility functions, uninstalled diff --git a/pkgconfig/gstreamer-pbutils.pc.in b/pkgconfig/gstreamer-pbutils.pc.in index 59954240bf..e1626a454c 100644 --- a/pkgconfig/gstreamer-pbutils.pc.in +++ b/pkgconfig/gstreamer-pbutils.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Base Utils Library Description: General utility functions diff --git a/pkgconfig/gstreamer-riff-uninstalled.pc.in b/pkgconfig/gstreamer-riff-uninstalled.pc.in index 490c0b02e4..2592ba14f9 100644 --- a/pkgconfig/gstreamer-riff-uninstalled.pc.in +++ b/pkgconfig/gstreamer-riff-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/riff +typelibdir=@abs_top_builddir@/gst-libs/gst/riff Name: GStreamer RIFF Library, Uninstalled Description: RIFF helper functions, uninstalled diff --git a/pkgconfig/gstreamer-riff.pc.in b/pkgconfig/gstreamer-riff.pc.in index 054d417ef9..0858276bf3 100644 --- a/pkgconfig/gstreamer-riff.pc.in +++ b/pkgconfig/gstreamer-riff.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer RIFF Library Description: RIFF helper functions diff --git a/pkgconfig/gstreamer-rtp-uninstalled.pc.in b/pkgconfig/gstreamer-rtp-uninstalled.pc.in index f976829b88..96c66cd741 100644 --- a/pkgconfig/gstreamer-rtp-uninstalled.pc.in +++ b/pkgconfig/gstreamer-rtp-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/rtp +typelibdir=@abs_top_builddir@/gst-libs/gst/rtp Name: GStreamer RTP Library, Uninstalled Description: RTP base classes and helper functions, uninstalled diff --git a/pkgconfig/gstreamer-rtp.pc.in b/pkgconfig/gstreamer-rtp.pc.in index c7a3b15dd9..ff74a9d126 100644 --- a/pkgconfig/gstreamer-rtp.pc.in +++ b/pkgconfig/gstreamer-rtp.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer RTP Library Description: RTP base classes and helper functions diff --git a/pkgconfig/gstreamer-rtsp-uninstalled.pc.in b/pkgconfig/gstreamer-rtsp-uninstalled.pc.in index 3bf86b1716..53258ec098 100644 --- a/pkgconfig/gstreamer-rtsp-uninstalled.pc.in +++ b/pkgconfig/gstreamer-rtsp-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/rtsp +typelibdir=@abs_top_builddir@/gst-libs/gst/rtsp Name: GStreamer RTSP Library, Uninstalled Description: RTSP base classes and helper functions, uninstalled diff --git a/pkgconfig/gstreamer-rtsp.pc.in b/pkgconfig/gstreamer-rtsp.pc.in index b2ca7cf63e..203e4002cb 100644 --- a/pkgconfig/gstreamer-rtsp.pc.in +++ b/pkgconfig/gstreamer-rtsp.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer RTSP Library Description: RTSP base classes and helper functions diff --git a/pkgconfig/gstreamer-sdp-uninstalled.pc.in b/pkgconfig/gstreamer-sdp-uninstalled.pc.in index d4591b5837..bd1d89ddb2 100644 --- a/pkgconfig/gstreamer-sdp-uninstalled.pc.in +++ b/pkgconfig/gstreamer-sdp-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/sdp +typelibdir=@abs_top_builddir@/gst-libs/gst/sdp Name: GStreamer SDP Library, Uninstalled Description: SDP helper functions, uninstalled diff --git a/pkgconfig/gstreamer-sdp.pc.in b/pkgconfig/gstreamer-sdp.pc.in index bf9c56acc4..9bb0f4044d 100644 --- a/pkgconfig/gstreamer-sdp.pc.in +++ b/pkgconfig/gstreamer-sdp.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer SDP Library Description: SDP helper functions diff --git a/pkgconfig/gstreamer-tag-uninstalled.pc.in b/pkgconfig/gstreamer-tag-uninstalled.pc.in index 5f2e791157..b953a4c1a7 100644 --- a/pkgconfig/gstreamer-tag-uninstalled.pc.in +++ b/pkgconfig/gstreamer-tag-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/tag +typelibdir=@abs_top_builddir@/gst-libs/gst/tag Name: GStreamer Tag Library, Uninstalled Description: Tag base classes and helper functions, uninstalled diff --git a/pkgconfig/gstreamer-tag.pc.in b/pkgconfig/gstreamer-tag.pc.in index 33c27b542e..a7a564c3b4 100644 --- a/pkgconfig/gstreamer-tag.pc.in +++ b/pkgconfig/gstreamer-tag.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Tag Library Description: Tag base classes and helper functions diff --git a/pkgconfig/gstreamer-video-uninstalled.pc.in b/pkgconfig/gstreamer-video-uninstalled.pc.in index 63792967b6..450be2bc2d 100644 --- a/pkgconfig/gstreamer-video-uninstalled.pc.in +++ b/pkgconfig/gstreamer-video-uninstalled.pc.in @@ -4,6 +4,8 @@ exec_prefix= libdir= # includedir is builddir because it is used to find gstconfig.h in places includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/video +typelibdir=@abs_top_builddir@/gst-libs/gst/video Name: GStreamer Video Library, Uninstalled Description: Video base classes and helper functions, uninstalled diff --git a/pkgconfig/gstreamer-video.pc.in b/pkgconfig/gstreamer-video.pc.in index cdddeb31b8..59a217d74e 100644 --- a/pkgconfig/gstreamer-video.pc.in +++ b/pkgconfig/gstreamer-video.pc.in @@ -2,6 +2,10 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 Name: GStreamer Video Library Description: Video base classes and helper functions diff --git a/po/da.po b/po/da.po index 23d1fa013f..1a4278e44b 100644 --- a/po/da.po +++ b/po/da.po @@ -3,16 +3,38 @@ # This file is distributed under the same license as the gst-plugins-base package. # # Mogens Jaeger , 2007. -# Joe Hansen , 2009, 2010. +# Joe Hansen , 2009, 2010, 2011. # # capture -> optage +# gain -> forstærkning # shutter -> lukketid +# +# Hej Torben, de fleste af dem her er vist afklaringen af hvorvidt det er et +# udsagnsord eller navneord. Har du været i koden og kigge? Ville godt nok +# være ked af at rette alle dem her, for så bare at »opdage« at de var gode nok, +# Nu har jeg haft src/gst-plugins-base/tags.c åben i en nylig version +# (gst-blugins-base version 0.10.31), og jeg tror ikke strengene bruges, +# mens der sker en aktiv handling. +# I filen ser det således ud: +# /* photography tags */ +# gst_tag_register (GST_TAG_CAPTURING_SHUTTER_SPEED, +# GST_TAG_FLAG_META, +# GST_TYPE_FRACTION, +# _("capturing shutter speed"), +# _("Shutter speed used when capturing an image, +# in seconds"), NULL); +# Dette kan du se som en indgang i en tabel over strenge. Den første korte +# streng er navnet på mærket, den anden er en beskrivelse af samme mærke. +# Strengene er altså betegnelser/navneord. +# +# brugt kunne erstattes med der blev brugt (men skal så rettes igennem for alle). +# msgid "" msgstr "" -"Project-Id-Version: gst-plugins-base 0.10.30.3\n" +"Project-Id-Version: gst-plugins-base 0.10.31.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-11-01 16:36+0000\n" -"PO-Revision-Date: 2010-10-27 16:38+0100\n" +"POT-Creation-Date: 2011-01-11 18:58+0000\n" +"PO-Revision-Date: 2011-01-07 16:38+0100\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish \n" "Language: da\n" @@ -292,108 +314,119 @@ msgstr "spor TRM-id" msgid "MusicBrainz TRM ID" msgstr "MusicBrainz TRM-id" +# nb ikke handling men navneord. +# "optagelukkertid" / "optagerlukkertid" +# Mogens: shutter speed = lukkerhastighed msgid "capturing shutter speed" -msgstr "optager hastighed for lukketid" +msgstr "optagerlukkertid" msgid "Shutter speed used when capturing an image, in seconds" -msgstr "Lukketidshastighed brug når et billede optages, i sekunder" +msgstr "Lukketidshastighed brugt når et billede optages, i sekunder" msgid "capturing focal ratio" -msgstr "optager brændforhold" +msgstr "brændforhold for optager" msgid "Focal ratio (f-number) used when capturing the image" msgstr "Brændforhold (f-nummer) brugt under optagelse af billedet" msgid "capturing focal length" -msgstr "optager brændvidde" +msgstr "brændvidde for optager" msgid "Focal length of the lens used capturing the image, in mm" -msgstr "Brændvidde på linser brugt under optagelse af billedet, i mm" +msgstr "Brændvidde på linse brugt under optagelse af billedet, i mm" msgid "capturing digital zoom ratio" -msgstr "optager digitalt zoomforhold" +msgstr "digitalt zoomforhold for optager" msgid "Digital zoom ratio used when capturing an image" msgstr "Digitalt zoomforhold brugt under optagelse af et billede" msgid "capturing iso speed" -msgstr "optager iso-hastighed" +msgstr "iso-hastighed for optager" msgid "The ISO speed used when capturing an image" msgstr "ISO-hastigheden brugt under optagelse af et billede" msgid "capturing exposure program" -msgstr "optager belysningsprogram" +msgstr "belysningsprogram for optagelse" msgid "The exposure program used when capturing an image" msgstr "Belysningsprogrammet brugt under optagelse af et billede" msgid "capturing exposure mode" -msgstr "optager belysningstilstand" +msgstr "belysningstilstand for optagelse" msgid "The exposure mode used when capturing an image" msgstr "Belysningstilstanden brugt under optagelse af et billede" +# "optagelsestype for optagelse af scene" / +# "optagelsestype for sceneoptagelse" / +# "sceneoptagelsestype for optager" / +# "optageroptagelsestype for scene" +# De to første rammer vist forlægget mest præcist. msgid "capturing scene capture type" -msgstr "optager optagelsestype for scene" +msgstr "optagelsestype for optagelse af scene" msgid "The scene capture mode used when capturing an image" -msgstr "Optagelsestilstanden for scene under optagelse af et billede" +msgstr "Sceneoptagelsestilstanden brugt da billedet blev optaget" +# "justering af optageforstærkning" / +# "justering af optagerforstærkning" msgid "capturing gain adjustment" -msgstr "optager forøgelsesjustering" +msgstr "justering af optageforstærkning" msgid "The overall gain adjustment applied on an image" -msgstr "Den samlede forøgelsesjustering brugt på et billede" +msgstr "Den samlede optageforstærkning brugt på et billede" msgid "capturing white balance" -msgstr "optager hvidbalance" +msgstr "hvidbalance for optagelse" msgid "The white balance mode set when capturing an image" msgstr "Hvidbalancetilstanden angivet under optagelse af et billede" +# kontrast for optagelse msgid "capturing contrast" -msgstr "optager kontrast" +msgstr "optagekontrast" +# Jeg tror "direction" skal oversættes til "indstilling af" +# (sml. "director" ~ "instruction" ~ "instruering" ~ "indstilling" msgid "The direction of contrast processing applied when capturing an image" -msgstr "Retningen på kontrastprocessen brugt under optagelse af et billede" +msgstr "Indstilling af kontrastprocessen brugt under optagelse af et billede" msgid "capturing saturation" -msgstr "optager farvemætning" +msgstr "farvemætning for optagelse" msgid "The direction of saturation processing applied when capturing an image" msgstr "" -"Retningen på farvemætningsprocessen brugt under optagelse af et billede" +"Indstillingen af farvemætningsprocessen brugt under optagelse af et billede" msgid "capturing sharpness" -msgstr "optager skarphed" +msgstr "optageskarphed" msgid "The direction of sharpness processing applied when capturing an image" -msgstr "Retningen på skarphedsprocessen brugt under optagelse af et billede" +msgstr "skarphedindstilling brugt under optagelse af et billede" msgid "capturing flash fired" -msgstr "optager blitzen der blev brugt" +msgstr "optagerblitzen der blev brugt" msgid "If the flash fired while capturing an image" msgstr "Hvorvidt blitzen blev brugt under optagelse af et billede" msgid "capturing flash mode" -msgstr "optager blitztilstand" +msgstr "optagererens blitztilstand" msgid "The selected flash mode while capturing an image" msgstr "Den valgte blitztilstand under optagelse af et billede" msgid "capturing metering mode" -msgstr "optager måletilstanden" +msgstr "optagerens måletilstand" msgid "" "The metering mode used while determining exposure for capturing an image" -msgstr "" -"Måletilstanden brugt under bestemmelse af belysning under optagelse af et " -"billede" +msgstr "Måletilstanden brugt da optagelsens belysning skulle bestemmes" msgid "capturing source" -msgstr "optager kilde" +msgstr "optagekilde" msgid "The source or type of device used for the capture" msgstr "Kilden eller typen af enhed brugt under optagelsen" @@ -608,38 +641,3 @@ msgstr "Enheden \"%s\" er allerede i brug." #, c-format msgid "Could not open device \"%s\" for reading and writing." msgstr "Kunne ikke tilgå enheden \"%s\"." - -#~ msgid "Can't display both text subtitles and subpictures." -#~ msgstr "Kan ikke vise både undertekster og underbilleder." - -#~ msgid "No Temp directory specified." -#~ msgstr "Ingen midlertidig mappe angivet." - -#~ msgid "Could not create temp file \"%s\"." -#~ msgstr "Kunne ikke oprette midlertidig fil \"%s\"." - -#~ msgid "Could not open file \"%s\" for reading." -#~ msgstr "Kunne ikke åbne fil \"%s\" til læsning." - -#~ msgid "Internal data flow error." -#~ msgstr "Intern datastrømsfejl." - -# eller oprette (evt. sammensat). -# beholdt danne og ikke sammensat da tillægsord. -#~ msgid "Could not create \"decodebin2\" element." -#~ msgstr "Kunne ikke danne et \"decodebin2\" element." - -#~ msgid "Could not create \"queue2\" element." -#~ msgstr "Kunne ikke danne et \"queue2\" element." - -#~ msgid "Could not create \"typefind\" element." -#~ msgstr "Kunne ikke danne et \"typefind\" element." - -#~ msgid "No file name specified." -#~ msgstr "Intet filnavn angivet." - -#~ msgid "artist sortname" -#~ msgstr "kunstner sorteringsnavn" - -#~ msgid "MusicBrainz artist sortname" -#~ msgstr "MusicBrainz kunstner sorteringsnavn" diff --git a/po/fi.po b/po/fi.po index e96c76bafa..7819735de1 100644 --- a/po/fi.po +++ b/po/fi.po @@ -10,10 +10,10 @@ # Suomennos: http://gnome.fi/ msgid "" msgstr "" -"Project-Id-Version: gst-plugins-base 0.10.28.2\n" +"Project-Id-Version: gst-plugins-base 0.10.30.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-10-16 01:03+0100\n" -"PO-Revision-Date: 2010-04-26 21:15+0300\n" +"POT-Creation-Date: 2011-01-06 20:42+0000\n" +"PO-Revision-Date: 2010-12-31 23:21+0200\n" "Last-Translator: Tommi Vainikainen \n" "Language-Team: Finnish \n" "Language: fi\n" @@ -21,7 +21,6 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: KBabel 1.11.2\n" msgid "Master" msgstr "Pääkanava" @@ -292,119 +291,119 @@ msgid "MusicBrainz TRM ID" msgstr "MusicBrainz TRM-tunniste" msgid "capturing shutter speed" -msgstr "" +msgstr "kuvaamisen suljinnopeus" msgid "Shutter speed used when capturing an image, in seconds" -msgstr "" +msgstr "Suljinnopeus kuvattaessa, sekunteina" msgid "capturing focal ratio" -msgstr "" +msgstr "kuvaamisen aukkosuhde" msgid "Focal ratio (f-number) used when capturing the image" -msgstr "" +msgstr "Aukkosuhde (f-luku) kuvattaessa" msgid "capturing focal length" -msgstr "" +msgstr "kuvaamisen polttoväli" msgid "Focal length of the lens used capturing the image, in mm" -msgstr "" +msgstr "Linssin polttoväli kuvattaessa, millimetriä" msgid "capturing digital zoom ratio" -msgstr "" +msgstr "kuvaamisen digitaalinen zoomaus" msgid "Digital zoom ratio used when capturing an image" -msgstr "" +msgstr "Digitaalinen suurennuskerroin kuvattaessa" msgid "capturing iso speed" -msgstr "" +msgstr "kuvaamisen ISO-nopeus" msgid "The ISO speed used when capturing an image" -msgstr "" +msgstr "ISO-nopeus kuvattaessa" msgid "capturing exposure program" -msgstr "" +msgstr "kuvaamisen valotusohjelma" msgid "The exposure program used when capturing an image" -msgstr "" +msgstr "Valotusohjelma kuvattaessa" msgid "capturing exposure mode" -msgstr "" +msgstr "kuvaamisen valotustapa" msgid "The exposure mode used when capturing an image" -msgstr "" +msgstr "Valotustapa kuvattaessa" msgid "capturing scene capture type" -msgstr "" +msgstr "kuvaamisen kuvausmoodi" msgid "The scene capture mode used when capturing an image" -msgstr "" +msgstr "Kuvausmoodi kuvattaessa" msgid "capturing gain adjustment" -msgstr "" +msgstr "kuvaamisen kirkkauskorjaus" msgid "The overall gain adjustment applied on an image" -msgstr "" +msgstr "Yleinen kirkkauskorjaus, joka kuvalle on tehty" msgid "capturing white balance" -msgstr "" +msgstr "kuvaamisen valkotasapaino" msgid "The white balance mode set when capturing an image" -msgstr "" +msgstr "Valittu valkotasapainotila kuvattaessa" msgid "capturing contrast" -msgstr "" +msgstr "kuvaamisen kontrasti" msgid "The direction of contrast processing applied when capturing an image" -msgstr "" +msgstr "Käytetty kontrastikäsittelyn suunta kuvattessa" msgid "capturing saturation" -msgstr "" +msgstr "kuvaamisen saturaatio" msgid "The direction of saturation processing applied when capturing an image" -msgstr "" +msgstr "Käytetty saturaatiokäsittelyn suunta kuvattessa" msgid "capturing sharpness" -msgstr "" +msgstr "kuvaamisen terävyys" msgid "The direction of sharpness processing applied when capturing an image" -msgstr "" +msgstr "Käytetty terävyyskäsittelyn suunta kuvattessa" msgid "capturing flash fired" -msgstr "" +msgstr "kuvaamisen salamankäyttö" msgid "If the flash fired while capturing an image" -msgstr "" +msgstr "Syttyikö salama kuvattaessa" msgid "capturing flash mode" -msgstr "" +msgstr "kuvaamisen salamamoodi" msgid "The selected flash mode while capturing an image" -msgstr "" +msgstr "Valittu salamamoodi kuvattessa" msgid "capturing metering mode" -msgstr "" +msgstr "kuvaamisen mittausmoodi" msgid "" "The metering mode used while determining exposure for capturing an image" -msgstr "" +msgstr "Käytetty mittausmoodi päätettäessä valotusta kuvattaessa" msgid "capturing source" -msgstr "" +msgstr "kuvaamisen lähde" msgid "The source or type of device used for the capture" -msgstr "" +msgstr "Lähde tai laitetyyppi kuvattaessa" msgid "image horizontal ppi" -msgstr "" +msgstr "kuvan vaakasuora ppi" msgid "Media (image/video) intended horizontal pixel density in ppi" -msgstr "" +msgstr "Median (kuva tai video) tarkoitettu vaakasuora pikselitiheys ppi:nä" msgid "image vertical ppi" -msgstr "" +msgstr "kuvan pystysuora ppi" msgid "Media (image/video) intended vertical pixel density in ppi" -msgstr "" +msgstr "Median (kuva tai video) tarkoitettu pystysuora pikselitiheys ppi:nä" msgid "This CD has no audio tracks" msgstr "Tällä CD-levyllä ei ole ääniraitoja" diff --git a/po/gl.po b/po/gl.po index 97ef2dcb68..47676d4c05 100644 --- a/po/gl.po +++ b/po/gl.po @@ -1,21 +1,21 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) 2010 Fran Dieguez # This file is distributed under the same license as the gst-plugins-base package. -# Fran Diéguez , 2010. +# Fran Diéguez , 2010, 2011. # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-base 0.10.29.2\n" +"Project-Id-Version: gst-plugins-base 0.10.31.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-10-16 01:03+0100\n" -"PO-Revision-Date: 2010-08-19 15:01+0200\n" +"POT-Creation-Date: 2011-01-11 18:58+0000\n" +"PO-Revision-Date: 2011-01-09 21:20+0100\n" "Last-Translator: Fran Diéguez \n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\\\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" "X-Poedit-Language: galego\n" msgid "Master" @@ -291,119 +291,120 @@ msgid "MusicBrainz TRM ID" msgstr "ID TRM en MusicBrainz" msgid "capturing shutter speed" -msgstr "" +msgstr "velocidade do obturador da toma" msgid "Shutter speed used when capturing an image, in seconds" -msgstr "" +msgstr "Velocidade do obturador usada ao tomar unha imaxe, en segundos" msgid "capturing focal ratio" -msgstr "" +msgstr "taxa focal da toma" msgid "Focal ratio (f-number) used when capturing the image" -msgstr "" +msgstr "Taxa focal (número-f) usada ao tomar a imaxe" msgid "capturing focal length" -msgstr "" +msgstr "lonxitude focal da toma" msgid "Focal length of the lens used capturing the image, in mm" -msgstr "" +msgstr "Lonxitude focal da lente usada ao tomar a imaxe, en mm" msgid "capturing digital zoom ratio" -msgstr "" +msgstr "taxa de ampliación dixital da toma" msgid "Digital zoom ratio used when capturing an image" -msgstr "" +msgstr "Taxa de ampliación dixital usada ao tomar unha imaxe" msgid "capturing iso speed" -msgstr "" +msgstr "velocidade ISO da toma" msgid "The ISO speed used when capturing an image" -msgstr "" +msgstr "A velocidade de captura ISO usada ao tomar unha imaxe" msgid "capturing exposure program" -msgstr "" +msgstr "programa de exposición da toma" msgid "The exposure program used when capturing an image" -msgstr "" +msgstr "O programa de exposición usado ao tomar unha imaxe" msgid "capturing exposure mode" -msgstr "" +msgstr "modo de exposición da toma" msgid "The exposure mode used when capturing an image" -msgstr "" +msgstr "O modo de exposición usado ao tomar unha imaxe" msgid "capturing scene capture type" -msgstr "" +msgstr "tipo de escena usado na toma" msgid "The scene capture mode used when capturing an image" -msgstr "" +msgstr "O modo de captura usado ao tomar unha imaxe" msgid "capturing gain adjustment" -msgstr "" +msgstr "axuste de ganancia da toma" msgid "The overall gain adjustment applied on an image" -msgstr "" +msgstr "O axuste xeral de ganancia aplicado nunha imaxe" msgid "capturing white balance" -msgstr "" +msgstr "balance de brancos da toma" msgid "The white balance mode set when capturing an image" -msgstr "" +msgstr "O modo de balance de brancos usado ao tomar unha imaxe" msgid "capturing contrast" -msgstr "" +msgstr "contraste da toma" msgid "The direction of contrast processing applied when capturing an image" -msgstr "" +msgstr "A dirección de contraste procesada aplicada ao tomar unha imaxe" msgid "capturing saturation" -msgstr "" +msgstr "saturación da toma" msgid "The direction of saturation processing applied when capturing an image" -msgstr "" +msgstr "A dirección de saturación procesada aplicada ao tomar unha imaxe" msgid "capturing sharpness" -msgstr "" +msgstr "nitidez da toma" msgid "The direction of sharpness processing applied when capturing an image" -msgstr "" +msgstr "A dirección de nitidez procesada aplicada ao tomar unha imaxe" msgid "capturing flash fired" -msgstr "" +msgstr "disparo de flash da toma" msgid "If the flash fired while capturing an image" -msgstr "" +msgstr "Se o flash se disparou ao capturar unha imaxe" msgid "capturing flash mode" -msgstr "" +msgstr "modo de flash da toma" msgid "The selected flash mode while capturing an image" -msgstr "" +msgstr "O modo de flash seleccionado ao tomar unha imaxe" msgid "capturing metering mode" -msgstr "" +msgstr "modo de medición da toma" msgid "" "The metering mode used while determining exposure for capturing an image" msgstr "" +"O modo de medición usado ao determinar a exposición ao tomar unha imaxe" msgid "capturing source" -msgstr "" +msgstr "orixe da toma" msgid "The source or type of device used for the capture" -msgstr "" +msgstr "A orixe ou tipo de dispositivo usado para a toma" msgid "image horizontal ppi" -msgstr "" +msgstr "ppi horizontal da imaxe" msgid "Media (image/video) intended horizontal pixel density in ppi" -msgstr "" +msgstr "Densidade horizontal de píxeles, en ppi, do medio (imaxe ou vídeo)" msgid "image vertical ppi" -msgstr "" +msgstr "ppi vertical da imaxe" msgid "Media (image/video) intended vertical pixel density in ppi" -msgstr "" +msgstr "Densidade vertical de píxeles, en ppi, do medio (imaxe ou vídeo)" msgid "This CD has no audio tracks" msgstr "Este CD non contén pistas de son" diff --git a/po/pt_BR.po b/po/pt_BR.po index f38d3e9f9e..30a86ba8a9 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1,16 +1,16 @@ # Brazilian Portuguese translation of gst-plugins-base. -# Copyright (C) 2008-2010 Free Software Foundation, Inc. +# Copyright (C) 2008-2011 Free Software Foundation, Inc. # This file is distributed under the same license as the gst-plugins-base package. -# Fabrício Godoy , 2008-2010. +# Fabrício Godoy , 2008-2011. # # PCM -> PCM # msgid "" msgstr "" -"Project-Id-Version: gst-plugins-base-0.10.29.2\n" +"Project-Id-Version: gst-plugins-base-0.10.31.2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-10-16 01:03+0100\n" -"PO-Revision-Date: 2010-06-28 20:25-0300\n" +"POT-Creation-Date: 2011-01-11 18:58+0000\n" +"PO-Revision-Date: 2011-01-08 01:02-0300\n" "Last-Translator: Fabrício Godoy \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" @@ -292,119 +292,126 @@ msgid "MusicBrainz TRM ID" msgstr "ident. da trilha TRM no MusicBrainz" msgid "capturing shutter speed" -msgstr "" +msgstr "velocidade do obturador ao capturar" msgid "Shutter speed used when capturing an image, in seconds" -msgstr "" +msgstr "Velocidade do obturador usada ao capturar uma imagem, em segundos" msgid "capturing focal ratio" -msgstr "" +msgstr "taxa focal ao capturar" msgid "Focal ratio (f-number) used when capturing the image" -msgstr "" +msgstr "Taxa focal usada ao capturar a imagem, f-number" msgid "capturing focal length" -msgstr "" +msgstr "distância focal ao capturar" msgid "Focal length of the lens used capturing the image, in mm" -msgstr "" +msgstr "Distância focal das lentes usada ao capturar a imagem, em mm" msgid "capturing digital zoom ratio" -msgstr "" +msgstr "taxa de zoom digital ao capturar" msgid "Digital zoom ratio used when capturing an image" -msgstr "" +msgstr "Taxa de zoom digital usada ao capturar uma imagem" msgid "capturing iso speed" -msgstr "" +msgstr "velocidade ISO ao capturar" msgid "The ISO speed used when capturing an image" -msgstr "" +msgstr "A velocidade ISO usada ao capturar uma imagem" msgid "capturing exposure program" -msgstr "" +msgstr "programa de exposição ao capturar" msgid "The exposure program used when capturing an image" -msgstr "" +msgstr "O programa de exposição usado ao capturar uma imagem" msgid "capturing exposure mode" -msgstr "" +msgstr "modo de exposição ao capturar" msgid "The exposure mode used when capturing an image" -msgstr "" +msgstr "O modo de exposição usado ao capturar uma imagem" msgid "capturing scene capture type" -msgstr "" +msgstr "tipo de captura de cena ao capturar" msgid "The scene capture mode used when capturing an image" -msgstr "" +msgstr "O modo de captura de cena usado ao capturar uma imagem" msgid "capturing gain adjustment" -msgstr "" +msgstr "ajuste de ganho ao capturar" msgid "The overall gain adjustment applied on an image" -msgstr "" +msgstr "O ajuste de ganho geral aplicado na imagem" msgid "capturing white balance" -msgstr "" +msgstr "balanço de branco ao capturar" msgid "The white balance mode set when capturing an image" -msgstr "" +msgstr "O modo de balanço de branco definido ao capturar uma imagem" msgid "capturing contrast" -msgstr "" +msgstr "contraste ao capturar" msgid "The direction of contrast processing applied when capturing an image" msgstr "" +"O procedimento de processamento de contraste aplicado ao capturar uma imagem" msgid "capturing saturation" -msgstr "" +msgstr "saturação ao capturar" msgid "The direction of saturation processing applied when capturing an image" msgstr "" +"O procedimento de processamento de saturação aplicado ao capturar uma imagem" msgid "capturing sharpness" -msgstr "" +msgstr "nitidez ao capturar" msgid "The direction of sharpness processing applied when capturing an image" msgstr "" +"O procedimento de processamento de nitidez aplicado ao capturar uma imagem" msgid "capturing flash fired" -msgstr "" +msgstr "disparar flash ao capturar" msgid "If the flash fired while capturing an image" -msgstr "" +msgstr "Define se o flash é disparado ao capturar uma imagem" msgid "capturing flash mode" -msgstr "" +msgstr "modo de flash ao capturar" msgid "The selected flash mode while capturing an image" -msgstr "" +msgstr "O modo de flash selecionado ao capturar uma imagem" msgid "capturing metering mode" -msgstr "" +msgstr "modo de medição de luz ao capturar" msgid "" "The metering mode used while determining exposure for capturing an image" msgstr "" +"O modo de medição de luz usado ao determinar a exposição para captura de uma " +"imagem" msgid "capturing source" -msgstr "" +msgstr "fonte ao capturar" msgid "The source or type of device used for the capture" -msgstr "" +msgstr "A fonte ou tipo de dispositivo usado para a captura" msgid "image horizontal ppi" -msgstr "" +msgstr "PPI horizontal da imagem" msgid "Media (image/video) intended horizontal pixel density in ppi" msgstr "" +"Densidade de pixels horizontais desejado para a mídia (imagem/vídeo), em PPI" msgid "image vertical ppi" -msgstr "" +msgstr "PPI vertical da imagem" msgid "Media (image/video) intended vertical pixel density in ppi" msgstr "" +"Densidade de pixels verticais desejado para a mídia (imagem/vídeo), em PPI" msgid "This CD has no audio tracks" msgstr "Este CD não tem trilhas de áudio" diff --git a/po/ru.po b/po/ru.po index 5541f8e3c8..646f8e1c26 100644 --- a/po/ru.po +++ b/po/ru.po @@ -3,13 +3,13 @@ # # Артём Попов , 2009. # Pavel Maryanov , 2009. -# Yuri Kozlov , 2010. +# Yuri Kozlov , 2010, 2011. msgid "" msgstr "" -"Project-Id-Version: gst-plugins-base 0.10.29.2\n" +"Project-Id-Version: gst-plugins-base 0.10.30.3\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" -"POT-Creation-Date: 2010-10-16 01:03+0100\n" -"PO-Revision-Date: 2010-09-26 21:29+0400\n" +"POT-Creation-Date: 2011-01-06 20:42+0000\n" +"PO-Revision-Date: 2011-01-01 16:29+0300\n" "Last-Translator: Yuri Kozlov \n" "Language-Team: Russian \n" "Language: ru\n" @@ -51,7 +51,7 @@ msgid "Playback" msgstr "Воспроизведение" msgid "Capture" -msgstr "Захват" +msgstr "Съёмка" msgid "Could not open device for playback in mono mode." msgstr "Не удалось открыть устройство для воспроизведения в режиме моно." @@ -292,119 +292,121 @@ msgid "MusicBrainz TRM ID" msgstr "MusicBrainz TRM ID" msgid "capturing shutter speed" -msgstr "" +msgstr "выдержка при съёмке" msgid "Shutter speed used when capturing an image, in seconds" -msgstr "" +msgstr "Выдержка при съёмке изображения, в секундах" msgid "capturing focal ratio" -msgstr "" +msgstr "диафрагменное число при съёмке" msgid "Focal ratio (f-number) used when capturing the image" -msgstr "" +msgstr "Диафрагменное число (f) при съёмке изображения" msgid "capturing focal length" -msgstr "" +msgstr "фокусное расстояние при съёмке" msgid "Focal length of the lens used capturing the image, in mm" -msgstr "" +msgstr "Фокусное расстояние зеркала при съёмке изображения, в мм" msgid "capturing digital zoom ratio" -msgstr "" +msgstr "коэффициент цифрового трансфокатора при съёмке" msgid "Digital zoom ratio used when capturing an image" -msgstr "" +msgstr "Коэффициент цифрового трансфокатора при съёмке изображения" msgid "capturing iso speed" -msgstr "" +msgstr "чувствительность ISO при съёмке" msgid "The ISO speed used when capturing an image" -msgstr "" +msgstr "Чувствительность ISO при съёмке изображения" msgid "capturing exposure program" -msgstr "" +msgstr "программа экспозиции при съёмке" msgid "The exposure program used when capturing an image" -msgstr "" +msgstr "Программа экспозиции при съёмке изображения" msgid "capturing exposure mode" -msgstr "" +msgstr "режим экспозиции при съёмке" msgid "The exposure mode used when capturing an image" -msgstr "" +msgstr "Режим экспозиции при съёмке изображения" msgid "capturing scene capture type" -msgstr "" +msgstr "тип сцены при съёмке" msgid "The scene capture mode used when capturing an image" -msgstr "" +msgstr "Тип сцены при съёмке изображения" msgid "capturing gain adjustment" -msgstr "" +msgstr "регулировка усиления при съёмке" msgid "The overall gain adjustment applied on an image" -msgstr "" +msgstr "Итоговая настройка усиления, применяемая к изображению" msgid "capturing white balance" -msgstr "" +msgstr "баланс белого при съёмке" msgid "The white balance mode set when capturing an image" -msgstr "" +msgstr "Режим баланса белого при съёмке изображения" msgid "capturing contrast" -msgstr "" +msgstr "контрастность при съёмке" msgid "The direction of contrast processing applied when capturing an image" -msgstr "" +msgstr "Направление обработки контраста при съёмке изображения" msgid "capturing saturation" -msgstr "" +msgstr "насыщенность при съёмке" msgid "The direction of saturation processing applied when capturing an image" -msgstr "" +msgstr "Направление обработки насыщенности при съёмке изображения" msgid "capturing sharpness" -msgstr "" +msgstr "резкость при съёмке" msgid "The direction of sharpness processing applied when capturing an image" -msgstr "" +msgstr "Направление обработки резкости при съёмке изображения" msgid "capturing flash fired" -msgstr "" +msgstr "вспышка при съёмке" msgid "If the flash fired while capturing an image" -msgstr "" +msgstr "Сработала ли вспышка при съёмке изображения" msgid "capturing flash mode" -msgstr "" +msgstr "режим вспышки при съёмке" msgid "The selected flash mode while capturing an image" -msgstr "" +msgstr "Выбранный режим для вспышки при съёмке изображения" msgid "capturing metering mode" -msgstr "" +msgstr "режим замера при съёмке" msgid "" "The metering mode used while determining exposure for capturing an image" msgstr "" +"Режим замера, использованный для определения экспозиции при съёмке " +"изображения" msgid "capturing source" -msgstr "" +msgstr "источник для съёмки" msgid "The source or type of device used for the capture" -msgstr "" +msgstr "Источник или тип устройства, использованный для съёмки" msgid "image horizontal ppi" -msgstr "" +msgstr "изображение в ppi по горизонтали" msgid "Media (image/video) intended horizontal pixel density in ppi" -msgstr "" +msgstr "Горизонтальная плотность носителя (изображение/видео) в ppi" msgid "image vertical ppi" -msgstr "" +msgstr "изображение в ppi по вертикали" msgid "Media (image/video) intended vertical pixel density in ppi" -msgstr "" +msgstr "Вертикальная плотность носителя (изображение/видео) в ppi" msgid "This CD has no audio tracks" msgstr "На CD нет звуковых дорожек" diff --git a/sys/xvimage/xvimagesink.c b/sys/xvimage/xvimagesink.c index 5f3eb21da0..f5ee8666c2 100644 --- a/sys/xvimage/xvimagesink.c +++ b/sys/xvimage/xvimagesink.c @@ -2089,7 +2089,7 @@ gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps) "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %" GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps); - if (!gst_caps_intersect (xvimagesink->xcontext->caps, caps)) + if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps)) goto incompatible_caps; structure = gst_caps_get_structure (caps, 0); diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 15f2ec82f9..f496787d35 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -37,7 +37,7 @@ endif if USE_GNOME_VFS check_gnomevfs = elements/gnomevfssink else -check_gnomevfs = +check_gnomevfs = endif if USE_GIO @@ -55,13 +55,13 @@ endif if USE_OGG check_ogg = pipelines/oggmux else -check_ogg = +check_ogg = endif if USE_PANGO check_pango = elements/textoverlay else -check_pango = +check_pango = endif if USE_VORBIS @@ -77,6 +77,16 @@ else check_theora = endif +if USE_VORBIS +if USE_THEORA +check_encodebin = elements/encodebin +else +check_encodebin = +endif +else +check_encodebin = +endif + if USE_PLUGIN_SUBPARSE check_subparse = elements/subparse else @@ -106,6 +116,7 @@ check_PROGRAMS = \ elements/audiotestsrc \ elements/decodebin \ elements/decodebin2 \ + $(check_encodebin) \ elements/ffmpegcolorspace \ elements/gdpdepay \ elements/gdppay \ @@ -120,6 +131,7 @@ check_PROGRAMS = \ generic/clock-selection \ generic/states \ gst/typefindfunctions \ + libs/libsabi \ libs/audio \ libs/cddabasesrc \ libs/fft \ @@ -127,7 +139,9 @@ check_PROGRAMS = \ libs/navigation \ libs/netbuffer \ libs/pbutils \ + libs/profile \ libs/rtp \ + libs/rtsp \ libs/tag \ libs/video \ $(check_orc) \ @@ -147,8 +161,18 @@ VALGRIND_TO_FIX = \ # these tests don't even pass noinst_PROGRAMS = $(check_libvisual) +noinst_HEADERS = \ + libs/struct_i386.h +# libs/struct_arm.h \ +# libs/struct_hppa.h \ +# libs/struct_ppc32.h \ +# libs/struct_ppc64.h \ +# libs/struct_sparc.h \ +# libs/struct_x86_64.h + AM_CFLAGS = $(GST_CFLAGS) $(GST_CHECK_CFLAGS) \ - -DGST_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" + -DGST_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" \ + -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS LDADD = $(GST_LIBS) $(GST_CHECK_LIBS) # valgrind testing @@ -156,6 +180,26 @@ VALGRIND_TESTS_DISABLE = $(VALGRIND_TO_FIX) SUPPRESSIONS = $(top_srcdir)/common/gst.supp $(srcdir)/gst-plugins-base.supp +libs_libsabi_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(AM_CFLAGS) + +libs_libsabi_LDADD = \ + $(top_builddir)/gst-libs/gst/app/libgstapp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/cdda/libgstcdda-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/fft/libgstfft-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/netbuffer/libgstnetbuffer-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/rtsp/libgstrtsp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_MAJORMINOR@.la \ + $(GST_BASE_LIBS) \ + $(LDADD) + libs_audio_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ @@ -222,6 +266,13 @@ libs_rtp_LDADD = \ $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_MAJORMINOR@.la \ $(GST_BASE_LIBS) $(LDADD) +libs_rtsp_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(AM_CFLAGS) +libs_rtsp_LDADD = \ + $(top_builddir)/gst-libs/gst/rtsp/libgstrtsp-@GST_MAJORMINOR@.la \ + $(GST_BASE_LIBS) $(LDADD) + libs_tag_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ @@ -238,6 +289,12 @@ libs_pbutils_LDADD = \ $(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_MAJORMINOR@.la \ $(GST_BASE_LIBS) $(LDADD) +libs_profile_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(AM_CFLAGS) +libs_profile_LDADD = \ + $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la $(LDADD) + elements_appsink_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(AM_CFLAGS) @@ -291,6 +348,9 @@ elements_playbin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD) elements_decodebin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) +elements_encodebin_LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la $(GST_BASE_LIBS) $(LDADD) +elements_encodebin_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) + elements_decodebin2_LDADD = $(GST_BASE_LIBS) $(LDADD) elements_decodebin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) diff --git a/tests/check/elements/.gitignore b/tests/check/elements/.gitignore index 3e11d9e015..20bd96e23f 100644 --- a/tests/check/elements/.gitignore +++ b/tests/check/elements/.gitignore @@ -9,6 +9,7 @@ audioresample audiotestsrc decodebin decodebin2 +encodebin gdpdepay gdppay gnomevfssink diff --git a/tests/check/elements/appsink.c b/tests/check/elements/appsink.c index 3d03439338..34982ef933 100644 --- a/tests/check/elements/appsink.c +++ b/tests/check/elements/appsink.c @@ -317,6 +317,91 @@ GST_START_TEST (test_buffer_list) GST_END_TEST; +static GstFlowReturn +callback_function_buffer (GstAppSink * appsink, gpointer p_counter) +{ + GstBuffer *buf; + gint *p_int_counter = p_counter; + + buf = gst_app_sink_pull_buffer (appsink); + fail_unless (GST_IS_BUFFER (buf)); + + /* buffer list has 3 buffers in two groups */ + switch (*p_int_counter) { + case 0: + fail_unless_equals_int (GST_BUFFER_SIZE (buf), sizeof (gint)); + fail_unless_equals_int ((((gint *) GST_BUFFER_DATA (buf))[0]), 1); + break; + case 1: + fail_unless_equals_int (GST_BUFFER_SIZE (buf), 2 * sizeof (gint)); + fail_unless_equals_int ((((gint *) GST_BUFFER_DATA (buf))[0]), 2); + fail_unless_equals_int ((((gint *) GST_BUFFER_DATA (buf))[1]), 4); + break; + default: + g_warn_if_reached (); + break; + } + + gst_buffer_unref (buf); + + *p_int_counter += 1; + + return GST_FLOW_OK; +} + +GST_START_TEST (test_buffer_list_fallback) +{ + GstElement *sink; + GstBufferList *list; + GstAppSinkCallbacks callbacks = { NULL }; + gint counter = 0; + + sink = setup_appsink (); + + callbacks.new_buffer = callback_function_buffer; + + gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks, &counter, NULL); + + ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC); + + list = create_buffer_list (); + fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK); + + fail_unless_equals_int (counter, 2); + + ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + cleanup_appsink (sink); +} + +GST_END_TEST; + +GST_START_TEST (test_buffer_list_fallback_signal) +{ + GstElement *sink; + GstBufferList *list; + gint counter = 0; + + sink = setup_appsink (); + + /* C calling convention to the rescue.. */ + g_signal_connect (sink, "new-buffer", G_CALLBACK (callback_function_buffer), + &counter); + + g_object_set (sink, "emit-signals", TRUE, NULL); + + ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC); + + list = create_buffer_list (); + fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK); + + fail_unless_equals_int (counter, 2); + + ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + cleanup_appsink (sink); +} + +GST_END_TEST; + static Suite * appsink_suite (void) { @@ -329,6 +414,8 @@ appsink_suite (void) tcase_add_test (tc_chain, test_notify0); tcase_add_test (tc_chain, test_notify1); tcase_add_test (tc_chain, test_buffer_list); + tcase_add_test (tc_chain, test_buffer_list_fallback); + tcase_add_test (tc_chain, test_buffer_list_fallback_signal); return s; } diff --git a/tests/check/elements/audioresample.c b/tests/check/elements/audioresample.c index a9a2e3b96a..a8a79a306b 100644 --- a/tests/check/elements/audioresample.c +++ b/tests/check/elements/audioresample.c @@ -327,18 +327,18 @@ test_discont_stream_instance (int inrate, int outrate, int samples, GST_START_TEST (test_discont_stream) { /* integral scalings */ - test_discont_stream_instance (48000, 24000, 500, 20); - test_discont_stream_instance (48000, 12000, 500, 20); - test_discont_stream_instance (12000, 24000, 500, 20); - test_discont_stream_instance (12000, 48000, 500, 20); + test_discont_stream_instance (48000, 24000, 5000, 20); + test_discont_stream_instance (48000, 12000, 5000, 20); + test_discont_stream_instance (12000, 24000, 5000, 20); + test_discont_stream_instance (12000, 48000, 5000, 20); /* non-integral scalings */ - test_discont_stream_instance (44100, 8000, 500, 20); - test_discont_stream_instance (8000, 44100, 500, 20); + test_discont_stream_instance (44100, 8000, 5000, 20); + test_discont_stream_instance (8000, 44100, 5000, 20); /* wacky scalings */ - test_discont_stream_instance (12345, 54321, 500, 20); - test_discont_stream_instance (101, 99, 500, 20); + test_discont_stream_instance (12345, 54321, 5000, 20); + test_discont_stream_instance (101, 99, 5000, 20); } GST_END_TEST; diff --git a/tests/check/elements/encodebin.c b/tests/check/elements/encodebin.c new file mode 100644 index 0000000000..3b5d66ad82 --- /dev/null +++ b/tests/check/elements/encodebin.c @@ -0,0 +1,838 @@ +/* GStreamer unit test for gstprofile + * + * Copyright (C) <2009> Edward Hervey + * + * 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 +#include + +/* 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; +} + +static GstEncodingProfile * +create_vorbis_only_profile (void) +{ + GstEncodingProfile *prof; + GstCaps *vorbis; + + vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL); + prof = + (GstEncodingProfile *) gst_encoding_audio_profile_new (vorbis, NULL, NULL, + 0); + gst_caps_unref (vorbis); + + return 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); + gst_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); + gst_object_unref (target); + gst_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); + gst_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); + gst_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); + gst_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); + gst_object_unref (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); + gst_object_unref (sinkpad); + sinkpad = NULL; + + 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); + gst_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); + gst_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); + gst_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); + gst_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); + + /* Check if the video sink pad was properly created */ + sinkpadtheora = gst_element_get_request_pad (ebin, "video_1"); + fail_unless (sinkpadtheora != NULL); + + fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED), + GST_STATE_CHANGE_SUCCESS); + + gst_element_release_request_pad (GST_ELEMENT (ebin), sinkpadvorbis); + gst_object_unref (sinkpadvorbis); + gst_element_release_request_pad (GST_ELEMENT (ebin), sinkpadtheora); + gst_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_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); + gst_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); + gst_object_unref (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); + + gst_object_unref (bus); + + gst_object_unref (pipeline); +} + +GST_END_TEST; + +GST_START_TEST (test_encodebin_render_audio_only_static) +{ + GstElement *ebin, *pipeline, *audiotestsrc, *fakesink; + GstEncodingProfile *prof; + GstBus *bus; + gboolean done = FALSE; + GstPad *sinkpad; + GstCaps *sinkcaps; + + /* Create an encodebin and render 5s of vorbis only */ + + 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_vorbis_only_profile (); + 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)); + + /* Requesting a new pad should fail */ + fail_if (gst_element_get_request_pad (ebin, "audio_0") != NULL); + 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_if (sinkpad != 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); + + gst_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); + + gst_object_unref (srcpad); + + 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); + } + } + + gst_element_release_request_pad (GST_ELEMENT (ebin), sinkpad); + gst_object_unref (sinkpad); + + /* Set back to NULL */ + fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + gst_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); + + gst_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 *sinkpad1, *sinkpad2, *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"); + sinkpad1 = gst_element_get_request_pad (ebin, "audio_0"); + fail_unless (srcpad != NULL); + fail_unless (sinkpad1 != NULL); + fail_unless_equals_int (gst_pad_link (srcpad, sinkpad1), GST_PAD_LINK_OK); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (videotestsrc, "src"); + sinkpad2 = gst_element_get_request_pad (ebin, "video_1"); + fail_unless_equals_int (gst_pad_link (srcpad, sinkpad2), GST_PAD_LINK_OK); + gst_object_unref (srcpad); + + 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); + } + } + + gst_element_release_request_pad (GST_ELEMENT (ebin), sinkpad1); + gst_object_unref (sinkpad1); + gst_element_release_request_pad (GST_ELEMENT (ebin), sinkpad2); + gst_object_unref (sinkpad2); + + /* Set back to NULL */ + fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + gst_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_only_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); diff --git a/tests/check/elements/playbin2.c b/tests/check/elements/playbin2.c index c7281dd5fb..aef9272bbd 100644 --- a/tests/check/elements/playbin2.c +++ b/tests/check/elements/playbin2.c @@ -464,6 +464,49 @@ GST_START_TEST (test_refcount) GST_END_TEST; +static void +source_setup (GstElement * playbin, GstElement * source, GstElement ** p_src) +{ + GST_LOG ("source-setup called, source = %s", G_OBJECT_TYPE_NAME (source)); + *p_src = gst_object_ref (source); + GST_LOG ("here"); +} + +GST_START_TEST (test_source_setup) +{ + GstElement *playbin, *videosink; + GstElement *src = NULL; + + if (!gst_default_registry_check_feature_version ("redvideosrc", 0, 10, 0)) { + fail_unless (gst_element_register (NULL, "redvideosrc", GST_RANK_PRIMARY, + gst_red_video_src_get_type ())); + } + + playbin = gst_element_factory_make ("playbin2", NULL); + g_object_set (playbin, "uri", "redvideo://", NULL); + + videosink = gst_element_factory_make ("fakesink", "myvideosink"); + g_object_set (playbin, "video-sink", videosink, NULL); + + g_signal_connect (playbin, "source-setup", G_CALLBACK (source_setup), &src); + + fail_unless_equals_int (gst_element_set_state (playbin, GST_STATE_PAUSED), + GST_STATE_CHANGE_ASYNC); + fail_unless_equals_int (gst_element_get_state (playbin, NULL, NULL, + GST_CLOCK_TIME_NONE), GST_STATE_CHANGE_SUCCESS); + + fail_unless (src != NULL); + fail_unless (G_OBJECT_TYPE (src) == gst_red_video_src_get_type ()); + + fail_unless_equals_int (gst_element_set_state (playbin, GST_STATE_NULL), + GST_STATE_CHANGE_SUCCESS); + + gst_object_unref (playbin); + gst_object_unref (src); +} + +GST_END_TEST; + /*** redvideo:// source ***/ static GstURIType @@ -696,6 +739,7 @@ playbin2_suite (void) tcase_add_test (tc_chain, test_missing_suburisource_handler); tcase_add_test (tc_chain, test_missing_primary_decoder); tcase_add_test (tc_chain, test_refcount); + tcase_add_test (tc_chain, test_source_setup); /* one day we might also want to have the following checks: * tcase_add_test (tc_chain, test_missing_secondary_decoder_one_fatal); diff --git a/tests/check/elements/videoscale.c b/tests/check/elements/videoscale.c index f8a38eb510..75f16dfdca 100644 --- a/tests/check/elements/videoscale.c +++ b/tests/check/elements/videoscale.c @@ -68,6 +68,24 @@ on_sink_handoff (GstElement * element, GstBuffer * buffer, GstPad * pad, *n_buffers = *n_buffers + 1; } +static gboolean +caps_are_64bpp (const GstCaps * caps) +{ + GstVideoFormat fmt; + GstCaps *fmt_caps; + + /* need fixed caps for _parse_caps */ + fmt_caps = gst_caps_copy (caps); + gst_structure_remove_field (gst_caps_get_structure (fmt_caps, 0), "width"); + gst_structure_remove_field (gst_caps_get_structure (fmt_caps, 0), "height"); + gst_structure_remove_field (gst_caps_get_structure (fmt_caps, 0), + "framerate"); + + fail_unless (gst_video_format_parse_caps (fmt_caps, &fmt, NULL, NULL)); + gst_caps_unref (fmt_caps); + return (fmt == GST_VIDEO_FORMAT_ARGB64 || fmt == GST_VIDEO_FORMAT_AYUV64); +} + static void run_test (const GstCaps * caps, gint src_width, gint src_height, gint dest_width, gint dest_height, gint method, @@ -82,6 +100,10 @@ run_test (const GstCaps * caps, gint src_width, gint src_height, GstCaps *copy; guint n_buffers = 0; + /* skip formats that ffmpegcolorspace can't handle */ + if (caps_are_64bpp (caps)) + return; + pipeline = gst_element_factory_make ("pipeline", "pipeline"); fail_unless (pipeline != NULL); @@ -198,6 +220,10 @@ GST_START_TEST (test_passthrough) GstCaps *caps = *p; for (method = 0; method < 3; method++) { + /* skip formats that ffmpegcolorspace can't handle */ + if (caps_are_64bpp (caps)) + continue; + GST_DEBUG ("Running test for caps '%" GST_PTR_FORMAT "'" " from %dx%u to %dx%d with method %d", caps, src_width, src_height, dest_width, dest_height, method); @@ -251,7 +277,6 @@ GST_START_TEST (name) \ run_test (caps, src_width, src_height, \ dest_width, dest_height, method, \ NULL, NULL, NULL, NULL); \ - \ gst_caps_unref (caps); \ p++; \ } \ @@ -787,6 +812,7 @@ videoscale_suite (void) tcase_add_test (tc_chain, test_negotiation); tcase_add_test (tc_chain, test_reverse_negotiation); + GST_ERROR ("FIXME: test 64-bpp formats as well"); return s; } diff --git a/tests/check/gst/typefindfunctions.c b/tests/check/gst/typefindfunctions.c index cec594393d..cbe188a19e 100644 --- a/tests/check/gst/typefindfunctions.c +++ b/tests/check/gst/typefindfunctions.c @@ -174,6 +174,141 @@ GST_START_TEST (test_mpegts) GST_END_TEST; +struct ac3_frmsize +{ + unsigned frmsizecod; + unsigned frmsize; +}; + +static void +make_ac3_packet (guint8 * data, guint bytesize, guint bsid) +{ + /* Actually not a fully valid packet; if the typefinder starts to + * check e.g. the CRCs, this test needs to be improved as well. */ + const guint8 ac3_header[] = { + 0x0b, 0x77, /* syncword */ + 0x00, 0x00, /* crc1 */ + 0x00, /* fscod 0xc0, frmsizecod 0x3f */ + 0x00 /* bsid 0xf8, bsmod 0x07 */ + }; + const struct ac3_frmsize frmsize[] = { + {17, 256}, {26, 640} /* small subset of supported sizes */ + }; + guint wordsize = bytesize >> 1, frmsizecod = 0; + int i; + + fail_unless ((bytesize & 0x01) == 0); + fail_unless (bytesize >= sizeof (ac3_header)); + + for (i = 0; i < G_N_ELEMENTS (frmsize); i++) { + if (frmsize[i].frmsize == wordsize) { + frmsizecod = frmsize[i].frmsizecod; + break; + } + } + + fail_unless (frmsizecod); + + memcpy (data, ac3_header, sizeof (ac3_header)); + data[4] = (data[4] & ~0x3f) | (frmsizecod & 0x3f); + data[5] = (bsid & 0x1f) << 3; + memset (data + 6, 0, bytesize - 6); +} + +GST_START_TEST (test_ac3) +{ + GstTypeFindProbability prob; + const gchar *type; + GstBuffer *buf; + GstCaps *caps = NULL; + guint bsid; + + for (bsid = 0; bsid < 32; bsid++) { + buf = gst_buffer_new_and_alloc ((256 + 640) * 2); + make_ac3_packet (GST_BUFFER_DATA (buf), 256 * 2, bsid); + make_ac3_packet (GST_BUFFER_DATA (buf) + 256 * 2, 640 * 2, bsid); + + caps = gst_type_find_helper_for_buffer (NULL, buf, &prob); + if (bsid <= 8) { + fail_unless (caps != NULL); + GST_LOG ("Found type for BSID %u: %" GST_PTR_FORMAT, bsid, caps); + + type = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + fail_unless_equals_string (type, "audio/x-ac3"); + fail_unless (prob > GST_TYPE_FIND_MINIMUM + && prob <= GST_TYPE_FIND_MAXIMUM); + gst_caps_unref (caps); + } else { + fail_unless (caps == NULL); + } + + gst_buffer_unref (buf); + } +} + +GST_END_TEST; + +static void +make_eac3_packet (guint8 * data, guint bytesize, guint bsid) +{ + /* Actually not a fully valid packet; if the typefinder starts to + * check e.g. the CRCs, this test needs to be improved as well. */ + const guint8 eac3_header[] = { + 0x0b, 0x77, /* syncword */ + 0x00, /* strmtyp 0xc0, substreamid 0x38, + * frmsize 0x07 (3 high bits) */ + 0x00, /* frmsize (low bits -> 11 total) */ + 0x00, /* fscod 0xc0, fscod2/numblocks 0x30, + * acmod 0x0e, lfeon 0x01 */ + 0x00 /* bsid 0xf8, dialnorm 0x07 (3 high bits) */ + }; + guint wordsize = bytesize >> 1; + + fail_unless ((bytesize & 0x01) == 0); + fail_unless (bytesize >= sizeof (eac3_header)); + + memcpy (data, eac3_header, sizeof (eac3_header)); + data[2] = (data[2] & ~0x07) | ((((wordsize - 1) & 0x700) >> 8) & 0xff); + data[3] = (wordsize - 1) & 0xff; + data[5] = (bsid & 0x1f) << 3; + memset (data + 6, 0, bytesize - 6); +} + +GST_START_TEST (test_eac3) +{ + GstTypeFindProbability prob; + const gchar *type; + GstBuffer *buf; + GstCaps *caps = NULL; + guint bsid; + + for (bsid = 0; bsid <= 32; bsid++) { + buf = gst_buffer_new_and_alloc (558 + 384); + make_eac3_packet (GST_BUFFER_DATA (buf), 558, bsid); + make_eac3_packet (GST_BUFFER_DATA (buf) + 558, 384, bsid); + + caps = gst_type_find_helper_for_buffer (NULL, buf, &prob); + if (bsid > 10 && bsid <= 16) { + /* Only BSIs 11..16 are valid for Annex E */ + fail_unless (caps != NULL); + GST_LOG ("Found type for BSID %u: %" GST_PTR_FORMAT, bsid, caps); + + type = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + fail_unless_equals_string (type, "audio/x-eac3"); + fail_unless (prob > GST_TYPE_FIND_MINIMUM + && prob <= GST_TYPE_FIND_MAXIMUM); + gst_caps_unref (caps); + } else { + /* Invalid E-AC-3 BSID, must not be detected as anything: */ + fail_unless (caps == NULL); + } + + gst_buffer_unref (buf); + } +} + +GST_END_TEST; + #define TEST_RANDOM_DATA_SIZE (4*1024) /* typefind random data, to make sure all typefinders are called */ @@ -231,6 +366,8 @@ typefindfunctions_suite (void) tcase_add_test (tc_chain, test_broken_flac_in_ogg); tcase_add_test (tc_chain, test_jpeg_not_ac3); tcase_add_test (tc_chain, test_mpegts); + tcase_add_test (tc_chain, test_ac3); + tcase_add_test (tc_chain, test_eac3); tcase_add_test (tc_chain, test_random_data); return s; diff --git a/tests/check/libs/.gitignore b/tests/check/libs/.gitignore index cd372588c4..f0e5c928e7 100644 --- a/tests/check/libs/.gitignore +++ b/tests/check/libs/.gitignore @@ -6,7 +6,9 @@ mixer navigation netbuffer pbutils +profile rtp +rtsp tag utils video diff --git a/tests/check/libs/libsabi.c b/tests/check/libs/libsabi.c new file mode 100644 index 0000000000..5202d921e2 --- /dev/null +++ b/tests/check/libs/libsabi.c @@ -0,0 +1,112 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * 2011 Stefan Kost + * + * libsabi.c: Unit test for ABI compatibility + * + * 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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* initial version of the file was generated using: + * grep -A1 "" ../../docs/libs/gst-plugins-base-libs-decl.txt | \ + * grep "" | grep -v "Private" | sort | \ + * sed -e 's/\(.*\)<\/NAME>/\ {\"\1\", sizeof (\1), 0\},/' + * + * it needs a bit of editing to remove opaque structs + */ + +#ifdef HAVE_CPU_I386 +#include "struct_i386.h" +#define HAVE_ABI_SIZES TRUE +#else +/* in case someone wants to generate a new arch */ +#include "struct_i386.h" +#define HAVE_ABI_SIZES FALSE +#endif + +GST_START_TEST (test_ABI) +{ + gst_check_abi_list (list, HAVE_ABI_SIZES); +} + +GST_END_TEST; + +static Suite * +libsabi_suite (void) +{ + Suite *s = suite_create ("LibsABI"); + TCase *tc_chain = tcase_create ("size check"); + + tcase_set_timeout (tc_chain, 0); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_ABI); + return s; +} + +GST_CHECK_MAIN (libsabi); diff --git a/tests/check/libs/pbutils.c b/tests/check/libs/pbutils.c index ae632214e7..144d9a6cc4 100644 --- a/tests/check/libs/pbutils.c +++ b/tests/check/libs/pbutils.c @@ -72,8 +72,7 @@ GST_START_TEST (test_pb_utils_post_missing_messages) bus = gst_element_get_bus (pipeline); /* first, test common assertion failure cases */ - ASSERT_CRITICAL (msg = gst_missing_uri_source_message_new (NULL, "http"); - ); + ASSERT_CRITICAL (msg = gst_missing_uri_source_message_new (NULL, "http")); ASSERT_CRITICAL (gst_missing_uri_source_message_new (pipeline, NULL)); ASSERT_CRITICAL (gst_missing_uri_sink_message_new (NULL, "http")); @@ -555,11 +554,11 @@ GST_START_TEST (test_pb_utils_install_plugins) ctx = gst_install_plugins_context_new (); - ASSERT_CRITICAL (ret = gst_install_plugins_sync (NULL, ctx);); + ASSERT_CRITICAL (ret = gst_install_plugins_sync (NULL, ctx)); ASSERT_CRITICAL (ret = - gst_install_plugins_async (NULL, ctx, result_cb, (gpointer) & marker);); + gst_install_plugins_async (NULL, ctx, result_cb, (gpointer) & marker)); ASSERT_CRITICAL (ret = - gst_install_plugins_async (details, ctx, NULL, (gpointer) & marker);); + gst_install_plugins_async (details, ctx, NULL, (gpointer) & marker)); /* make sure the functions return the right error code if the helper does * not exist */ diff --git a/tests/check/libs/profile.c b/tests/check/libs/profile.c new file mode 100644 index 0000000000..285a58ebd4 --- /dev/null +++ b/tests/check/libs/profile.c @@ -0,0 +1,621 @@ +/* GStreamer unit test for gstprofile + * + * Copyright (C) <2009> Edward Hervey + * + * 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 */ +#include +#include +#include +#include + +#include +#include + +#define CHECK_PROFILE(profile, name, description, format, preset, presence, restriction) \ + { \ + fail_if(profile == NULL); \ + fail_unless_equals_string (gst_encoding_profile_get_name (profile), name); \ + fail_unless_equals_string (gst_encoding_profile_get_description (profile), description); \ + fail_unless (gst_caps_is_equal (gst_encoding_profile_get_format (profile), format)); \ + fail_unless_equals_string (gst_encoding_profile_get_preset (profile), preset); \ + fail_unless_equals_int (gst_encoding_profile_get_presence (profile), presence); \ + fail_unless (gst_caps_is_equal (gst_encoding_profile_get_restriction (profile), restriction)); \ + } + +GST_START_TEST (test_profile_creation) +{ + GstEncodingProfile *encprof; + GstEncodingAudioProfile *audioprof; + GstEncodingVideoProfile *videoprof; + GstCaps *ogg, *vorbis, *theora; + GstCaps *test1, *test2; + + ogg = gst_caps_new_simple ("application/ogg", NULL); + vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL); + theora = gst_caps_new_simple ("video/x-theora", NULL); + + encprof = (GstEncodingProfile *) gst_encoding_container_profile_new ((gchar *) + "ogg-theora-vorbis", "dumb-profile", ogg, (gchar *) "dumb-preset"); + CHECK_PROFILE (encprof, "ogg-theora-vorbis", "dumb-profile", ogg, + "dumb-preset", 0, NULL); + + audioprof = gst_encoding_audio_profile_new (vorbis, (gchar *) "HQ", NULL, 0); + CHECK_PROFILE ((GstEncodingProfile *) audioprof, NULL, NULL, vorbis, "HQ", 0, + NULL); + + videoprof = gst_encoding_video_profile_new (theora, (gchar *) "HQ", NULL, 0); + CHECK_PROFILE ((GstEncodingProfile *) videoprof, NULL, NULL, theora, "HQ", + 0, NULL); + + fail_unless (gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) encprof, + (GstEncodingProfile *) audioprof)); + fail_unless (gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) encprof, + (GstEncodingProfile *) videoprof)); + + /* Test caps */ + test1 = gst_caps_from_string ("video/x-theora; audio/x-vorbis"); + test2 = gst_encoding_profile_get_input_caps (encprof); + fail_unless (gst_caps_is_equal (test1, test2)); + gst_caps_unref (test1); + gst_caps_unref (test2); + + gst_encoding_profile_unref (encprof); + gst_caps_unref (ogg); + gst_caps_unref (theora); + gst_caps_unref (vorbis); +} + +GST_END_TEST; + + +GST_START_TEST (test_profile_input_caps) +{ + GstEncodingProfile *sprof; + GstCaps *vorbis; + GstCaps *out, *restriction, *test1; + + vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL); + + /* Simple case, no restriction */ + sprof = (GstEncodingProfile *) + gst_encoding_audio_profile_new (vorbis, NULL, NULL, 0); + fail_if (sprof == NULL); + + out = gst_encoding_profile_get_input_caps (sprof); + fail_if (out == NULL); + fail_unless (gst_caps_is_equal (out, vorbis)); + gst_caps_unref (out); + gst_encoding_profile_unref (sprof); + + /* One simple restriction */ + restriction = gst_caps_from_string ("audio/x-raw-int,channels=2,rate=44100"); + test1 = gst_caps_from_string ("audio/x-vorbis,channels=2,rate=44100"); + fail_if (restriction == NULL); + + sprof = (GstEncodingProfile *) + gst_encoding_audio_profile_new (vorbis, NULL, restriction, 0); + fail_if (sprof == NULL); + + out = gst_encoding_profile_get_input_caps (sprof); + fail_if (out == NULL); + GST_DEBUG ("got caps %" GST_PTR_FORMAT, out); + fail_unless (gst_caps_is_equal (out, test1)); + gst_caps_unref (out); + gst_caps_unref (restriction); + gst_caps_unref (test1); + gst_encoding_profile_unref (sprof); + + gst_caps_unref (vorbis); +} + +GST_END_TEST; + + +GST_START_TEST (test_target_naming) +{ + GstEncodingTarget *target; + + /* NULL values */ + ASSERT_CRITICAL (target = gst_encoding_target_new (NULL, NULL, NULL, NULL)); + fail_if (target != NULL); + ASSERT_CRITICAL (target = + gst_encoding_target_new ("donkey", NULL, NULL, NULL)); + fail_if (target != NULL); + ASSERT_CRITICAL (target = + gst_encoding_target_new (NULL, "donkey", NULL, NULL)); + fail_if (target != NULL); + ASSERT_CRITICAL (target = + gst_encoding_target_new (NULL, NULL, "donkey", NULL)); + fail_if (target != NULL); + + /* Name and Category validation */ + + /* empty non-NULL strings */ + fail_if (gst_encoding_target_new ("", "valid", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "", "description", NULL) != NULL); + + /* don't start with a lower case ASCII character */ + fail_if (gst_encoding_target_new ("A", "valid", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("3", "valid", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("-", "valid", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("!", "valid", "description", NULL) != NULL); + fail_if (gst_encoding_target_new (" ", "valid", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "A", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "3", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "-", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "!", "description", NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", " ", "description", NULL) != NULL); + + /* Starting with anything else is valid */ + target = gst_encoding_target_new ("a", "valid", "description", NULL); + fail_if (target == NULL); + gst_encoding_target_unref (target); + target = gst_encoding_target_new ("z", "valid", "description", NULL); + fail_if (target == NULL); + gst_encoding_target_unref (target); + target = gst_encoding_target_new ("valid", "a", "description", NULL); + fail_if (target == NULL); + gst_encoding_target_unref (target); + target = gst_encoding_target_new ("valid", "z", "description", NULL); + fail_if (target == NULL); + gst_encoding_target_unref (target); + + /* only inner valid characters are lower-case ASCII letters *OR* digits *OR* hyphens */ + fail_if (gst_encoding_target_new ("aA", "valid", "description", + NULL) != NULL); + fail_if (gst_encoding_target_new ("a!", "valid", "description", + NULL) != NULL); + fail_if (gst_encoding_target_new ("space donkeys", "valid", "description", + NULL) != NULL); + fail_if (gst_encoding_target_new ("howaboutùnicode", "valid", "description", + NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "aA", "description", + NULL) != NULL); + fail_if (gst_encoding_target_new ("valid", "a!", "description", + NULL) != NULL); + + target = + gst_encoding_target_new ("donkey-4-ever", "valid", "description", NULL); + fail_if (target == NULL); + gst_encoding_target_unref (target); + target = + gst_encoding_target_new ("valid", "donkey-4-ever", "description", NULL); + fail_if (target == NULL); + gst_encoding_target_unref (target); + +} + +GST_END_TEST; + +static GstEncodingTarget * +create_saveload_target (const gchar * targetname) +{ + GstEncodingTarget *target; + GstEncodingProfile *profile, *sprof; + GstCaps *caps, *caps2; + + GST_DEBUG ("Creating target"); + + target = gst_encoding_target_new (targetname, "herding", + "Plenty of pony glitter profiles", NULL); + caps = gst_caps_from_string ("animal/x-pony"); + profile = + (GstEncodingProfile *) gst_encoding_container_profile_new ("pony", + "I don't want a description !", caps, NULL); + gst_caps_unref (caps); + gst_encoding_target_add_profile (target, profile); + + caps = gst_caps_from_string ("audio/x-pony-song,pretty=True"); + caps2 = gst_caps_from_string ("audio/x-raw-int,channels=1,rate=44100"); + sprof = + (GstEncodingProfile *) gst_encoding_audio_profile_new (caps, NULL, caps2, + 1); + gst_encoding_container_profile_add_profile ((GstEncodingContainerProfile *) + profile, sprof); + gst_caps_unref (caps); + gst_caps_unref (caps2); + + caps = gst_caps_from_string ("video/x-glitter,sparkling=True"); + caps2 = + gst_caps_from_string + ("video/x-raw-yuv,width=640,height=480,framerate=15/1"); + sprof = (GstEncodingProfile *) + gst_encoding_video_profile_new (caps, "seriously glittery", caps2, 0); + gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile *) + sprof, TRUE); + gst_encoding_container_profile_add_profile ((GstEncodingContainerProfile *) + profile, sprof); + gst_caps_unref (caps); + gst_caps_unref (caps2); + + return target; +} + +GST_START_TEST (test_target_profile) +{ + GstEncodingTarget *target; + GstEncodingProfile *prof; + + target = create_saveload_target ("myponytarget"); + + /* NULL isn't a valid profile name */ + ASSERT_CRITICAL (gst_encoding_target_get_profile (target, NULL)); + + /* try finding a profile that doesn't exist */ + fail_if (gst_encoding_target_get_profile (target, + "no-really-does-not-exist")); + + /* try finding a profile that exists */ + prof = gst_encoding_target_get_profile (target, "pony"); + fail_if (prof == NULL); + + gst_encoding_profile_unref (prof); + gst_encoding_target_unref (target); +} + +GST_END_TEST; + +GST_START_TEST (test_saving_profile) +{ + GstEncodingTarget *orig, *loaded = NULL; + GstEncodingProfile *proforig, *profloaded; + gchar *profile_file_name; + + /* Create and store a target */ + orig = create_saveload_target ("myponytarget2"); + GST_DEBUG ("Saving target 'myponytarget2'"); + fail_unless (gst_encoding_target_save (orig, NULL)); + + /* Check we can load it */ + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "encoding-profiles", "herding", "myponytarget2.gep", NULL); + GST_DEBUG ("Loading target from '%s'", profile_file_name); + loaded = gst_encoding_target_load_from_file (profile_file_name, NULL); + fail_unless (loaded != NULL); + g_free (profile_file_name); + + GST_DEBUG ("Checking targets are equal"); + /* Check targets are identical */ + /* 1. at the target level */ + fail_unless_equals_string (gst_encoding_target_get_name (orig), + gst_encoding_target_get_name (loaded)); + fail_unless_equals_string (gst_encoding_target_get_category (orig), + gst_encoding_target_get_category (loaded)); + fail_unless_equals_string (gst_encoding_target_get_description (orig), + gst_encoding_target_get_description (loaded)); + fail_unless_equals_int (g_list_length ((GList *) + gst_encoding_target_get_profiles (loaded)), 1); + + /* 2. at the profile level */ + profloaded = + (GstEncodingProfile *) gst_encoding_target_get_profiles (loaded)->data; + proforig = + (GstEncodingProfile *) gst_encoding_target_get_profiles (orig)->data; + + fail_unless_equals_int (G_TYPE_FROM_INSTANCE (profloaded), + G_TYPE_FROM_INSTANCE (proforig)); + GST_DEBUG ("Comparing loaded:%p to original:%p", profloaded, proforig); + fail_unless (gst_encoding_profile_is_equal (profloaded, proforig)); + + gst_encoding_target_unref (orig); + gst_encoding_target_unref (loaded); +} + +GST_END_TEST; + +static void +test_individual_target (GstEncodingTarget * target) +{ + GstEncodingProfile *prof; + GstCaps *tmpcaps, *tmpcaps2; + GstEncodingProfile *sprof1, *sprof2; + + GST_DEBUG ("Checking the target properties"); + /* Check the target */ + fail_unless_equals_string (gst_encoding_target_get_name (target), + "myponytarget"); + fail_unless_equals_string (gst_encoding_target_get_category (target), + "herding"); + fail_unless_equals_string (gst_encoding_target_get_description (target), + "Plenty of pony glitter profiles"); + + GST_DEBUG ("Checking the number of profiles the target contains"); + fail_unless_equals_int (g_list_length ((GList *) + gst_encoding_target_get_profiles (target)), 1); + + + GST_DEBUG ("Checking the container profile"); + /* Check the profile */ + prof = (GstEncodingProfile *) gst_encoding_target_get_profiles (target)->data; + tmpcaps = gst_caps_from_string ("animal/x-pony"); + CHECK_PROFILE (prof, "pony", "I don't want a description !", tmpcaps, NULL, 0, + 0); + gst_caps_unref (tmpcaps); + + GST_DEBUG ("Checking the container profile has 2 stream profiles"); + /* Check the stream profiles */ + fail_unless_equals_int (g_list_length ((GList *) + gst_encoding_container_profile_get_profiles ( + (GstEncodingContainerProfile *) prof)), 2); + + GST_DEBUG ("Checking the container profile has the audio/x-pony-song stream"); + tmpcaps = gst_caps_from_string ("audio/x-pony-song,pretty=True"); + tmpcaps2 = gst_caps_from_string ("audio/x-raw-int,channels=1,rate=44100"); + sprof1 = + (GstEncodingProfile *) gst_encoding_audio_profile_new (tmpcaps, NULL, + tmpcaps2, 1); + fail_unless (gst_encoding_container_profile_contains_profile ( + (GstEncodingContainerProfile *) prof, sprof1)); + gst_encoding_profile_unref (sprof1); + gst_caps_unref (tmpcaps); + gst_caps_unref (tmpcaps2); + + GST_DEBUG ("Checking the container profile has the video//x-glitter stream"); + tmpcaps = gst_caps_from_string ("video/x-glitter,sparkling=True"); + tmpcaps2 = + gst_caps_from_string + ("video/x-raw-yuv,width=640,height=480,framerate=15/1"); + sprof2 = (GstEncodingProfile *) + gst_encoding_video_profile_new (tmpcaps, "seriously glittery", tmpcaps2, + 0); + gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile *) + sprof2, TRUE); + fail_unless (gst_encoding_container_profile_contains_profile ( + (GstEncodingContainerProfile *) prof, sprof2)); + gst_encoding_profile_unref (sprof2); + gst_caps_unref (tmpcaps); + gst_caps_unref (tmpcaps2); +} + +GST_START_TEST (test_loading_profile) +{ + GstEncodingTarget *target; + gchar *profile_file_name; + GstEncodingProfile *profile; + GstCaps *tmpcaps; + GValue strvalue = { 0, }; + GValue miniobjectvalue = { 0, }; + + /* Test loading using short method and all arguments */ + target = gst_encoding_target_load ("myponytarget", "herding", NULL); + fail_unless (target != NULL); + test_individual_target (target); + gst_encoding_target_unref (target); + + /* Test loading using short method and no category */ + target = gst_encoding_target_load ("myponytarget", NULL, NULL); + fail_unless (target != NULL); + test_individual_target (target); + gst_encoding_target_unref (target); + + /* Test loading using fully specified path */ + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "encoding-profiles", "herding", "myponytarget.gep", NULL); + + GST_DEBUG ("Loading target from '%s'", profile_file_name); + target = gst_encoding_target_load_from_file (profile_file_name, NULL); + g_free (profile_file_name); + fail_unless (target != NULL); + test_individual_target (target); + gst_encoding_target_unref (target); + + /* Test getting the profiles directly + * First without category */ + profile = gst_encoding_profile_find ("myponytarget", "pony", NULL); + fail_unless (profile != NULL); + tmpcaps = gst_caps_from_string ("animal/x-pony"); + CHECK_PROFILE (profile, "pony", "I don't want a description !", tmpcaps, NULL, + 0, 0); + gst_caps_unref (tmpcaps); + gst_encoding_profile_unref (profile); + + /* Then with a specific category */ + profile = gst_encoding_profile_find ("myponytarget", "pony", "herding"); + fail_unless (profile != NULL); + tmpcaps = gst_caps_from_string ("animal/x-pony"); + CHECK_PROFILE (profile, "pony", "I don't want a description !", tmpcaps, NULL, + 0, 0); + gst_caps_unref (tmpcaps); + gst_encoding_profile_unref (profile); + + /* For my next trick, I will need the assistance of a GValue */ + g_value_init (&strvalue, G_TYPE_STRING); + g_value_init (&miniobjectvalue, GST_TYPE_ENCODING_PROFILE); + g_value_set_static_string (&strvalue, "myponytarget/pony"); + fail_unless (g_value_transform (&strvalue, &miniobjectvalue)); + profile = (GstEncodingProfile *) gst_value_dup_mini_object (&miniobjectvalue); + fail_if (profile == NULL); + g_value_unset (&strvalue); + g_value_unset (&miniobjectvalue); + tmpcaps = gst_caps_from_string ("animal/x-pony"); + CHECK_PROFILE (profile, "pony", "I don't want a description !", tmpcaps, NULL, + 0, 0); + gst_caps_unref (tmpcaps); + gst_encoding_profile_unref (profile); + + /* Let's go crazy for error detection */ + fail_if (gst_encoding_profile_find ("myponytarget", "whales", NULL)); + fail_if (gst_encoding_profile_find ("myponytarget", "whales", "herding")); + fail_if (gst_encoding_profile_find ("myponytarget", "", NULL)); + fail_if (gst_encoding_profile_find ("", "pony", NULL)); +} + +GST_END_TEST; + +GST_START_TEST (test_target_list) +{ + GList *categories; + GList *targets; + GList *tmp; + + /* Make sure we get our test category in the available categories */ + categories = gst_encoding_list_available_categories (); + fail_if (categories == NULL); + fail_if (g_list_find_custom (categories, "herding", + (GCompareFunc) g_strcmp0) == NULL); + g_list_foreach (categories, (GFunc) g_free, NULL); + g_list_free (categories); + + /* Try getting all available targets with a specified category */ + targets = gst_encoding_list_all_targets ("herding"); + fail_if (targets == NULL); + for (tmp = targets; tmp; tmp = tmp->next) { + GstEncodingTarget *target = (GstEncodingTarget *) tmp->data; + if (!g_strcmp0 (gst_encoding_target_get_name (target), "myponytarget")) + break; + } + /* If tmp is NULL, it means we iterated the whole list without finding + * our target */ + fail_if (tmp == NULL); + g_list_foreach (targets, (GFunc) gst_mini_object_unref, NULL); + g_list_free (targets); + + /* Try getting all available targets without a specified category */ + targets = gst_encoding_list_all_targets (NULL); + fail_if (targets == NULL); + for (tmp = targets; tmp; tmp = tmp->next) { + GstEncodingTarget *target = (GstEncodingTarget *) tmp->data; + if (!g_strcmp0 (gst_encoding_target_get_name (target), "myponytarget")) + break; + } + /* If tmp is NULL, it means we iterated the whole list without finding + * our target */ + fail_if (tmp == NULL); + g_list_foreach (targets, (GFunc) gst_mini_object_unref, NULL); + g_list_free (targets); +} + +GST_END_TEST; + + +static const gchar *profile_string = "\ +[GStreamer Encoding Target]\n\ +name=myponytarget\n\ +category=herding\n\ +description=Plenty of pony glitter profiles\n\ +\n\ +[profile-pony1]\n\ +name=pony\n\ +type=container\n\ +description=I don't want a description !\n\ +format=animal/x-pony\n\ +\n\ +[streamprofile-pony11]\n\ +parent=pony\n\ +type=audio\n\ +format=audio/x-pony-song,pretty=True\n\ +restriction=audio/x-raw-int,channels=1,rate=44100\n\ +presence=1\n\ +\n\ +[streamprofile-pony12]\n\ +parent=pony\n\ +type=video\n\ +preset=seriously glittery\n\ +format=video/x-glitter,sparkling=True\n\ +restriction=video/x-raw-yuv,width=640,height=480,framerate=15/1\n\ +presence=0\n\ +variableframerate=true\n\ +"; + +static void +remove_profile_file (void) +{ + gchar *profile_file_name; + + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "encoding-profiles", "herding", "myponytarget.gep", NULL); + g_unlink (profile_file_name); + g_free (profile_file_name); + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "encoding-profiles", "herding", "myponytarget2.gep", NULL); + g_unlink (profile_file_name); + g_free (profile_file_name); +} + +static void +create_profile_file (void) +{ + gchar *profile_file_name; + gchar *profile_dir; + GError *error = NULL; + + profile_dir = + g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "encoding-profiles", "herding", NULL); + profile_file_name = + g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "encoding-profiles", "herding", "myponytarget.gep", NULL); + g_mkdir_with_parents (profile_dir, S_IRUSR | S_IWUSR | S_IXUSR); + if (!g_file_set_contents (profile_file_name, profile_string, + strlen (profile_string), &error)) + GST_WARNING ("Couldn't write contents to file : %s", error->message); + g_free (profile_dir); + g_free (profile_file_name); +} + +static void +test_setup (void) +{ + create_profile_file (); +} + +static void +test_teardown (void) +{ + remove_profile_file (); +} + + +static Suite * +profile_suite (void) +{ + Suite *s = suite_create ("profile support library"); + TCase *tc_chain = tcase_create ("general"); + gboolean can_write; + gchar *gst_dir; + + /* cehck if we can create profiles */ + gst_dir = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", NULL); + can_write = (g_access (gst_dir, R_OK | W_OK | X_OK) == 0); + g_free (gst_dir); + + suite_add_tcase (s, tc_chain); + + tcase_add_test (tc_chain, test_profile_creation); + tcase_add_test (tc_chain, test_profile_input_caps); + tcase_add_test (tc_chain, test_target_naming); + tcase_add_test (tc_chain, test_target_profile); + if (can_write) { + tcase_add_test (tc_chain, test_loading_profile); + tcase_add_test (tc_chain, test_saving_profile); + tcase_add_test (tc_chain, test_target_list); + } + + tcase_add_unchecked_fixture (tc_chain, test_setup, test_teardown); + + return s; +} + +GST_CHECK_MAIN (profile); diff --git a/tests/check/libs/rtsp.c b/tests/check/libs/rtsp.c new file mode 100644 index 0000000000..377f277bf3 --- /dev/null +++ b/tests/check/libs/rtsp.c @@ -0,0 +1,153 @@ +/* GStreamer unit tests for the RTSP support library + * + * Copyright (C) 2010 Andy Wingo + * + * 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 + +#include +#include + +GST_START_TEST (test_rtsp_url_basic) +{ + GstRTSPUrl *url = NULL; + GstRTSPResult res; + + res = gst_rtsp_url_parse ("rtsp://localhost/foo/bar", &url); + fail_unless (res == GST_RTSP_OK); + fail_unless (url != NULL); + fail_unless (url->transports & GST_RTSP_LOWER_TRANS_TCP); + fail_unless (url->transports & GST_RTSP_LOWER_TRANS_UDP); + fail_unless (url->transports & GST_RTSP_LOWER_TRANS_UDP_MCAST); + fail_unless (url->family == GST_RTSP_FAM_INET); + fail_unless (!url->user); + fail_unless (!url->passwd); + fail_unless (!strcmp (url->host, "localhost")); + /* fail_unless (url->port == GST_RTSP_DEFAULT_PORT); */ + fail_unless (!strcmp (url->abspath, "/foo/bar")); + fail_unless (!url->query); + + gst_rtsp_url_free (url); +} + +GST_END_TEST; + +GST_START_TEST (test_rtsp_url_components_1) +{ + GstRTSPUrl *url = NULL; + GstRTSPResult res; + gchar **comps = NULL; + + res = gst_rtsp_url_parse ("rtsp://localhost/foo/bar", &url); + fail_unless (res == GST_RTSP_OK); + fail_unless (url != NULL); + + comps = gst_rtsp_url_decode_path_components (url); + fail_unless (comps != NULL); + fail_unless (g_strv_length (comps) == 3); + fail_unless (!strcmp (comps[0], "")); + fail_unless (!strcmp (comps[1], "foo")); + fail_unless (!strcmp (comps[2], "bar")); + + g_strfreev (comps); + gst_rtsp_url_free (url); +} + +GST_END_TEST; + +GST_START_TEST (test_rtsp_url_components_2) +{ + GstRTSPUrl *url = NULL; + GstRTSPResult res; + gchar **comps = NULL; + + res = gst_rtsp_url_parse ("rtsp://localhost/foo%2Fbar/qux%20baz", &url); + fail_unless (res == GST_RTSP_OK); + fail_unless (url != NULL); + + comps = gst_rtsp_url_decode_path_components (url); + fail_unless (comps != NULL); + fail_unless (g_strv_length (comps) == 3); + fail_unless (!strcmp (comps[0], "")); + fail_unless (!strcmp (comps[1], "foo/bar")); + fail_unless (!strcmp (comps[2], "qux baz")); + + g_strfreev (comps); + gst_rtsp_url_free (url); +} + +GST_END_TEST; + +GST_START_TEST (test_rtsp_url_components_3) +{ + GstRTSPUrl *url = NULL; + GstRTSPResult res; + gchar **comps = NULL; + + res = gst_rtsp_url_parse ("rtsp://localhost/foo%00bar/qux%20baz", &url); + fail_unless (res == GST_RTSP_OK); + fail_unless (url != NULL); + + comps = gst_rtsp_url_decode_path_components (url); + fail_unless (comps != NULL); + fail_unless (g_strv_length (comps) == 3); + fail_unless (!strcmp (comps[0], "")); + fail_unless (!strcmp (comps[1], "foo%00bar")); + fail_unless (!strcmp (comps[2], "qux baz")); + + g_strfreev (comps); + gst_rtsp_url_free (url); +} + +GST_END_TEST; + +static Suite * +rtsp_suite (void) +{ + Suite *s = suite_create ("rtsp support library"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_rtsp_url_basic); + tcase_add_test (tc_chain, test_rtsp_url_components_1); + tcase_add_test (tc_chain, test_rtsp_url_components_2); + tcase_add_test (tc_chain, test_rtsp_url_components_3); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = rtsp_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/tests/check/libs/struct_i386.h b/tests/check/libs/struct_i386.h new file mode 100644 index 0000000000..6f504adefa --- /dev/null +++ b/tests/check/libs/struct_i386.h @@ -0,0 +1,100 @@ + +GstCheckABIStruct list[] = { + {"GstAppBufferClass", sizeof (GstAppBufferClass), 16}, + {"GstAppBuffer", sizeof (GstAppBuffer), 88}, + {"GstAppSinkCallbacks", sizeof (GstAppSinkCallbacks), 28}, + {"GstAppSinkClass", sizeof (GstAppSinkClass), 404}, + {"GstAppSink", sizeof (GstAppSink), 404}, + {"GstAppSrcCallbacks", sizeof (GstAppSrcCallbacks), 28}, + {"GstAppSrcClass", sizeof (GstAppSrcClass), 412}, + {"GstAppSrc", sizeof (GstAppSrc), 396}, + {"GstAudioClockClass", sizeof (GstAudioClockClass), 192}, + {"GstAudioClock", sizeof (GstAudioClock), 228}, + {"GstAudioFilterClass", sizeof (GstAudioFilterClass), 396}, + {"GstAudioFilter", sizeof (GstAudioFilter), 480}, + {"GstAudioSinkClass", sizeof (GstAudioSinkClass), 432}, + {"GstAudioSink", sizeof (GstAudioSink), 456}, + {"GstAudioSrcClass", sizeof (GstAudioSrcClass), 460}, + {"GstAudioSrc", sizeof (GstAudioSrc), 460}, + {"GstBaseAudioSinkClass", sizeof (GstBaseAudioSinkClass), 388}, + {"GstBaseAudioSink", sizeof (GstBaseAudioSink), 436}, + {"GstBaseAudioSrcClass", sizeof (GstBaseAudioSrcClass), 416}, + {"GstBaseAudioSrc", sizeof (GstBaseAudioSrc), 440}, + {"GstBaseRTPAudioPayloadClass", sizeof (GstBaseRTPAudioPayloadClass), 288}, + {"GstBaseRTPAudioPayload", sizeof (GstBaseRTPAudioPayload), 364}, + {"GstBaseRTPDepayloadClass", sizeof (GstBaseRTPDepayloadClass), 280}, + {"GstBaseRTPDepayload", sizeof (GstBaseRTPDepayload), 304}, + {"GstBaseRTPPayloadClass", sizeof (GstBaseRTPPayloadClass), 272}, + {"GstBaseRTPPayload", sizeof (GstBaseRTPPayload), 324}, + {"GstCddaBaseSrcClass", sizeof (GstCddaBaseSrcClass), 432}, + {"GstCddaBaseSrc", sizeof (GstCddaBaseSrc), 508}, + {"GstCddaBaseSrcTrack", sizeof (GstCddaBaseSrcTrack), 36}, + {"GstColorBalanceChannelClass", sizeof (GstColorBalanceChannelClass), 88}, + {"GstColorBalanceChannel", sizeof (GstColorBalanceChannel), 24}, + {"GstColorBalanceClass", sizeof (GstColorBalanceClass), 44}, + {"GstDiscovererClass", sizeof (GstDiscovererClass), 96}, + {"GstDiscoverer", sizeof (GstDiscoverer), 32}, + {"GstFFTF32Complex", sizeof (GstFFTF32Complex), 8}, + {"GstFFTF32", sizeof (GstFFTF32), 28}, + {"GstFFTF64Complex", sizeof (GstFFTF64Complex), 16}, + {"GstFFTF64", sizeof (GstFFTF64), 28}, + {"GstFFTS16Complex", sizeof (GstFFTS16Complex), 4}, + {"GstFFTS16", sizeof (GstFFTS16), 28}, + {"GstFFTS32Complex", sizeof (GstFFTS32Complex), 8}, + {"GstFFTS32", sizeof (GstFFTS32), 28}, + {"GstMixerClass", sizeof (GstMixerClass), 72}, + {"GstMixerOptionsClass", sizeof (GstMixerOptionsClass), 100}, + {"GstMixerOptions", sizeof (GstMixerOptions), 52}, + {"GstMixerTrackClass", sizeof (GstMixerTrackClass), 84}, + {"GstMixerTrack", sizeof (GstMixerTrack), 32}, + {"GstNavigationInterface", sizeof (GstNavigationInterface), 28}, + {"GstNetAddress", sizeof (GstNetAddress), 40}, + {"GstNetBufferClass", sizeof (GstNetBufferClass), 32}, + {"GstNetBuffer", sizeof (GstNetBuffer), 176}, + {"GstPropertyProbeInterface", sizeof (GstPropertyProbeInterface), 44}, + {"gst_riff_acid", sizeof (gst_riff_acid), 24}, + {"gst_riff_dmlh", sizeof (gst_riff_dmlh), 4}, + {"gst_riff_index_entry", sizeof (gst_riff_index_entry), 16}, + {"gst_riff_strf_auds", sizeof (gst_riff_strf_auds), 16}, + {"gst_riff_strf_iavs", sizeof (gst_riff_strf_iavs), 32}, + {"gst_riff_strf_vids", sizeof (gst_riff_strf_vids), 40}, + {"gst_riff_strh", sizeof (gst_riff_strh), 48}, + {"GstRingBufferClass", sizeof (GstRingBufferClass), 172}, + {"GstRingBuffer", sizeof (GstRingBuffer), 220}, + {"GstRingBufferSpec", sizeof (GstRingBufferSpec), 112}, + {"GstRTCPPacket", sizeof (GstRTCPPacket), 36}, + {"GstRTPPayloadInfo", sizeof (GstRTPPayloadInfo), 24}, + {"GstRTSPExtensionInterface", sizeof (GstRTSPExtensionInterface), 60}, + {"GstRTSPMessage", sizeof (GstRTSPMessage), 28}, + {"GstRTSPRange", sizeof (GstRTSPRange), 8}, + {"GstRTSPTime", sizeof (GstRTSPTime), 12}, + {"GstRTSPTimeRange", sizeof (GstRTSPTimeRange), 28}, + {"GstRTSPTransport", sizeof (GstRTSPTransport), 76}, + {"GstRTSPUrl", sizeof (GstRTSPUrl), 32}, + {"GstRTSPWatchFuncs", sizeof (GstRTSPWatchFuncs), 40}, + {"GstSDPAttribute", sizeof (GstSDPAttribute), 8}, + {"GstSDPBandwidth", sizeof (GstSDPBandwidth), 8}, + {"GstSDPConnection", sizeof (GstSDPConnection), 20}, + {"GstSDPKey", sizeof (GstSDPKey), 8}, + {"GstSDPMedia", sizeof (GstSDPMedia), 44}, + {"GstSDPMessage", sizeof (GstSDPMessage), 96}, + {"GstSDPOrigin", sizeof (GstSDPOrigin), 24}, + {"GstSDPTime", sizeof (GstSDPTime), 12}, + {"GstSDPZone", sizeof (GstSDPZone), 8}, + {"GstStreamVolumeInterface", sizeof (GstStreamVolumeInterface), 24}, + {"GstTagDemuxClass", sizeof (GstTagDemuxClass), 284}, + {"GstTagDemux", sizeof (GstTagDemux), 148}, + {"GstTunerChannelClass", sizeof (GstTunerChannelClass), 92}, + {"GstTunerChannel", sizeof (GstTunerChannel), 40}, + {"GstTunerClass", sizeof (GstTunerClass), 76}, + {"GstTunerNormClass", sizeof (GstTunerNormClass), 84}, + {"GstTunerNorm", sizeof (GstTunerNorm), 36}, + {"GstVideoFilterClass", sizeof (GstVideoFilterClass), 376}, + {"GstVideoFilter", sizeof (GstVideoFilter), 356}, + {"GstVideoOrientationInterface", sizeof (GstVideoOrientationInterface), 56}, + {"GstVideoRectangle", sizeof (GstVideoRectangle), 16}, + {"GstVideoSinkClass", sizeof (GstVideoSinkClass), 384}, + {"GstVideoSink", sizeof (GstVideoSink), 408}, + {"GstXOverlayClass", sizeof (GstXOverlayClass), 32}, + {NULL, 0, 0} +}; diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index 841338093f..48c38a55c4 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -8,8 +8,8 @@ if USE_GIO GIO_SUBDIRS = gio endif -SUBDIRS = app dynamic $(FT2_SUBDIRS) $(GIO_SUBDIRS) overlay playrec volume v4l +SUBDIRS = app dynamic $(FT2_SUBDIRS) $(GIO_SUBDIRS) overlay playrec volume v4l encoding -DIST_SUBDIRS = app dynamic gio overlay seek snapshot playrec volume v4l +DIST_SUBDIRS = app dynamic gio overlay seek snapshot playrec volume v4l encoding include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/tests/examples/encoding/.gitignore b/tests/examples/encoding/.gitignore new file mode 100644 index 0000000000..c684c2e50a --- /dev/null +++ b/tests/examples/encoding/.gitignore @@ -0,0 +1 @@ +encoding diff --git a/tests/examples/encoding/Makefile.am b/tests/examples/encoding/Makefile.am new file mode 100644 index 0000000000..2bcc902878 --- /dev/null +++ b/tests/examples/encoding/Makefile.am @@ -0,0 +1,12 @@ +examples = encoding + +encoding_SOURCES = gstcapslist.c encoding.c +EXTRA_DIST = gstcapslist.h + +noinst_PROGRAMS = $(examples) + +LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \ + $(GST_LIBS) +AM_CFLAGS = -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) diff --git a/tests/examples/encoding/encoding.c b/tests/examples/encoding/encoding.c new file mode 100644 index 0000000000..89674ce445 --- /dev/null +++ b/tests/examples/encoding/encoding.c @@ -0,0 +1,512 @@ +/* Example application for using GstProfile and encodebin + * Copyright (C) 2009 Edward Hervey + * + * 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 +#include +#include +#include +#include +#include +#include "gstcapslist.h" + +static gboolean silent = FALSE; + +static void +list_codecs (void) +{ + GstCaps *l; + GstCaps *caps; + guint i, len; + + caps = gst_caps_new_empty (); + + g_print ("Available container formats:\n"); + l = gst_caps_list_container_formats (GST_RANK_NONE); + len = gst_caps_get_size (l); + for (i = 0; i < len; i++) { + GstStructure *st = gst_caps_steal_structure (l, 0); + gchar *tmpstr, *desc; + + gst_caps_append_structure (caps, st); + + tmpstr = gst_caps_to_string (caps); + desc = gst_pb_utils_get_codec_description (caps); + g_print (" %s - %s\n", desc, tmpstr); + g_free (tmpstr); + if (desc) + g_free (desc); + gst_caps_remove_structure (caps, 0); + } + g_print ("\n"); + gst_caps_unref (l); + + g_print ("Available video codecs:\n"); + l = gst_caps_list_video_encoding_formats (GST_RANK_NONE); + len = gst_caps_get_size (l); + for (i = 0; i < len; i++) { + GstStructure *st = gst_caps_steal_structure (l, 0); + gchar *tmpstr, *desc; + + gst_caps_append_structure (caps, st); + + tmpstr = gst_caps_to_string (caps); + desc = gst_pb_utils_get_codec_description (caps); + g_print (" %s - %s\n", desc, tmpstr); + g_free (tmpstr); + if (desc) + g_free (desc); + gst_caps_remove_structure (caps, 0); + } + g_print ("\n"); + gst_caps_unref (l); + + g_print ("Available audio codecs:\n"); + l = gst_caps_list_audio_encoding_formats (GST_RANK_NONE); + len = gst_caps_get_size (l); + for (i = 0; i < len; i++) { + GstStructure *st = gst_caps_steal_structure (l, 0); + gchar *tmpstr, *desc; + + gst_caps_append_structure (caps, st); + + tmpstr = gst_caps_to_string (caps); + desc = gst_pb_utils_get_codec_description (caps); + g_print (" %s - %s\n", desc, tmpstr); + g_free (tmpstr); + if (desc) + g_free (desc); + gst_caps_remove_structure (caps, 0); + } + g_print ("\n"); + gst_caps_unref (l); + + gst_caps_unref (caps); +} + +static gchar * +generate_filename (const GstCaps * container, const GstCaps * vcodec, + const GstCaps * acodec) +{ + gchar *a, *b, *c; + gchar *res = NULL; + guint i; + + a = gst_pb_utils_get_codec_description (container); + b = gst_pb_utils_get_codec_description (vcodec); + c = gst_pb_utils_get_codec_description (acodec); + + if (!a) + a = g_strdup_printf ("%.10s", + g_uri_escape_string (gst_caps_to_string (container), NULL, FALSE)); + if (!b) + b = g_strdup_printf ("%.10s", + g_uri_escape_string (gst_caps_to_string (vcodec), NULL, FALSE)); + if (!c) + c = g_strdup_printf ("%.10s", + g_uri_escape_string (gst_caps_to_string (acodec), NULL, FALSE)); + + for (i = 0; i < 256 && res == NULL; i++) { + res = g_strdup_printf ("%s-%s-%s-%d.file", a, b, c, i); + if (g_file_test (res, G_FILE_TEST_EXISTS)) { + g_free (res); + res = NULL; + } + } + /* Make sure file doesn't already exist */ + + g_free (a); + g_free (b); + g_free (c); + + return res; +} + +static GstEncodingProfile * +create_profile (GstCaps * cf, GstCaps * vf, GstCaps * af) +{ + GstEncodingContainerProfile *cprof = NULL; + + cprof = + gst_encoding_container_profile_new ((gchar *) "test-application-profile", + NULL, cf, NULL); + + if (vf) + gst_encoding_container_profile_add_profile (cprof, + (GstEncodingProfile *) gst_encoding_video_profile_new (vf, + NULL, NULL, 0)); + if (af) + gst_encoding_container_profile_add_profile (cprof, (GstEncodingProfile *) + gst_encoding_audio_profile_new (af, NULL, NULL, 0)); + + /* Let's print out some info */ + if (!silent) { + gchar *desc = gst_pb_utils_get_codec_description (cf); + gchar *cd = gst_caps_to_string (cf); + g_print ("Encoding parameters\n"); + g_print (" Container format : %s (%s)\n", desc, cd); + g_free (desc); + g_free (cd); + if (vf) { + desc = gst_pb_utils_get_codec_description (vf); + cd = gst_caps_to_string (vf); + g_print (" Video format : %s (%s)\n", desc, cd); + g_free (desc); + g_free (cd); + } + if (af) { + desc = gst_pb_utils_get_codec_description (af); + cd = gst_caps_to_string (af); + g_print (" Audio format : %s (%s)\n", desc, cd); + g_free (desc); + g_free (cd); + } + } + + return (GstEncodingProfile *) cprof; +} + +static GstEncodingProfile * +create_profile_from_string (gchar * format, gchar * vformat, gchar * aformat) +{ + GstEncodingProfile *prof = NULL; + GstCaps *cf = NULL, *vf = NULL, *af = NULL; + + if (format) + cf = gst_caps_from_string (format); + if (vformat) + vf = gst_caps_from_string (vformat); + if (aformat) + af = gst_caps_from_string (aformat); + + if (G_UNLIKELY ((vformat && (vf == NULL)) || (aformat && (af == NULL)))) + goto beach; + + prof = create_profile (cf, vf, af); + +beach: + if (cf) + gst_caps_unref (cf); + if (vf) + gst_caps_unref (vf); + if (af) + gst_caps_unref (af); + + return prof; +} + +static void +pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstElement * encodebin) +{ + GstPad *sinkpad; + + sinkpad = gst_element_get_compatible_pad (encodebin, pad, NULL); + + if (sinkpad == NULL) { + GstCaps *caps; + + /* Ask encodebin for a compatible pad */ + caps = gst_pad_get_caps (pad); + g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad); + if (caps) + gst_caps_unref (caps); + } + if (sinkpad == NULL) { + g_print ("Couldn't get an encoding channel for pad %s:%s\n", + GST_DEBUG_PAD_NAME (pad)); + return; + } + + if (G_UNLIKELY (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)) { + g_print ("Couldn't link pads\n"); + } + + return; +} + +static gboolean +autoplug_continue_cb (GstElement * uridecodebin, GstPad * somepad, + GstCaps * caps, GstElement * encodebin) +{ + GstPad *sinkpad; + + g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad); + + if (sinkpad == NULL) + return TRUE; + + return FALSE; +} + +static void +bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop) +{ + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + g_print ("ERROR\n"); + gst_bus_set_flushing (bus, TRUE); + g_main_loop_quit (mainloop); + break; + case GST_MESSAGE_EOS: + g_print ("Done\n"); + g_main_loop_quit (mainloop); + break; + default: + break; + } +} + +static void +transcode_file (gchar * uri, gchar * outputuri, GstEncodingProfile * prof) +{ + GstElement *pipeline; + GstElement *src; + GstElement *ebin; + GstElement *sink; + GstBus *bus; + GstCaps *profilecaps, *rescaps; + GMainLoop *mainloop; + + g_print (" Input URI : %s\n", uri); + g_print (" Output URI : %s\n", outputuri); + + sink = gst_element_make_from_uri (GST_URI_SINK, outputuri, "sink"); + if (G_UNLIKELY (sink == NULL)) { + g_print ("Can't create output sink, most likely invalid output URI !\n"); + return; + } + + src = gst_element_factory_make ("uridecodebin", NULL); + if (G_UNLIKELY (src == NULL)) { + g_print ("Can't create uridecodebin for input URI, aborting!\n"); + return; + } + + /* Figure out the streams that can be passed as-is to encodebin */ + g_object_get (src, "caps", &rescaps, NULL); + rescaps = gst_caps_copy (rescaps); + profilecaps = gst_encoding_profile_get_input_caps (prof); + gst_caps_append (rescaps, profilecaps); + + /* Set properties */ + g_object_set (src, "uri", uri, "caps", rescaps, NULL); + + ebin = gst_element_factory_make ("encodebin", NULL); + g_object_set (ebin, "profile", prof, NULL); + + g_signal_connect (src, "autoplug-continue", G_CALLBACK (autoplug_continue_cb), + ebin); + g_signal_connect (src, "pad-added", G_CALLBACK (pad_added_cb), ebin); + + pipeline = gst_pipeline_new ("encoding-pipeline"); + + gst_bin_add_many (GST_BIN (pipeline), src, ebin, sink, NULL); + + gst_element_link (ebin, sink); + + mainloop = g_main_loop_new (NULL, FALSE); + + bus = gst_pipeline_get_bus ((GstPipeline *) pipeline); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop); + + if (gst_element_set_state (pipeline, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + g_print ("Failed to start the encoding\n"); + return; + } + + g_main_loop_run (mainloop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +static gchar * +ensure_uri (gchar * location) +{ + gchar *res; + gchar *path; + + if (gst_uri_is_valid (location)) + return g_strdup (location); + + if (!g_path_is_absolute (location)) { + gchar *cur_dir; + cur_dir = g_get_current_dir (); + path = g_build_filename (cur_dir, location, NULL); + g_free (cur_dir); + } else + path = g_strdup (location); + + res = g_filename_to_uri (path, NULL, NULL); + g_free (path); + + return res; +} + +int +main (int argc, char **argv) +{ + GError *err = NULL; + gchar *outputuri = NULL; + gchar *format = NULL; + gchar *aformat = NULL; + gchar *vformat = NULL; + gboolean allmissing = FALSE; + gboolean listcodecs = FALSE; + GOptionEntry options[] = { + {"silent", 's', 0, G_OPTION_ARG_NONE, &silent, + "Don't output the information structure", NULL}, + {"outputuri", 'o', 0, G_OPTION_ARG_STRING, &outputuri, + "URI to encode to", "URI (://)"}, + {"format", 'f', 0, G_OPTION_ARG_STRING, &format, + "Container format", ""}, + {"vformat", 'v', 0, G_OPTION_ARG_STRING, &vformat, + "Video format", ""}, + {"aformat", 'a', 0, G_OPTION_ARG_STRING, &aformat, + "Audio format", ""}, + {"allmissing", 'm', 0, G_OPTION_ARG_NONE, &allmissing, + "encode to all matching format/codec that aren't specified", NULL}, + {"list-codecs", 'l', 0, G_OPTION_ARG_NONE, &listcodecs, + "list all available codecs and container formats", NULL}, + {NULL} + }; + GOptionContext *ctx; + GstEncodingProfile *prof; + gchar *inputuri; + + if (!g_thread_supported ()) + g_thread_init (NULL); + + ctx = g_option_context_new ("- encode URIs with GstProfile and encodebin"); + g_option_context_add_main_entries (ctx, options, NULL); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_print ("Error initializing: %s\n", err->message); + exit (1); + } + + if (listcodecs) { + list_codecs (); + g_option_context_free (ctx); + exit (0); + } + + if (outputuri == NULL || argc != 2) { + g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL)); + g_option_context_free (ctx); + exit (-1); + } + + g_option_context_free (ctx); + + /* Fixup outputuri to be a URI */ + inputuri = ensure_uri (argv[1]); + outputuri = ensure_uri (outputuri); + + if (allmissing) { + GList *muxers; + GstCaps *formats = NULL; + GstCaps *vformats = NULL; + GstCaps *aformats = NULL; + guint f, v, a, flen, vlen, alen; + + if (!format) + formats = gst_caps_list_container_formats (GST_RANK_NONE); + else + formats = gst_caps_from_string (format); + + if (!vformat) + vformats = gst_caps_list_video_encoding_formats (GST_RANK_NONE); + else + vformats = gst_caps_from_string (vformat); + + if (!aformat) + aformats = gst_caps_list_audio_encoding_formats (GST_RANK_NONE); + else + aformats = gst_caps_from_string (aformat); + muxers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER, + GST_RANK_NONE); + + flen = gst_caps_get_size (formats); + + for (f = 0; f < flen; f++) { + GstCaps *container = + gst_caps_new_full (gst_caps_steal_structure (formats, 0), NULL); + GstCaps *compatv = + gst_caps_list_compatible_codecs (container, vformats, muxers); + GstCaps *compata = + gst_caps_list_compatible_codecs (container, aformats, muxers); + + vlen = gst_caps_get_size (compatv); + alen = gst_caps_get_size (compata); + + + for (v = 0; v < vlen; v++) { + GstCaps *vcodec = + gst_caps_new_full (gst_structure_copy (gst_caps_get_structure + (compatv, v)), NULL); + for (a = 0; a < alen; a++) { + GstCaps *acodec = + gst_caps_new_full (gst_structure_copy (gst_caps_get_structure + (compata, a)), NULL); + + prof = + create_profile ((GstCaps *) container, (GstCaps *) vcodec, + (GstCaps *) acodec); + if (G_UNLIKELY (prof == NULL)) { + g_print ("Wrong arguments\n"); + break; + } + outputuri = + ensure_uri (generate_filename (container, vcodec, acodec)); + transcode_file (inputuri, outputuri, prof); + gst_encoding_profile_unref (prof); + + gst_caps_unref (acodec); + } + gst_caps_unref (vcodec); + } + gst_caps_unref (container); + } + + } else { + + /* Create the profile */ + prof = create_profile_from_string (format, vformat, aformat); + if (G_UNLIKELY (prof == NULL)) { + g_print ("Encoding arguments are not valid !\n"); + return 1; + } + + /* Trancode file */ + transcode_file (inputuri, outputuri, prof); + + /* cleanup */ + gst_encoding_profile_unref (prof); + + } + return 0; +} diff --git a/tests/examples/encoding/gstcapslist.c b/tests/examples/encoding/gstcapslist.c new file mode 100644 index 0000000000..4908e438c5 --- /dev/null +++ b/tests/examples/encoding/gstcapslist.c @@ -0,0 +1,286 @@ +/* GStreamer + * Copyright (C) <2010> Edward Hervey + * + * 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. + */ + +#include "gstcapslist.h" + +/* + * Caps listing convenience functions + */ + +static gboolean +remove_range_foreach (GQuark field_id, const GValue * value, GstStructure * st) +{ + GType ftype = G_VALUE_TYPE (value); + const gchar *fname; + + if (ftype == GST_TYPE_INT_RANGE || ftype == GST_TYPE_DOUBLE_RANGE || + ftype == GST_TYPE_FRACTION_RANGE) { + gst_structure_remove_field (st, g_quark_to_string (field_id)); + return FALSE; + } + + fname = g_quark_to_string (field_id); + + /* if (strstr (fname, "framerate") || strstr (fname, "pixel-aspect-ratio") || */ + /* strstr (fname, "rate")) { */ + /* gst_structure_remove_field (st, g_quark_to_string (field_id)); */ + /* return FALSE; */ + /* } */ + + return TRUE; +} + +static void +clear_caps (GstCaps * caps, GstCaps * rescaps) +{ + GstCaps *res; + GstStructure *st; + guint i; + + res = gst_caps_make_writable (caps); + + GST_DEBUG ("incoming caps %" GST_PTR_FORMAT, res); + + /* Remove width/height/framerate/depth/width fields */ + for (i = gst_caps_get_size (res); i; i--) { + st = gst_caps_get_structure (res, i - 1); + + /* Remove range fields */ + while (!gst_structure_foreach (st, + (GstStructureForeachFunc) remove_range_foreach, st)); + } + + GST_DEBUG ("stripped %" GST_PTR_FORMAT, res); + + /* And append to list without duplicates */ + while ((st = gst_caps_steal_structure (res, 0))) { + /* Skip fake codecs/containers */ + if (gst_structure_has_name (st, "audio/x-raw-int") || + gst_structure_has_name (st, "audio/x-raw-float") || + gst_structure_has_name (st, "video/x-raw-yuv") || + gst_structure_has_name (st, "video/x-raw-rgb") || + gst_structure_has_name (st, "unknown/unknown")) { + gst_structure_free (st); + continue; + } + + gst_caps_append_structure (rescaps, st); + } + + gst_caps_unref (res); +} + +static GstCaps * +get_all_caps (GList * elements, GstPadDirection direction) +{ + GstCaps *res = NULL, *res2; + GList *tmp; + + res = gst_caps_new_empty (); + + for (tmp = elements; tmp; tmp = tmp->next) { + GstElementFactory *factory = (GstElementFactory *) tmp->data; + const GList *templates; + GList *walk; + + templates = gst_element_factory_get_static_pad_templates (factory); + for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { + GstStaticPadTemplate *templ = walk->data; + if (templ->direction == direction) + clear_caps (gst_static_caps_get (&templ->static_caps), res); + } + } + + res2 = gst_caps_normalize (res); + gst_caps_unref (res); + return res2; +} + +/** + * gst_caps_list_container_formats: + * @minrank: The minimum #GstRank + * + * Returns a #GstCaps corresponding to all the container formats + * one can mux to on this system. + * + * Returns: A #GstCaps. Unref with %gst_caps_unref when done with it. + */ +GstCaps * +gst_caps_list_container_formats (GstRank minrank) +{ + GstCaps *res; + GList *muxers; + + muxers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER, + minrank); + res = get_all_caps (muxers, GST_PAD_SRC); + gst_plugin_feature_list_free (muxers); + + return res; +} + +static GstCaps * +gst_caps_list_encoding_formats (GstRank minrank) +{ + GstCaps *res; + GList *encoders; + + encoders = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER, + minrank); + res = get_all_caps (encoders, GST_PAD_SRC); + gst_plugin_feature_list_free (encoders); + + return res; +} + +/** + * gst_caps_list_video_encoding_formats: + * @minrank: The minimum #GstRank + * + * Returns a #GstCaps corresponding to all the video or image formats one + * can encode to on this system. + * + * Returns: A #GstCaps. Unref with %gst_caps_unref when done with it. + */ +GstCaps * +gst_caps_list_video_encoding_formats (GstRank minrank) +{ + GstCaps *res; + GList *encoders; + + encoders = + gst_element_factory_list_get_elements + (GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER, minrank); + res = get_all_caps (encoders, GST_PAD_SRC); + gst_plugin_feature_list_free (encoders); + + return res; +} + + +/** + * gst_caps_list_audio_encoding_formats: + * @minrank: The minimum #GstRank + * + * Returns a #GstCaps corresponding to all the audio formats one + * can encode to on this system. + * + * Returns: A #GstCaps. Unref with %gst_caps_unref when done with it. + */ +GstCaps * +gst_caps_list_audio_encoding_formats (GstRank minrank) +{ + GstCaps *res; + GList *encoders; + + encoders = + gst_element_factory_list_get_elements + (GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER, minrank); + res = get_all_caps (encoders, GST_PAD_SRC); + gst_plugin_feature_list_free (encoders); + + return res; +} + +/** + * gst_caps_list_compatible_codecs: + * @containerformat: A #GstCaps corresponding to a container format + * @codecformats: An optional #GstCaps of codec formats + * @muxers: An optional #GList of muxer #GstElementFactory. + * + * Returns an array of #GstCaps corresponding to the audio/video/text formats + * one can encode to and that can be muxed in the provided @containerformat. + * + * If specified, only the #GstCaps contained in @codecformats will be checked + * against, else all compatible audio/video formats will be returned. + * + * If specified, only the #GstElementFactory contained in @muxers will be checked, + * else all available muxers on the system will be checked. + * + * Returns: A #GstCaps containing all compatible formats. Unref with %gst_caps_unref + * when done. + */ +GstCaps * +gst_caps_list_compatible_codecs (const GstCaps * containerformat, + GstCaps * codecformats, GList * muxers) +{ + const GList *templates; + GstElementFactory *factory; + GList *walk; + GstCaps *res = NULL; + GstCaps *tmpcaps; + GList *tmp; + gboolean hadmuxers = (muxers != NULL); + gboolean hadcodecs = (codecformats != NULL); + + GST_DEBUG ("containerformat: %" GST_PTR_FORMAT, containerformat); + GST_DEBUG ("codecformats: %" GST_PTR_FORMAT, codecformats); + + if (!hadmuxers) + muxers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER, + GST_RANK_NONE); + if (!hadcodecs) + codecformats = gst_caps_list_encoding_formats (GST_RANK_NONE); + + /* Get the highest rank muxer matching containerformat */ + tmp = + gst_element_factory_list_filter (muxers, containerformat, GST_PAD_SRC, + TRUE); + if (G_UNLIKELY (tmp == NULL)) + goto beach; + + factory = (GstElementFactory *) tmp->data; + + GST_DEBUG ("Trying with factory %s", + gst_element_factory_get_longname (factory)); + + /* Match all muxer sink pad templates against the available codec formats */ + templates = gst_element_factory_get_static_pad_templates (factory); + gst_plugin_feature_list_free (tmp); + + tmpcaps = gst_caps_new_empty (); + + for (walk = (GList *) templates; walk; walk = walk->next) { + GstStaticPadTemplate *templ = walk->data; + + if (templ->direction == GST_PAD_SINK) { + GstCaps *templ_caps; + + templ_caps = gst_static_caps_get (&templ->static_caps); + gst_caps_append (tmpcaps, gst_caps_copy (templ_caps)); + } + } + + res = gst_caps_intersect (tmpcaps, codecformats); + gst_caps_unref (tmpcaps); + +beach: + if (!hadmuxers) + gst_plugin_feature_list_free (muxers); + if (!hadcodecs) + gst_caps_unref (codecformats); + + tmpcaps = gst_caps_normalize (res); + gst_caps_unref (res); + + return tmpcaps; +} diff --git a/tests/examples/encoding/gstcapslist.h b/tests/examples/encoding/gstcapslist.h new file mode 100644 index 0000000000..fa0ed83bfe --- /dev/null +++ b/tests/examples/encoding/gstcapslist.h @@ -0,0 +1,35 @@ +/* GStreamer + * Copyright (C) <2010> Edward Hervey + * + * 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. + */ + +#include + +GstCaps *gst_caps_list_compatible_codecs (const GstCaps *containerformat, + GstCaps *codecformats, + GList *muxers); + +GstCaps *gst_caps_list_compatible_containers (GstCaps *mediaformat, + GList *containerformats); + + +GstCaps *gst_caps_list_container_formats (GstRank minrank); + +GstCaps *gst_caps_list_video_encoding_formats (GstRank minrank); + +GstCaps *gst_caps_list_audio_encoding_formats (GstRank minrank); + diff --git a/tests/examples/seek/jsseek.c b/tests/examples/seek/jsseek.c index fcff009577..d12840baaf 100644 --- a/tests/examples/seek/jsseek.c +++ b/tests/examples/seek/jsseek.c @@ -1408,7 +1408,7 @@ set_update_fill (gboolean active) if (active) { if (fill_id == 0) { fill_id = - g_timeout_add (FILL_INTERVAL, (GtkFunction) update_fill, pipeline); + g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline); } } else { if (fill_id) { @@ -1427,7 +1427,7 @@ set_update_scale (gboolean active) if (active) { if (update_id == 0) { update_id = - g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); + g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline); } } else { if (update_id) { @@ -2841,8 +2841,6 @@ main (int argc, char **argv) shuttle_hscale = gtk_hscale_new (shuttle_adjustment); gtk_scale_set_digits (GTK_SCALE (shuttle_hscale), 2); gtk_scale_set_value_pos (GTK_SCALE (shuttle_hscale), GTK_POS_TOP); - gtk_range_set_update_policy (GTK_RANGE (shuttle_hscale), - GTK_UPDATE_CONTINUOUS); g_signal_connect (shuttle_hscale, "value_changed", G_CALLBACK (shuttle_value_changed), pipeline); g_signal_connect (shuttle_hscale, "format_value", @@ -2861,7 +2859,6 @@ main (int argc, char **argv) gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT); gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE); gtk_range_set_fill_level (GTK_RANGE (hscale), 100.0); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); g_signal_connect (hscale, "button_press_event", G_CALLBACK (start_seek), pipeline); diff --git a/tests/examples/seek/scrubby.c b/tests/examples/seek/scrubby.c index aa6b358501..c9a02f42bb 100644 --- a/tests/examples/seek/scrubby.c +++ b/tests/examples/seek/scrubby.c @@ -314,7 +314,7 @@ static gboolean stop_seek (GtkWidget * widget, gpointer user_data) { update_id = - g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); + g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline); GST_DEBUG ("stop seek"); @@ -340,7 +340,7 @@ play_cb (GtkButton * button, gpointer data) gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); gst_element_set_state (pipeline, GST_STATE_PLAYING); update_id = - g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); + g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline); } } @@ -507,13 +507,11 @@ main (int argc, char **argv) 1.0, 1.0)); hscale = gtk_hscale_new (adjustment); gtk_scale_set_digits (GTK_SCALE (hscale), 2); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); sadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (1.0, 0.0, 5.0, 0.1, 1.0, 0.0)); shscale = gtk_hscale_new (sadjustment); gtk_scale_set_digits (GTK_SCALE (shscale), 2); - gtk_range_set_update_policy (GTK_RANGE (shscale), GTK_UPDATE_CONTINUOUS); schanged_id = g_signal_connect (shscale, "value_changed", G_CALLBACK (speed_cb), diff --git a/tests/examples/seek/seek.c b/tests/examples/seek/seek.c index 5f0e1e5e1b..b82a851f1c 100644 --- a/tests/examples/seek/seek.c +++ b/tests/examples/seek/seek.c @@ -1398,7 +1398,7 @@ set_update_fill (gboolean active) if (active) { if (fill_id == 0) { fill_id = - g_timeout_add (FILL_INTERVAL, (GtkFunction) update_fill, pipeline); + g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline); } } else { if (fill_id) { @@ -1417,7 +1417,7 @@ set_update_scale (gboolean active) if (active) { if (update_id == 0) { update_id = - g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline); + g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline); } } else { if (update_id) { @@ -2402,6 +2402,7 @@ msg_buffering (GstBus * bus, GstMessage * message, GstPipeline * data) do_download_buffering (percent); break; case GST_BUFFERING_LIVE: + is_live = TRUE; case GST_BUFFERING_TIMESHIFT: case GST_BUFFERING_STREAM: do_stream_buffering (percent); @@ -2786,8 +2787,6 @@ main (int argc, char **argv) shuttle_hscale = gtk_hscale_new (shuttle_adjustment); gtk_scale_set_digits (GTK_SCALE (shuttle_hscale), 2); gtk_scale_set_value_pos (GTK_SCALE (shuttle_hscale), GTK_POS_TOP); - gtk_range_set_update_policy (GTK_RANGE (shuttle_hscale), - GTK_UPDATE_CONTINUOUS); g_signal_connect (shuttle_hscale, "value_changed", G_CALLBACK (shuttle_value_changed), pipeline); g_signal_connect (shuttle_hscale, "format_value", @@ -2806,7 +2805,6 @@ main (int argc, char **argv) gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT); gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE); gtk_range_set_fill_level (GTK_RANGE (hscale), N_GRAD); - gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS); g_signal_connect (hscale, "button_press_event", G_CALLBACK (start_seek), pipeline); diff --git a/tests/examples/snapshot/snapshot.c b/tests/examples/snapshot/snapshot.c index b307111340..14f28093e4 100644 --- a/tests/examples/snapshot/snapshot.c +++ b/tests/examples/snapshot/snapshot.c @@ -41,7 +41,7 @@ main (int argc, char *argv[]) gst_init (&argc, &argv); if (argc != 2) { - g_print ("usage: %s \n Writes snapshot.png in the current directory", + g_print ("usage: %s \n Writes snapshot.png in the current directory\n", argv[0]); exit (-1); } @@ -53,7 +53,7 @@ main (int argc, char *argv[]) pipeline = gst_parse_launch (descr, &error); if (error != NULL) { - g_print ("could not construct pipeline: %s", error->message); + g_print ("could not construct pipeline: %s\n", error->message); g_error_free (error); exit (-1); } @@ -99,8 +99,8 @@ main (int argc, char *argv[]) * by seeking to somewhere else we have a bigger chance of getting something * more interesting. An optimisation would be to detect black images and then * seek a little more */ - gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, - position); + gst_element_seek_simple (pipeline, GST_FORMAT_TIME, + GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH, position); /* get the preroll buffer from appsink, this block untils appsink really * prerolls */ diff --git a/tests/icles/.gitignore b/tests/icles/.gitignore index 72ace7370c..bd175b1a8b 100644 --- a/tests/icles/.gitignore +++ b/tests/icles/.gitignore @@ -1,4 +1,6 @@ audio-trickplay +input-selector-test +output-selector-test playbin-text position-formats stress-playbin diff --git a/tests/icles/Makefile.am b/tests/icles/Makefile.am index 8fbdb92713..d7a3236b90 100644 --- a/tests/icles/Makefile.am +++ b/tests/icles/Makefile.am @@ -26,6 +26,18 @@ test_xoverlay_LDADD = $(GST_LIBS) $(X_LIBS) $(LIBM) $(GTK_LIBS) \ $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-$(GST_MAJORMINOR).la endif +output_selector_test_SOURCES = output-selector-test.c +output_selector_test_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +output_selector_test_LDADD = $(GST_LIBS) +output_selector_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +input_selector_test_SOURCES = input-selector-test.c +input_selector_test_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +input_selector_test_LDADD = $(GST_LIBS) +input_selector_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +X_TESTS += output-selector-test input-selector-test + else X_TESTS = endif diff --git a/tests/icles/input-selector-test.c b/tests/icles/input-selector-test.c new file mode 100644 index 0000000000..4742c03376 --- /dev/null +++ b/tests/icles/input-selector-test.c @@ -0,0 +1,162 @@ +/* GStreamer + * Copyright (C) 2003 Julien Moutte + * + * 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 +#include + + +#include + +static GMainLoop *loop = NULL; + +static gboolean +my_bus_callback (GstBus * bus, GstMessage * message, gpointer data) +{ + g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message)); + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR:{ + GError *err; + gchar *debug; + + gst_message_parse_error (message, &err, &debug); + g_print ("Error: %s\n", err->message); + g_error_free (err); + g_free (debug); + + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_EOS: + /* end-of-stream */ + g_main_loop_quit (loop); + break; + default: + /* unhandled message */ + break; + } + + /* we want to be notified again the next time there is a message + * on the bus, so returning TRUE (FALSE means we want to stop watching + * for messages on the bus and our callback should not be called again) + */ + return TRUE; +} + + + +static gboolean +switch_timer (GstElement * video_switch) +{ + gint nb_sources; + GstPad *active_pad, *new_pad; + gchar *active_name; + + g_message ("switching"); + g_object_get (G_OBJECT (video_switch), "n-pads", &nb_sources, NULL); + g_object_get (G_OBJECT (video_switch), "active-pad", &active_pad, NULL); + + active_name = gst_pad_get_name (active_pad); + if (strcmp (active_name, "sink0") == 0) { + new_pad = gst_element_get_static_pad (video_switch, "sink1"); + } else { + new_pad = gst_element_get_static_pad (video_switch, "sink0"); + } + g_object_set (G_OBJECT (video_switch), "active-pad", new_pad, NULL); + g_free (active_name); + gst_object_unref (new_pad); + + g_message ("current number of sources : %d, active source %s", + nb_sources, gst_pad_get_name (active_pad)); + + return (GST_STATE (GST_ELEMENT (video_switch)) == GST_STATE_PLAYING); +} + +static void +last_message_received (GObject * segment) +{ + gchar *last_message; + + g_object_get (segment, "last_message", &last_message, NULL); + g_print ("last-message: %s\n", last_message); + g_free (last_message); +} + +int +main (int argc, char *argv[]) +{ + GstElement *pipeline, *src1, *src2, *video_switch, *video_sink, *segment; + GstElement *sink1_sync, *sink2_sync, *capsfilter; + GstBus *bus; + + /* Initing GStreamer library */ + gst_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + + pipeline = gst_pipeline_new ("pipeline"); + src1 = gst_element_factory_make ("videotestsrc", "src1"); + g_object_set (G_OBJECT (src1), "pattern", 0, NULL); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + g_object_set (G_OBJECT (src2), "pattern", 1, NULL); + capsfilter = gst_element_factory_make ("capsfilter", "caps0"); + g_object_set (G_OBJECT (capsfilter), "caps", + gst_caps_from_string ("video/x-raw-rgb,width=640,height=480"), NULL); + video_switch = gst_element_factory_make ("input-selector", "video_switch"); + segment = gst_element_factory_make ("identity", "identity-segment"); + g_object_set (G_OBJECT (segment), "silent", TRUE, NULL); + g_signal_connect (G_OBJECT (segment), "notify::last-message", + G_CALLBACK (last_message_received), segment); + g_object_set (G_OBJECT (segment), "single-segment", TRUE, NULL); + video_sink = gst_element_factory_make ("ximagesink", "video_sink"); + g_object_set (G_OBJECT (video_sink), "sync", FALSE, NULL); + sink1_sync = gst_element_factory_make ("identity", "sink0_sync"); + g_object_set (G_OBJECT (sink1_sync), "sync", TRUE, NULL); + sink2_sync = gst_element_factory_make ("identity", "sink1_sync"); + g_object_set (G_OBJECT (sink2_sync), "sync", TRUE, NULL); + gst_bin_add_many (GST_BIN (pipeline), src1, src2, segment, video_switch, + video_sink, sink1_sync, sink2_sync, capsfilter, NULL); + gst_element_link (src1, sink1_sync); + gst_element_link (sink1_sync, video_switch); + gst_element_link (src2, capsfilter); + gst_element_link (capsfilter, sink2_sync); + gst_element_link (sink2_sync, video_switch); + gst_element_link (video_switch, segment); + gst_element_link (segment, /*scaler); + gst_element_link (scaler, */ video_sink); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, my_bus_callback, NULL); + gst_object_unref (bus); + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + + g_timeout_add (200, (GSourceFunc) switch_timer, video_switch); + + g_main_loop_run (loop); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY); + + /* unref */ + gst_object_unref (GST_OBJECT (pipeline)); + + exit (0); +} diff --git a/tests/icles/output-selector-test.c b/tests/icles/output-selector-test.c new file mode 100644 index 0000000000..24ad2835dc --- /dev/null +++ b/tests/icles/output-selector-test.c @@ -0,0 +1,173 @@ +#include + +#define SWITCH_TIMEOUT 1000 +#define NUM_VIDEO_BUFFERS 500 + +static GMainLoop *loop; + +/* Output selector src pads */ +static GstPad *osel_src1 = NULL; +static GstPad *osel_src2 = NULL; + +static gboolean +my_bus_callback (GstBus * bus, GstMessage * message, gpointer data) +{ + g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message)); + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR:{ + GError *err; + gchar *debug; + + gst_message_parse_error (message, &err, &debug); + g_print ("Error: %s\n", err->message); + g_error_free (err); + g_free (debug); + + g_main_loop_quit (loop); + break; + } + case GST_MESSAGE_EOS: + /* end-of-stream */ + g_main_loop_quit (loop); + break; + default: + /* unhandled message */ + break; + } + /* we want to be notified again the next time there is a message + * on the bus, so returning TRUE (FALSE means we want to stop watching + * for messages on the bus and our callback should not be called again) + */ + return TRUE; +} + +static gboolean +switch_cb (gpointer user_data) +{ + GstElement *sel = GST_ELEMENT (user_data); + GstPad *old_pad, *new_pad = NULL; + + g_object_get (G_OBJECT (sel), "active-pad", &old_pad, NULL); + + if (old_pad == osel_src1) + new_pad = osel_src2; + else + new_pad = osel_src1; + + g_object_set (G_OBJECT (sel), "active-pad", new_pad, NULL); + + g_print ("switched from %s:%s to %s:%s\n", GST_DEBUG_PAD_NAME (old_pad), + GST_DEBUG_PAD_NAME (new_pad)); + + gst_object_unref (old_pad); + + return TRUE; + +} + +static void +on_bin_element_added (GstBin * bin, GstElement * element, gpointer user_data) +{ + g_object_set (G_OBJECT (element), "sync", FALSE, "async", FALSE, NULL); +} + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *pipeline, *src, *toverlay, *osel, *sink1, *sink2, *c1, *c2, *c0; + GstPad *sinkpad; + GstBus *bus; + + /* init GStreamer */ + gst_init (&argc, &argv); + loop = g_main_loop_new (NULL, FALSE); + + /* create elements */ + pipeline = gst_element_factory_make ("pipeline", "pipeline"); + src = gst_element_factory_make ("videotestsrc", "src"); + c0 = gst_element_factory_make ("ffmpegcolorspace", NULL); + toverlay = gst_element_factory_make ("timeoverlay", "timeoverlay"); + osel = gst_element_factory_make ("output-selector", "osel"); + c1 = gst_element_factory_make ("ffmpegcolorspace", NULL); + c2 = gst_element_factory_make ("ffmpegcolorspace", NULL); + sink1 = gst_element_factory_make ("autovideosink", "sink1"); + sink2 = gst_element_factory_make ("autovideosink", "sink2"); + + if (!pipeline || !src || !c0 || !toverlay || !osel || !c1 || !c2 || !sink1 || + !sink2) { + g_print ("missing element\n"); + return -1; + } + + /* add them to bin */ + gst_bin_add_many (GST_BIN (pipeline), src, c0, toverlay, osel, c1, sink1, c2, + sink2, NULL); + + /* set properties */ + g_object_set (G_OBJECT (src), "is-live", TRUE, NULL); + g_object_set (G_OBJECT (src), "do-timestamp", TRUE, NULL); + g_object_set (G_OBJECT (src), "num-buffers", NUM_VIDEO_BUFFERS, NULL); + g_object_set (G_OBJECT (osel), "resend-latest", TRUE, NULL); + + /* handle deferred properties */ + g_signal_connect (G_OBJECT (sink1), "element-added", + G_CALLBACK (on_bin_element_added), NULL); + g_signal_connect (G_OBJECT (sink2), "element-added", + G_CALLBACK (on_bin_element_added), NULL); + + /* link src ! timeoverlay ! osel */ + if (!gst_element_link_many (src, c0, toverlay, osel, NULL)) { + g_print ("linking failed\n"); + return -1; + } + + /* link output 1 */ + sinkpad = gst_element_get_static_pad (c1, "sink"); + osel_src1 = gst_element_get_request_pad (osel, "src%d"); + if (gst_pad_link (osel_src1, sinkpad) != GST_PAD_LINK_OK) { + g_print ("linking output 1 converter failed\n"); + return -1; + } + gst_object_unref (sinkpad); + + if (!gst_element_link (c1, sink1)) { + g_print ("linking output 1 failed\n"); + return -1; + } + + /* link output 2 */ + sinkpad = gst_element_get_static_pad (c2, "sink"); + osel_src2 = gst_element_get_request_pad (osel, "src%d"); + if (gst_pad_link (osel_src2, sinkpad) != GST_PAD_LINK_OK) { + g_print ("linking output 2 converter failed\n"); + return -1; + } + gst_object_unref (sinkpad); + + if (!gst_element_link (c2, sink2)) { + g_print ("linking output 2 failed\n"); + return -1; + } + + /* add switch callback */ + g_timeout_add (SWITCH_TIMEOUT, switch_cb, osel); + + /* change to playing */ + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, my_bus_callback, loop); + gst_object_unref (bus); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + /* now run */ + g_main_loop_run (loop); + + /* also clean up */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (osel, osel_src1); + gst_element_release_request_pad (osel, osel_src2); + gst_object_unref (GST_OBJECT (pipeline)); + + return 0; +} diff --git a/tools/gst-discoverer.c b/tools/gst-discoverer.c index bc25409914..00d2d0f415 100644 --- a/tools/gst-discoverer.c +++ b/tools/gst-discoverer.c @@ -204,11 +204,10 @@ print_stream_info (GstDiscovererStreamInfo * info, void *depth) desc = gst_stream_video_information_to_string (info, GPOINTER_TO_INT (depth) + 1); - } - - if (desc) { - g_print ("%s", desc); - g_free (desc); + if (desc) { + g_print ("%s", desc); + g_free (desc); + } } } @@ -240,11 +239,41 @@ print_topology (GstDiscovererStreamInfo * info, gint depth) } } -static void -print_duration (GstDiscovererInfo * info, gint tab) +static gboolean +print_tag_each (GQuark field_id, const GValue * value, gpointer user_data) { - g_print ("%*s%" GST_TIME_FORMAT "\n", tab + 1, " ", + gint tab = GPOINTER_TO_INT (user_data); + gchar *ser; + + if (G_VALUE_HOLDS_STRING (value)) + ser = g_value_dup_string (value); + else if (GST_VALUE_HOLDS_BUFFER (value)) { + GstBuffer *buf = gst_value_get_buffer (value); + ser = g_strdup_printf ("", GST_BUFFER_SIZE (buf)); + } else + ser = gst_value_serialize (value); + + g_print ("%*s%s: %s\n", tab, " ", + gst_tag_get_nick (g_quark_to_string (field_id)), ser); + g_free (ser); + + return TRUE; +} + +static void +print_properties (GstDiscovererInfo * info, gint tab) +{ + const GstTagList *tags; + + g_print ("%*sDuration: %" GST_TIME_FORMAT "\n", tab + 1, " ", GST_TIME_ARGS (gst_discoverer_info_get_duration (info))); + g_print ("%*sSeekable: %s\n", tab + 1, " ", + (gst_discoverer_info_get_seekable (info) ? "yes" : "no")); + if ((tags = gst_discoverer_info_get_tags (info))) { + g_print ("%*sTags: \n", tab + 1, " "); + gst_structure_foreach ((const GstStructure *) tags, print_tag_each, + GINT_TO_POINTER (tab + 5)); + } } static void @@ -296,8 +325,8 @@ print_info (GstDiscovererInfo * info, GError * err) if ((sinfo = gst_discoverer_info_get_stream_info (info))) { g_print ("\nTopology:\n"); print_topology (sinfo, 1); - g_print ("\nDuration:\n"); - print_duration (info, 1); + g_print ("\nProperties:\n"); + print_properties (info, 1); gst_discoverer_stream_info_unref (sinfo); } @@ -462,6 +491,7 @@ main (int argc, char **argv) gst_discoverer_stop (dc); g_free (ps); + g_main_loop_unref (ml); } g_object_unref (dc); diff --git a/win32/common/_stdint.h b/win32/common/_stdint.h index c731bb5cf6..86481efef1 100644 --- a/win32/common/_stdint.h +++ b/win32/common/_stdint.h @@ -1,8 +1,8 @@ #ifndef _GST_PLUGINS_BASE__STDINT_H #define _GST_PLUGINS_BASE__STDINT_H 1 #ifndef _GENERATED_STDINT_H -#define _GENERATED_STDINT_H "gst-plugins-base 0.10.31.1" -/* generated using gnu compiler gcc (Debian 4.4.5-8) 4.4.5 */ +#define _GENERATED_STDINT_H "gst-plugins-base 0.10.32.1" +/* generated using gnu compiler gcc (Debian 4.4.5-10) 4.4.5 */ #define _STDINT_HAVE_STDINT_H 1 #include #endif diff --git a/win32/common/config.h b/win32/common/config.h index 463159c018..1b5ecec9e8 100644 --- a/win32/common/config.h +++ b/win32/common/config.h @@ -20,7 +20,7 @@ #undef CDPARANOIA_HEADERS_IN_DIR /* Default audio sink */ -#define DEFAULT_AUDIOSINK "directaudiosink" +#define DEFAULT_AUDIOSINK "directsoundsink" /* Default audio source */ #undef DEFAULT_AUDIOSRC @@ -53,6 +53,9 @@ /* The GnomeVFS modules directory. */ #undef GNOME_VFS_MODULES_DIR +/* system wide data directory */ +#define GST_DATADIR PREFIX "\\share" + /* macro to use to show function name */ #undef GST_FUNCTION @@ -68,6 +71,9 @@ /* GStreamer license */ #define GST_LICENSE "LGPL" +/* major/minor version */ +#define GST_MAJORMINOR "0.10" + /* package name in plugins */ #define GST_PACKAGE_NAME "GStreamer Base Plug-ins git" @@ -75,7 +81,7 @@ #define GST_PACKAGE_ORIGIN "Unknown package origin" /* GStreamer package release date/time for plugins as YYYY-MM-DD */ -#define GST_PACKAGE_RELEASE_DATETIME "2010-12-02T00:05Z" +#define GST_PACKAGE_RELEASE_DATETIME "2011-01-27T15:23Z" /* I know the API is subject to change. */ #undef G_UDEV_API_IS_SUBJECT_TO_CHANGE @@ -331,7 +337,7 @@ #define PACKAGE_NAME "GStreamer Base Plug-ins" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "GStreamer Base Plug-ins 0.10.31.1" +#define PACKAGE_STRING "GStreamer Base Plug-ins 0.10.32.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gst-plugins-base" @@ -340,7 +346,7 @@ #undef PACKAGE_URL /* Define to the version of this package. */ -#define PACKAGE_VERSION "0.10.31.1" +#define PACKAGE_VERSION "0.10.32.1" /* directory where plugins are located */ #ifdef _DEBUG @@ -368,7 +374,7 @@ #undef STDC_HEADERS /* Version number of package */ -#define VERSION "0.10.31.1" +#define VERSION "0.10.32.1" /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ diff --git a/win32/common/libgstapp.def b/win32/common/libgstapp.def index 9b1aa24313..892d680e4d 100644 --- a/win32/common/libgstapp.def +++ b/win32/common/libgstapp.def @@ -31,3 +31,4 @@ EXPORTS gst_app_src_set_max_bytes gst_app_src_set_size gst_app_src_set_stream_type + gst_app_stream_type_get_type diff --git a/win32/common/libgstpbutils.def b/win32/common/libgstpbutils.def index 5f96c969f9..083e294914 100644 --- a/win32/common/libgstpbutils.def +++ b/win32/common/libgstpbutils.def @@ -26,6 +26,7 @@ EXPORTS gst_discoverer_info_get_duration gst_discoverer_info_get_misc gst_discoverer_info_get_result + gst_discoverer_info_get_seekable gst_discoverer_info_get_stream_info gst_discoverer_info_get_stream_list gst_discoverer_info_get_streams @@ -57,6 +58,50 @@ EXPORTS gst_discoverer_video_info_get_width gst_discoverer_video_info_is_image gst_discoverer_video_info_is_interlaced + gst_encoding_audio_profile_get_type + gst_encoding_audio_profile_new + gst_encoding_container_profile_add_profile + gst_encoding_container_profile_contains_profile + gst_encoding_container_profile_get_profiles + gst_encoding_container_profile_get_type + gst_encoding_container_profile_new + gst_encoding_list_all_targets + gst_encoding_list_available_categories + gst_encoding_profile_find + gst_encoding_profile_get_description + gst_encoding_profile_get_format + gst_encoding_profile_get_input_caps + gst_encoding_profile_get_name + gst_encoding_profile_get_presence + gst_encoding_profile_get_preset + gst_encoding_profile_get_restriction + gst_encoding_profile_get_type + gst_encoding_profile_get_type_nick + gst_encoding_profile_is_equal + gst_encoding_profile_set_description + gst_encoding_profile_set_format + gst_encoding_profile_set_name + gst_encoding_profile_set_presence + gst_encoding_profile_set_preset + gst_encoding_profile_set_restriction + gst_encoding_target_add_profile + gst_encoding_target_get_category + gst_encoding_target_get_description + gst_encoding_target_get_name + gst_encoding_target_get_profile + gst_encoding_target_get_profiles + gst_encoding_target_get_type + gst_encoding_target_load + gst_encoding_target_load_from_file + gst_encoding_target_new + gst_encoding_target_save + gst_encoding_target_save_to_file + gst_encoding_video_profile_get_pass + gst_encoding_video_profile_get_type + gst_encoding_video_profile_get_variableframerate + gst_encoding_video_profile_new + gst_encoding_video_profile_set_pass + gst_encoding_video_profile_set_variableframerate gst_install_plugins_async gst_install_plugins_context_free gst_install_plugins_context_get_type diff --git a/win32/common/libgstrtp.def b/win32/common/libgstrtp.def index bfa8c98d49..41c9660c93 100644 --- a/win32/common/libgstrtp.def +++ b/win32/common/libgstrtp.def @@ -10,6 +10,7 @@ EXPORTS gst_base_rtp_audio_payload_set_samplebits_options gst_base_rtp_depayload_get_type gst_base_rtp_depayload_push + gst_base_rtp_depayload_push_list gst_base_rtp_depayload_push_ts gst_basertppayload_get_type gst_basertppayload_is_filled diff --git a/win32/common/libgstrtsp.def b/win32/common/libgstrtsp.def index 1b904cb697..5327c5b8cf 100644 --- a/win32/common/libgstrtsp.def +++ b/win32/common/libgstrtsp.def @@ -93,6 +93,7 @@ EXPORTS gst_rtsp_transport_new gst_rtsp_transport_parse gst_rtsp_url_copy + gst_rtsp_url_decode_path_components gst_rtsp_url_free gst_rtsp_url_get_port gst_rtsp_url_get_request_uri diff --git a/win32/common/libgstvideo.def b/win32/common/libgstvideo.def index a2340291d0..ef663d2f0e 100644 --- a/win32/common/libgstvideo.def +++ b/win32/common/libgstvideo.def @@ -7,6 +7,7 @@ EXPORTS gst_video_filter_get_type gst_video_format_convert gst_video_format_from_fourcc + gst_video_format_get_component_depth gst_video_format_get_component_height gst_video_format_get_component_offset gst_video_format_get_component_width diff --git a/win32/common/pbutils-enumtypes.c b/win32/common/pbutils-enumtypes.c index 8303b77bb8..2652ec066c 100644 --- a/win32/common/pbutils-enumtypes.c +++ b/win32/common/pbutils-enumtypes.c @@ -6,6 +6,8 @@ #include "pbutils.h" #include "codec-utils.h" #include "descriptions.h" +#include "encoding-profile.h" +#include "encoding-target.h" #include "install-plugins.h" #include "missing-plugins.h" #include "gstdiscoverer.h" diff --git a/win32/common/video-enumtypes.c b/win32/common/video-enumtypes.c index 66477c22bc..f1ba0e5773 100644 --- a/win32/common/video-enumtypes.c +++ b/win32/common/video-enumtypes.c @@ -48,6 +48,11 @@ gst_video_format_get_type (void) {GST_VIDEO_FORMAT_BGR15, "GST_VIDEO_FORMAT_BGR15", "bgr15"}, {GST_VIDEO_FORMAT_UYVP, "GST_VIDEO_FORMAT_UYVP", "uyvp"}, {GST_VIDEO_FORMAT_A420, "GST_VIDEO_FORMAT_A420", "a420"}, + {GST_VIDEO_FORMAT_RGB8_PALETTED, "GST_VIDEO_FORMAT_RGB8_PALETTED", + "rgb8-paletted"}, + {GST_VIDEO_FORMAT_YUV9, "GST_VIDEO_FORMAT_YUV9", "yuv9"}, + {GST_VIDEO_FORMAT_YVU9, "GST_VIDEO_FORMAT_YVU9", "yvu9"}, + {GST_VIDEO_FORMAT_IYU1, "GST_VIDEO_FORMAT_IYU1", "iyu1"}, {0, NULL, NULL} }; GType g_define_type_id = g_enum_register_static ("GstVideoFormat", values);