Merge branch 'master' into 0.11

Conflicts:
	configure.ac
	ext/alsa/gstalsasrc.c
	gst-libs/gst/audio/gstbaseaudiosink.c
	gst-libs/gst/tag/gstxmptag.c
	gst/playback/gstsubtitleoverlay.c
	gst/videorate/gstvideorate.c
	sys/xvimage/xvimagesink.c
This commit is contained in:
Sebastian Dröge 2011-05-16 17:06:22 +02:00
commit d0362c2b87
70 changed files with 7903 additions and 711 deletions

2240
ChangeLog

File diff suppressed because it is too large Load diff

163
NEWS
View file

@ -1,4 +1,165 @@
This is GStreamer Base Plug-ins 0.10.32, "Your Life You Like It Well" This is GStreamer Base Plug-ins 0.10.34, "Lemmings"
Changes since 0.10.33:
* None: this release is identical to 0.10.33 and just done to keep core/base
versions in sync
Changes since 0.10.32:
* audioringbuffer: make sure to not start if the may_start flag is FALSE
* baseaudiosink: arrange for running clock when rendering eos
* baseaudiosink: don't allow aligning behind the read-segment
* baseaudiosink: start ringbuffer upon going to PLAYING and already EOS
* riff: Add support for video/x-camstudio
* rtcpbuffer: fix invalid read in validation of padding in rtcp packet
* rtcpbuffer: Round to next 32bit word, not current 32bit word at end of SDES chunk
* rtpbuffer: Off-by-one error when creating RTP header extensions with a two-byte header
* rtsptransport: ensure valid int result when parsing ranges
* tag: map the ID3v2 TENC frame to GST_TAG_ENCODED_BY
* tag: add GST_TAG_CAPTURING_EXPOSURE_COMPENSATION incl. EXIF/XMP mappings
* tag: add a new GstTagXmpWriter interface to select XMP schemas to be used
* tagdemux: also push cached events downstream when operating in pull mode
* video: add GST_VIDEO_BUFFER_PROGRESSIVE flag
* video: add ARGB64 and AYUV64 (16 bits per channel) formats
* video: add r210 (10 bits per channel) format
* video: add gst_video_format_get_component_depth() and _new_template_caps()
* video: fix creation of grayscale caps and height calculation for YUV9/YVU9
* appsink: emit "new-buffer-list" signal for buffer lists if handled by app
* audiorate: add "skip-to-first" property
* decodebin2: don't use the same parser element multiple times in the same chain
* decodebin2: improve detection of raw caps in expose-all-streams=false mode
* discoverer: don't wait for subtitle streams to preroll; leak fixes
* discoverer: use nominal bitrate if bitrate tag is unavailable
* encodebin: add an audioconvert after the audio resampler
* encodebin: fix refcounting issues and leaks related to request pads
* encodebin: return a new reference of the pad for the "request-pad" signal
* encodebin: set all elements to NULL and remove them from the bin when removing a source group
* encodebin: tear down old profiles when setting new ones
* multifdsink: disconnect inactive clients in the select loop too
* oggmux: prefer headers from caps to determine stream type (for VP8)
* oggmux: fix issue with ogg page numbering and discont flag handling
* oggmux: ensure stream serial numbers are unique
* oggmux: use running time for muxing instead of timestamps
* oggparse: better detection of delta unit flag
* playbin2, uridecodebin: add "source-setup" signal
* playbin2: always prefer the custom set sink and also set it back to NULL in all cases
* playbin2: check if an already existing sink supports the non-raw format too
* playbin2: fix handling of non-raw custom sinks
* playbin2: if a sink claims to support ANY caps assume that it only supports the usual raw formats
* playbin2: only consider the audio/video sinks in autoplug_continue for the normal uridecodebin
* playbin2: use gst_pad_accept_caps() instead of intersecting with the getcaps caps
* playbin2: set sinks to READY before checking if it accept caps
* textoverlay: add support for ARGB and other RGB alpha variants, and xBGR and RGBx
* textoverlay: add support for vertical center alignment
* textoverlay: converted AYUV to use 'A OVER B' alpha compositing
* textoverlay: use a class wide mutex to work around pango reentrance issues
* theoraenc: don't reset the video quality when setting the bitrate
* theoraenc: allow adjustment of the speed level while running
* theoraenc: set speed-level property defaults from libtheora's defaults
* typefinding: MPEG-TS detection fixes
* typefinding: detect HTTP live streaming m3u8 playlists
* typefinding: detect windows icon files and DEGAS images (to avoid false positives)
* typefinding: detect raw h.263
* typefinding: add depth and endianness fields to DTS caps
* uridecodebin: Add default handler for autoplug-select
* uridecodebin: add https:// to protocols for which to enable buffering
* uridecodebin: expose "autoplug-sort" signal
* uridecodebin: post proper error message if decodebin2/typefind elements are missing
* uridecodebin: Return NULL from the default autoplug-sort handler
* videorate: fix "skip-to-first" timestamp setup
* videoscale: add 16-bit-channel support (ARGB64, AYUV64), fix ARGB bilinear scaling
* videotestsrc: add 16-bit-per-channel support (ARGB64, AYUV64)
* vorbis: add support for using tremolo on android
* vorbistag: Add support for METADATA_BLOCK_PICTURE tags
* vorbistag: Write GST_TAG_IMAGE and GST_TAG_PREVIEW_IMAGE as METADATA_BLOCK_PICTURE
* win32: fix DEFAULT_AUDIOSINK, should be direct*sound*sink
* xvimagesink: don't paint the window black when going to NULL
Bugs fixed since 0.10.32:
* 618516 : [typefinding] need raw H.263 typefinder
* 619778 : oggdemux: fails on zero-length pages with Patent_Absurdity_HD_3540kbit.ogv
* 633837 : videoscale: invalid reads after conversion to orc linear scaling
* 412678 : random segfaults or memory corruptions with multiple textoverlays (pango not reentrant)
* 620364 : [typefinding] .ico file detected as AAC
* 625129 : typefinding: file incorrectly detected as audio/x-dts
* 626152 : [playbin2] add " source-setup " signal
* 627268 : [tag] add GST_TAG_ENCODED_BY and map id3v2 TENC frame
* 629196 : oggmux: re-tagging an Ogg Vorbis file may corrupt audio data
* 632291 : discoverer: sparse tracks cause prerolling to hang till timeout
* 632889 : [multifdsink] [PATCH] Disconnect inactive clients in the select loop too
* 635669 : [vorbistag] Support METADATA_BLOCK_PICTURE for Vorbis cover art
* 635784 : ringbuffer: make sure to not start if the may_start flag is FALSE
* 635800 : xvimagesink flashes black when going from READY_TO_NULL
* 636886 : baseaudiosink: no running clock when eos leads to hang in PLAYING
* 639136 : [oggparse]code is not safe when using libogg fuctions
* 639159 : [textoverloay] Add vertical center alignment option
* 639237 : textoverlay: patch to use " A OVER B " alpha compositing
* 639744 : [oggdemux] Removing dead code:
* 640189 : oggmux: cleanup
* 640211 : oggmux: ensure serialnos are unique
* 640607 : appsink never sends " new-buffer-list " signal
* 640709 : [typefindfunctions] h264 typefinder registered with MPEG_VIDEO_CAPS
* 640804 : checks: encodebin test fails if theora or vorbis plugins are not available
* 641706 : discoverer: Keep references on discoverer objects for callbacks
* 641860 : discoverer: Use nominal bitrate if bitrate tag is unavailable
* 641917 : [gdppay] Ensure buffer's medata is writeable before setting it
* 641927 : [encodebin] refcount issue with the " request-pad " signal
* 641952 : [videoscale] assertion on fixate_caps
* 642174 : Playbin2 cannot work with non-raw custom sinks
* 642232 : theoraenc sets Video quality to zero when explicitely setting the bitrate to 0
* 642274 : [playbin2] arbitrary audio-sink is chosen even though explicitely having set a custom audio-sink bin
* 642381 : potential memleak in decodebin2
* 642466 : playbin2: after replacing a video sink with the pipeline in NULL state I still get the old one
* 642720 : audiotestsrc: pipelines with multiple instances with wave=gaussian-noise, white-noise, or pink-noise are very slow
* 642942 : adder: offset_end field of outgoing buffers is set to GST_BUFFER_OFFSET_NONE
* 642949 : pbutils: encoding-target: chaining error object in loading target from file may cause crash if there is no error
* 643775 : [oggmux] use running time instead of timestamps
* 644416 : [encodebin] Cannot be reused
* 644745 : [oggmux] Fails to mux Speex content, doesn't preroll
* 644845 : [alsa] Comparison of unsigned int < 0 always false in gstalsamixer.c
* 644996 : libsABI check doesn't depend only on architecture
* 645167 : [xmp] Add a new XmpConfig interface
* 645437 : encoding-profile: Fix syntax in Example: Creating a profile
* 646570 : baseaudiosink: don't allow aligning behind the read-segment
* 646572 : baseaudiosrc: protect against ringbuffer disappearing while in a query
* 646573 : baseaudiosrc: Add src object lock around call to ringbuffer parse caps
* 646575 : rtcpbuffer: Round to next 32bit word, not current 32bit word at end of SDES chunk
* 646576 : rtcpbuffer: fix invalid read in validation of padding in rtcp packet
* 646923 : video: Remove unused variable
* 646924 : rtp: Remove unused variables
* 646925 : encoding-profile: Remove unused variables
* 646952 : Fix the strlol return type mismatch :
* 647399 : Bad typo in ID3 tags: psychadelic - > psychedelic
* 647721 : Remove excessive checking for video.c
* 647781 : [playbin2] missing shutdown steps and inconsistent error behaviour
* 647856 : [oggmux] Assumes that the first buffer can be used to detect the stream type
* 647857 : [xvimagesink/ximagesink] Handle NULL caps in buffer_alloc()
* 647942 : [pango] Use different Pango contexts for the different subclasses
* 647943 : [pango] Class global pango mutex not always used
* 648459 : tag: exif: register common tags from tag library
* 648466 : Ogg to LPCM transcoding fails
* 648548 : videoscale broken with orc 0.4.13
* 642667 : [playbin2] autoplug-factories code does not do what it claims to do
* 642732 : [playbin2] sinks set to READY after activating groups causes bad autoplug-continue decisions
* 646744 : libgsttag: Minor issues building gst-plugins-base with MS compiler
* 647294 : gst-plugins-base doesn't compile with GCC 4.6
API additions since 0.10.32:
* gst_tag_list_to_xmp_buffer_full()
* gst_tag_xmp_list_schemas()
* gst_tag_xmp_writer_add_all_schemas()
* gst_tag_xmp_writer_add_schema()
* gst_tag_xmp_writer_get_type()
* gst_tag_xmp_writer_has_schema()
* gst_tag_xmp_writer_remove_all_schemas()
* gst_tag_xmp_writer_remove_schema()
* gst_tag_xmp_writer_tag_list_to_xmp_buffer()
* GST_TAG_CAPTURING_EXPOSURE_COMPENSATION
* gst_video_format_get_component_depth()
* gst_video_format_new_template_caps()
Changes since 0.10.31: Changes since 0.10.31:

159
RELEASE
View file

@ -1,5 +1,5 @@
Release notes for GStreamer Base Plug-ins 0.10.32 "Your Life You Like It Well" Release notes for GStreamer Base Plug-ins 0.10.34 "Lemmings"
@ -9,6 +9,8 @@ GStreamer Base Plug-ins.
The 0.10.x series is a stable series targeted at end users. 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.
@ -53,130 +55,9 @@ contains a set of less supported plug-ins that haven't passed the
Features of this release Features of this release
* GLib requirement is now >= 2.22, gobject-introspection >= 0.9.12 * This release is identical to 0.10.33 (to keep core/base versions in sync)
* New encodebin element There were no bugs fixed in this release
* 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
* 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_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 Download
@ -205,35 +86,5 @@ Applications
Contributors to this release Contributors to this release
* Alessandro Decina
* Andoni Morales Alastruey
* Andrea Sebastianutti
* Andy Wingo
* Arun Raghavan
* Bastien Nocera
* Benjamin Gaignard
* Byeong-ryeol Kim
* David Schleef
* Edward Hervey
* Evan Broder
* Gavin Stark
* Havard Graff
* Koop Mast
* Lane Brooks
* Leo Singer
* Mark Nauwelaerts
* Michael Smith
* René Stadler
* Rob Clark
* Robert Swain
* Sebastian Dröge
* Sreerenj Balachandran
* Stefan Kost
* Thiago Santos
* Tim-Philipp Müller * Tim-Philipp Müller
* Vincent Penquerc'h
* Wim Taymans
* Yang Xichuan
* Zeeshan Ali (Khattak)
* christian schaller
   

View file

@ -49,7 +49,7 @@ dnl - interfaces added/removed/changed -> increment CURRENT, REVISION = 0
dnl - interfaces added -> increment AGE dnl - interfaces added -> increment AGE
dnl - interfaces removed -> AGE = 0 dnl - interfaces removed -> AGE = 0
dnl sets GST_LT_LDFLAGS dnl sets GST_LT_LDFLAGS
AS_LIBTOOL(GST, 23, 0, 23) AS_LIBTOOL(GST, 24, 0, 24)
dnl FIXME: this macro doesn't actually work; dnl FIXME: this macro doesn't actually work;
dnl the generated libtool script has no support for the listed tags. dnl the generated libtool script has no support for the listed tags.

View file

@ -52,6 +52,7 @@
<xi:include href="xml/gstbaseaudiosrc.xml" /> <xi:include href="xml/gstbaseaudiosrc.xml" />
<xi:include href="xml/gstmultichannel.xml" /> <xi:include href="xml/gstmultichannel.xml" />
<xi:include href="xml/gstringbuffer.xml" /> <xi:include href="xml/gstringbuffer.xml" />
<xi:include href="xml/gstaudioiec61937.xml" />
</chapter> </chapter>
<chapter id="gstreamer-cdda"> <chapter id="gstreamer-cdda">

View file

@ -317,6 +317,13 @@ gst_ring_buffer_debug_spec_buff
gst_ring_buffer_debug_spec_caps gst_ring_buffer_debug_spec_caps
</SECTION> </SECTION>
<SECTION>
<FILE>gstaudioiec61937</FILE>
<INCLUDE>gst/audio/gstaudioiec61937.h</INCLUDE>
gst_audio_iec61937_frame_size
gst_audio_iec61937_payload
</SECTION>
# cdda # cdda

View file

@ -3,10 +3,10 @@
<description>Adds multiple streams</description> <description>Adds multiple streams</description>
<filename>../../gst/adder/.libs/libgstadder.so</filename> <filename>../../gst/adder/.libs/libgstadder.so</filename>
<basename>libgstadder.so</basename> <basename>libgstadder.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>ALSA plugin library</description> <description>ALSA plugin library</description>
<filename>../../ext/alsa/.libs/libgstalsa.so</filename> <filename>../../ext/alsa/.libs/libgstalsa.so</filename>
<basename>libgstalsa.so</basename> <basename>libgstalsa.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Elements used to communicate with applications</description> <description>Elements used to communicate with applications</description>
<filename>../../gst/app/.libs/libgstapp.so</filename> <filename>../../gst/app/.libs/libgstapp.so</filename>
<basename>libgstapp.so</basename> <basename>libgstapp.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Convert audio to different formats</description> <description>Convert audio to different formats</description>
<filename>../../gst/audioconvert/.libs/libgstaudioconvert.so</filename> <filename>../../gst/audioconvert/.libs/libgstaudioconvert.so</filename>
<basename>libgstaudioconvert.so</basename> <basename>libgstaudioconvert.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Adjusts audio frames</description> <description>Adjusts audio frames</description>
<filename>../../gst/audiorate/.libs/libgstaudiorate.so</filename> <filename>../../gst/audiorate/.libs/libgstaudiorate.so</filename>
<basename>libgstaudiorate.so</basename> <basename>libgstaudiorate.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Resamples audio</description> <description>Resamples audio</description>
<filename>../../gst/audioresample/.libs/libgstaudioresample.so</filename> <filename>../../gst/audioresample/.libs/libgstaudioresample.so</filename>
<basename>libgstaudioresample.so</basename> <basename>libgstaudioresample.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Creates audio test signals of given frequency and volume</description> <description>Creates audio test signals of given frequency and volume</description>
<filename>../../gst/audiotestsrc/.libs/libgstaudiotestsrc.so</filename> <filename>../../gst/audiotestsrc/.libs/libgstaudiotestsrc.so</filename>
<basename>libgstaudiotestsrc.so</basename> <basename>libgstaudiotestsrc.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Read audio from CD in paranoid mode</description> <description>Read audio from CD in paranoid mode</description>
<filename>../../ext/cdparanoia/.libs/libgstcdparanoia.so</filename> <filename>../../ext/cdparanoia/.libs/libgstcdparanoia.so</filename>
<basename>libgstcdparanoia.so</basename> <basename>libgstcdparanoia.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>decoder bin</description> <description>decoder bin</description>
<filename>../../gst/playback/.libs/libgstdecodebin.so</filename> <filename>../../gst/playback/.libs/libgstdecodebin.so</filename>
<basename>libgstdecodebin.so</basename> <basename>libgstdecodebin.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>various encoding-related elements</description> <description>various encoding-related elements</description>
<filename>../../gst/encoding/.libs/libgstencodebin.so</filename> <filename>../../gst/encoding/.libs/libgstencodebin.so</filename>
<basename>libgstencodebin.so</basename> <basename>libgstencodebin.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,7 +3,7 @@
<description>colorspace conversion copied from FFMpeg 0.4.9-pre1</description> <description>colorspace conversion copied from FFMpeg 0.4.9-pre1</description>
<filename>../../gst/ffmpegcolorspace/.libs/libgstffmpegcolorspace.so</filename> <filename>../../gst/ffmpegcolorspace/.libs/libgstffmpegcolorspace.so</filename>
<basename>libgstffmpegcolorspace.so</basename> <basename>libgstffmpegcolorspace.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>FFMpeg</package> <package>FFMpeg</package>

View file

@ -3,10 +3,10 @@
<description>Payload/depayload GDP packets</description> <description>Payload/depayload GDP packets</description>
<filename>../../gst/gdp/.libs/libgstgdp.so</filename> <filename>../../gst/gdp/.libs/libgstgdp.so</filename>
<basename>libgstgdp.so</basename> <basename>libgstgdp.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>GIO elements</description> <description>GIO elements</description>
<filename>../../ext/gio/.libs/libgstgio.so</filename> <filename>../../ext/gio/.libs/libgstgio.so</filename>
<basename>libgstgio.so</basename> <basename>libgstgio.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>elements to read from and write to Gnome-VFS uri&apos;s</description> <description>elements to read from and write to Gnome-VFS uri&apos;s</description>
<filename>../../ext/gnomevfs/.libs/libgstgnomevfs.so</filename> <filename>../../ext/gnomevfs/.libs/libgstgnomevfs.so</filename>
<basename>libgstgnomevfs.so</basename> <basename>libgstgnomevfs.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>libvisual visualization plugins</description> <description>libvisual visualization plugins</description>
<filename>../../ext/libvisual/.libs/libgstlibvisual.so</filename> <filename>../../ext/libvisual/.libs/libgstlibvisual.so</filename>
<basename>libgstlibvisual.so</basename> <basename>libgstlibvisual.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>ogg stream manipulation (info about ogg: http://xiph.org)</description> <description>ogg stream manipulation (info about ogg: http://xiph.org)</description>
<filename>../../ext/ogg/.libs/libgstogg.so</filename> <filename>../../ext/ogg/.libs/libgstogg.so</filename>
<basename>libgstogg.so</basename> <basename>libgstogg.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Pango-based text rendering and overlay</description> <description>Pango-based text rendering and overlay</description>
<filename>../../ext/pango/.libs/libgstpango.so</filename> <filename>../../ext/pango/.libs/libgstpango.so</filename>
<basename>libgstpango.so</basename> <basename>libgstpango.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>various playback elements</description> <description>various playback elements</description>
<filename>../../gst/playback/.libs/libgstplaybin.so</filename> <filename>../../gst/playback/.libs/libgstplaybin.so</filename>
<basename>libgstplaybin.so</basename> <basename>libgstplaybin.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Subtitle parsing</description> <description>Subtitle parsing</description>
<filename>../../gst/subparse/.libs/libgstsubparse.so</filename> <filename>../../gst/subparse/.libs/libgstsubparse.so</filename>
<basename>libgstsubparse.so</basename> <basename>libgstsubparse.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>transfer data over the network via TCP</description> <description>transfer data over the network via TCP</description>
<filename>../../gst/tcp/.libs/libgsttcp.so</filename> <filename>../../gst/tcp/.libs/libgsttcp.so</filename>
<basename>libgsttcp.so</basename> <basename>libgsttcp.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Theora plugin library</description> <description>Theora plugin library</description>
<filename>../../ext/theora/.libs/libgsttheora.so</filename> <filename>../../ext/theora/.libs/libgsttheora.so</filename>
<basename>libgsttheora.so</basename> <basename>libgsttheora.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>default typefind functions</description> <description>default typefind functions</description>
<filename>../../gst/typefind/.libs/libgsttypefindfunctions.so</filename> <filename>../../gst/typefind/.libs/libgsttypefindfunctions.so</filename>
<basename>libgsttypefindfunctions.so</basename> <basename>libgsttypefindfunctions.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
</elements> </elements>

View file

@ -3,10 +3,10 @@
<description>URI Decoder bin</description> <description>URI Decoder bin</description>
<filename>../../gst/playback/.libs/libgstdecodebin2.so</filename> <filename>../../gst/playback/.libs/libgstdecodebin2.so</filename>
<basename>libgstdecodebin2.so</basename> <basename>libgstdecodebin2.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Adjusts video frames</description> <description>Adjusts video frames</description>
<filename>../../gst/videorate/.libs/libgstvideorate.so</filename> <filename>../../gst/videorate/.libs/libgstvideorate.so</filename>
<basename>libgstvideorate.so</basename> <basename>libgstvideorate.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Resizes video</description> <description>Resizes video</description>
<filename>../../gst/videoscale/.libs/libgstvideoscale.so</filename> <filename>../../gst/videoscale/.libs/libgstvideoscale.so</filename>
<basename>libgstvideoscale.so</basename> <basename>libgstvideoscale.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Creates a test video stream</description> <description>Creates a test video stream</description>
<filename>../../gst/videotestsrc/.libs/libgstvideotestsrc.so</filename> <filename>../../gst/videotestsrc/.libs/libgstvideotestsrc.so</filename>
<basename>libgstvideotestsrc.so</basename> <basename>libgstvideotestsrc.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>plugin for controlling audio volume</description> <description>plugin for controlling audio volume</description>
<filename>../../gst/volume/.libs/libgstvolume.so</filename> <filename>../../gst/volume/.libs/libgstvolume.so</filename>
<basename>libgstvolume.so</basename> <basename>libgstvolume.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>Vorbis plugin library</description> <description>Vorbis plugin library</description>
<filename>../../ext/vorbis/.libs/libgstvorbis.so</filename> <filename>../../ext/vorbis/.libs/libgstvorbis.so</filename>
<basename>libgstvorbis.so</basename> <basename>libgstvorbis.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>X11 video output element based on standard Xlib calls</description> <description>X11 video output element based on standard Xlib calls</description>
<filename>../../sys/ximage/.libs/libgstximagesink.so</filename> <filename>../../sys/ximage/.libs/libgstximagesink.so</filename>
<basename>libgstximagesink.so</basename> <basename>libgstximagesink.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -3,10 +3,10 @@
<description>XFree86 video output plugin using Xv extension</description> <description>XFree86 video output plugin using Xv extension</description>
<filename>../../sys/xvimage/.libs/libgstxvimagesink.so</filename> <filename>../../sys/xvimage/.libs/libgstxvimagesink.so</filename>
<basename>libgstxvimagesink.so</basename> <basename>libgstxvimagesink.so</basename>
<version>0.10.32.4</version> <version>0.10.34.1</version>
<license>LGPL</license> <license>LGPL</license>
<source>gst-plugins-base</source> <source>gst-plugins-base</source>
<package>GStreamer Base Plug-ins prerelease</package> <package>GStreamer Base Plug-ins git</package>
<origin>Unknown package origin</origin> <origin>Unknown package origin</origin>
<elements> <elements>
<element> <element>

View file

@ -87,6 +87,12 @@ static gboolean gst_alsasrc_close (GstAudioSrc * asrc);
static guint gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length); static guint gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length);
static guint gst_alsasrc_delay (GstAudioSrc * asrc); static guint gst_alsasrc_delay (GstAudioSrc * asrc);
static void gst_alsasrc_reset (GstAudioSrc * asrc); static void gst_alsasrc_reset (GstAudioSrc * asrc);
static GstStateChangeReturn gst_alsasrc_change_state (GstElement * element,
GstStateChange transition);
static GstFlowReturn gst_alsasrc_create (GstBaseSrc * bsrc, guint64 offset,
guint length, GstBuffer ** outbuf);
static GstClockTime gst_alsasrc_get_timestamp (GstAlsaSrc * src);
/* AlsaSrc signals and args */ /* AlsaSrc signals and args */
enum enum
@ -189,11 +195,13 @@ gst_alsasrc_class_init (GstAlsaSrcClass * klass)
GstElementClass *gstelement_class; GstElementClass *gstelement_class;
GstBaseSrcClass *gstbasesrc_class; GstBaseSrcClass *gstbasesrc_class;
GstAudioSrcClass *gstaudiosrc_class; GstAudioSrcClass *gstaudiosrc_class;
GstBaseAudioSrcClass *gstbaseaudiosrc_class;
gobject_class = (GObjectClass *) klass; gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gstbasesrc_class = (GstBaseSrcClass *) klass; gstbasesrc_class = (GstBaseSrcClass *) klass;
gstaudiosrc_class = (GstAudioSrcClass *) klass; gstaudiosrc_class = (GstAudioSrcClass *) klass;
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
gobject_class->finalize = gst_alsasrc_finalize; gobject_class->finalize = gst_alsasrc_finalize;
gobject_class->get_property = gst_alsasrc_get_property; gobject_class->get_property = gst_alsasrc_get_property;
@ -206,7 +214,10 @@ gst_alsasrc_class_init (GstAlsaSrcClass * klass)
gst_element_class_add_pad_template (gstelement_class, gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&alsasrc_src_factory)); gst_static_pad_template_get (&alsasrc_src_factory));
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_alsasrc_change_state);
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasrc_getcaps); gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasrc_getcaps);
gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_alsasrc_create);
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open); gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open);
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasrc_prepare); gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasrc_prepare);
@ -232,6 +243,49 @@ gst_alsasrc_class_init (GstAlsaSrcClass * klass)
DEFAULT_PROP_CARD_NAME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); DEFAULT_PROP_CARD_NAME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
} }
static GstClockTime
gst_alsasrc_get_timestamp (GstAlsaSrc * src)
{
snd_pcm_status_t *status;
snd_htimestamp_t tstamp;
GstClockTime timestamp;
snd_pcm_uframes_t availmax;
GST_DEBUG_OBJECT (src, "Getting alsa timestamp!");
if (!src) {
GST_ERROR_OBJECT (src, "No alsa handle created yet !");
return 0;
}
if (snd_pcm_status_malloc (&status) != 0) {
GST_ERROR_OBJECT (src, "snd_pcm_status_malloc failed");
}
if (snd_pcm_status (src->handle, status) != 0) {
GST_ERROR_OBJECT (src, "snd_pcm_status failed");
}
/* get high resolution time stamp from driver */
snd_pcm_status_get_htstamp (status, &tstamp);
timestamp = GST_TIMESPEC_TO_TIME (tstamp);
/* Max available frames sets the depth of the buffer */
availmax = snd_pcm_status_get_avail_max (status);
/* Compensate the fact that the timestamp references the last sample */
timestamp -= gst_util_uint64_scale_int (availmax * 2, GST_SECOND, src->rate);
/* Compensate for the delay until the package is available */
timestamp += gst_util_uint64_scale_int (snd_pcm_status_get_delay (status),
GST_SECOND, src->rate);
snd_pcm_status_free (status);
GST_DEBUG_OBJECT (src, "ALSA timestamp : %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
return timestamp;
}
static void static void
gst_alsasrc_set_property (GObject * object, guint prop_id, gst_alsasrc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
@ -282,6 +336,57 @@ gst_alsasrc_get_property (GObject * object, guint prop_id,
} }
} }
static GstStateChangeReturn
gst_alsasrc_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (element);
GstAlsaSrc *asrc = GST_ALSA_SRC (element);
GstClock *clk;
switch (transition) {
/* Show the compiler that we care */
case GST_STATE_CHANGE_NULL_TO_READY:
case GST_STATE_CHANGE_READY_TO_PAUSED:
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
case GST_STATE_CHANGE_PAUSED_TO_READY:
case GST_STATE_CHANGE_READY_TO_NULL:
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
clk = src->clock;
asrc->driver_timestamps = FALSE;
if (GST_IS_SYSTEM_CLOCK (clk)) {
gint clocktype;
g_object_get (clk, "clock-type", &clocktype, NULL);
if (clocktype == GST_CLOCK_TYPE_MONOTONIC) {
asrc->driver_timestamps = TRUE;
}
}
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
return ret;
}
static GstFlowReturn
gst_alsasrc_create (GstBaseSrc * bsrc, guint64 offset, guint length,
GstBuffer ** outbuf)
{
GstFlowReturn ret = GST_FLOW_OK;
GstAlsaSrc *asrc = GST_ALSA_SRC (bsrc);
ret =
GST_BASE_SRC_CLASS (parent_class)->create (bsrc, offset, length, outbuf);
if (asrc->driver_timestamps == TRUE && *outbuf) {
GST_BUFFER_TIMESTAMP (*outbuf) =
gst_alsasrc_get_timestamp ((GstAlsaSrc *) bsrc);
}
return ret;
}
static void static void
gst_alsasrc_init (GstAlsaSrc * alsasrc) gst_alsasrc_init (GstAlsaSrc * alsasrc)
{ {
@ -289,6 +394,7 @@ gst_alsasrc_init (GstAlsaSrc * alsasrc)
alsasrc->device = g_strdup (DEFAULT_PROP_DEVICE); alsasrc->device = g_strdup (DEFAULT_PROP_DEVICE);
alsasrc->cached_caps = NULL; alsasrc->cached_caps = NULL;
alsasrc->driver_timestamps = FALSE;
alsasrc->alsa_lock = g_mutex_new (); alsasrc->alsa_lock = g_mutex_new ();
} }

View file

@ -64,6 +64,7 @@ struct _GstAlsaSrc {
guint rate; guint rate;
guint channels; guint channels;
gint bytes_per_sample; gint bytes_per_sample;
gboolean driver_timestamps;
guint buffer_time; guint buffer_time;
guint period_time; guint period_time;

View file

@ -26,7 +26,8 @@ libgstaudio_@GST_MAJORMINOR@_la_SOURCES = \
gstbaseaudiosrc.c \ gstbaseaudiosrc.c \
gstaudiofilter.c \ gstaudiofilter.c \
gstaudiosink.c \ gstaudiosink.c \
gstaudiosrc.c gstaudiosrc.c \
gstaudioiec61937.c
nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers) nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers)
libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
@ -40,7 +41,8 @@ libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
gstaudiosink.h \ gstaudiosink.h \
gstaudiosrc.h \ gstaudiosrc.h \
mixerutils.h \ mixerutils.h \
multichannel.h multichannel.h \
gstaudioiec61937.h
nodist_libgstaudio_@GST_MAJORMINOR@include_HEADERS = \ nodist_libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
audio-enumtypes.h audio-enumtypes.h

View file

@ -0,0 +1,319 @@
/* GStreamer audio helper functions for IEC 61937 payloading
* (c) 2011 Intel Corporation
* 2011 Collabora Multimedia
* 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:gstaudioiec61937
* @short_description: Utility functions for IEC 61937 payloading
*
* This module contains some helper functions for encapsulating various
* audio formats in IEC 61937 headers and padding.
*
* Since: 0.10.35
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstaudioiec61937.h"
#define IEC61937_HEADER_SIZE 8
#define IEC61937_PAYLOAD_SIZE_AC3 (1536 * 4)
#define IEC61937_PAYLOAD_SIZE_EAC3 (6144 * 4)
static gint
caps_get_int_field (const GstCaps * caps, const gchar * field)
{
const GstStructure *st;
gint ret = 0;
st = gst_caps_get_structure (caps, 0);
gst_structure_get_int (st, field, &ret);
return ret;
}
static const gchar *
caps_get_string_field (const GstCaps * caps, const gchar * field)
{
const GstStructure *st = gst_caps_get_structure (caps, 0);
return gst_structure_get_string (st, field);
}
/**
* gst_audio_iec61937_frame_size
* @type: the type of data to be payloaded as a #GstBufferFormatType
*
* Returns 0 if the given @type is not supported or cannot be payloaded, else
* returns the size of the buffer expected by gst_audio_iec61937_payload() for
* payloading @type.
*
* Since: 0.10.35
*/
guint
gst_audio_iec61937_frame_size (const GstRingBufferSpec * spec)
{
switch (spec->type) {
case GST_BUFTYPE_AC3:
return IEC61937_PAYLOAD_SIZE_AC3;
case GST_BUFTYPE_EAC3:
/* Check that the parser supports /some/ alignment. Need to be less
* strict about this at checking time since the alignment is dynamically
* set at the moment. */
if (caps_get_string_field (spec->caps, "alignment"))
return IEC61937_PAYLOAD_SIZE_EAC3;
else
return 0;
case GST_BUFTYPE_DTS:
{
gint dts_frame_size = caps_get_int_field (spec->caps, "frame-size");
gint iec_frame_size = caps_get_int_field (spec->caps, "block-size") * 4;
/* Note: this will also (correctly) fail if either field is missing */
if (iec_frame_size >= (dts_frame_size + IEC61937_HEADER_SIZE))
return iec_frame_size;
else
return 0;
}
case GST_BUFTYPE_MPEG:
{
int version, layer, channels, frames;
version = caps_get_int_field (spec->caps, "mpegaudioversion");
layer = caps_get_int_field (spec->caps, "layer");
channels = caps_get_int_field (spec->caps, "channels");
/* Bail out if we can't figure out either, if it's MPEG 2.5, or if it's
* MP3 with multichannel audio */
if (!version || !layer || version == 3 || channels > 2)
return 0;
if (version == 1 && layer == 1)
frames = 384;
else if (version == 2 && layer == 1 && spec->rate < 32000)
frames = 768;
else if (version == 2 && layer == 1 && spec->rate < 32000)
frames = 2304;
else
frames = 1152;
return frames * 4;
}
default:
return 0;
}
}
/**
* gst_audio_iec61937_payload
* @src: a buffer containing the data to payload
* @src_n: size of @src in bytes
* @dst: the destination buffer to store the payloaded contents in. Should not
* overlap with @src
* @dst_n: size of @dst in bytes
* @type: the type of data in @src
*
* Payloads @src in the form specified by IEC 61937 for @type and stores
* the result in @dst. @src must contain exactly one frame of data and the
* frame is not checked for errors.
*
* Returns: transfer-full: #TRUE if the payloading was successful, #FALSE
* otherwise.
*
* Since: 0.10.35
*/
gboolean
gst_audio_iec61937_payload (const guint8 * src, guint src_n, guint8 * dst,
guint dst_n, const GstRingBufferSpec * spec)
{
guint i, tmp;
#if G_BYTE_ORDER == G_BIG_ENDIAN
guint8 zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5, six = 6,
seven = 7;
#else
/* We need to send the data byte-swapped */
guint8 zero = 1, one = 0, two = 3, three = 2, four = 5, five = 4, six = 7,
seven = 6;
#endif
g_return_val_if_fail (src != NULL, FALSE);
g_return_val_if_fail (dst != NULL, FALSE);
g_return_val_if_fail (src != dst, FALSE);
g_return_val_if_fail (dst_n >= gst_audio_iec61937_frame_size (spec), FALSE);
if (dst_n < src_n + IEC61937_HEADER_SIZE)
return FALSE;
/* Pa, Pb */
dst[zero] = 0xF8;
dst[one] = 0x72;
dst[two] = 0x4E;
dst[three] = 0x1F;
switch (spec->type) {
case GST_BUFTYPE_AC3:
{
g_return_val_if_fail (src_n >= 6, FALSE);
/* Pc: bit 13-15 - stream number (0)
* bit 11-12 - reserved (0)
* bit 8-10 - bsmod from AC3 frame */
dst[four] = src[5] & 0x7;
/* Pc: bit 7 - error bit (0)
* bit 5-6 - subdata type (0)
* bit 0-4 - data type (1) */
dst[five] = 1;
/* Pd: bit 15-0 - frame size in bits */
tmp = src_n * 8;
dst[six] = (guint8) (tmp >> 8);
dst[seven] = (guint8) (tmp & 0xff);
break;
}
case GST_BUFTYPE_EAC3:
{
if (g_str_equal (caps_get_string_field (spec->caps, "alignment"),
"iec61937"))
return FALSE;
/* Pc: bit 13-15 - stream number (0)
* bit 11-12 - reserved (0)
* bit 8-10 - bsmod from E-AC3 frame if present */
/* FIXME: this works, but nicer if we can put in the actual bsmod */
dst[four] = 0;
/* Pc: bit 7 - error bit (0)
* bit 5-6 - subdata type (0)
* bit 0-4 - data type (21) */
dst[five] = 21;
/* Pd: bit 15-0 - frame size in bytes */
dst[six] = ((guint16) src_n) >> 8;
dst[seven] = ((guint16) src_n) & 0xff;
break;
}
case GST_BUFTYPE_DTS:
{
int blocksize = caps_get_int_field (spec->caps, "block-size");
g_return_val_if_fail (src_n != 0, FALSE);
if (blocksize == 0)
return FALSE;
/* Pc: bit 13-15 - stream number (0)
* bit 11-12 - reserved (0)
* bit 8-10 - for DTS type I-III (0) */
dst[four] = 0;
/* Pc: bit 7 - error bit (0)
* bit 5-6 - reserved (0)
* bit 0-4 - data type (11 = type I, 12 = type II,
* 13 = type III) */
dst[five] = 11 + (blocksize / 1024);
/* Pd: bit 15-0 - frame size in bytes */
dst[six] = ((guint16) src_n) >> 8;
dst[seven] = ((guint16) src_n) & 0xff;
break;
}
case GST_BUFTYPE_MPEG:
{
int version, layer;
version = caps_get_int_field (spec->caps, "mpegaudioversion");
layer = caps_get_int_field (spec->caps, "layer");
g_return_val_if_fail (version > 0 && layer > 0, FALSE);
/* NOTE: multichannel audio (MPEG-2) is not supported */
/* Pc: bit 13-15 - stream number (0)
* bit 11-12 - reserved (0)
* bit 9-10 - 0 - no dynamic range control
* - 2 - dynamic range control exists
* - 1,3 - reserved
* bit 8 - Normal (0) or Karaoke (1) mode */
dst[four] = 0;
/* Pc: bit 7 - error bit (0)
* bit 5-6 - reserved (0)
* bit 0-4 - data type (04 = MPEG 1, Layer 1
* 05 = MPEG 1, Layer 2, 3 / MPEG 2, w/o ext.
* 06 = MPEG 2, with extension
* 08 - MPEG 2 LSF, Layer 1
* 09 - MPEG 2 LSF, Layer 2
* 10 - MPEG 2 LSF, Layer 3 */
if (version == 1 && layer == 1)
dst[five] = 0x04;
else if ((version == 1 && (layer == 2 || layer == 3)) ||
(version == 2 && spec->rate >= 32000))
dst[five] = 0x05;
else if (version == 2 && layer == 1 && spec->rate < 32000)
dst[five] = 0x08;
else if (version == 2 && layer == 2 && spec->rate < 32000)
dst[five] = 0x09;
else if (version == 2 && layer == 3 && spec->rate < 32000)
dst[five] = 0x0A;
else
g_return_val_if_reached (FALSE);
/* Pd: bit 15-0 - frame size in bits */
dst[six] = ((guint16) src_n * 8) >> 8;
dst[seven] = ((guint16) src_n * 8) & 0xff;
break;
}
default:
return FALSE;
}
/* Copy the payload */
i = 8;
#if G_BYTE_ORDER == G_BIG_ENDIAN
memcpy (dst + i, src, src_n);
#else
/* Byte-swapped again */
/* FIXME: orc-ify this */
for (tmp = 1; tmp < src_n; tmp += 2) {
dst[i + tmp - 1] = src[tmp];
dst[i + tmp] = src[tmp - 1];
}
/* Do we have 1 byte remaining? */
if (src_n % 2) {
dst[i + src_n - 1] = 0;
dst[i + src_n] = src[src_n - 1];
i++;
}
#endif
i += src_n;
/* Zero the rest */
memset (dst + i, 0, dst_n - i);
return TRUE;
}

View file

@ -0,0 +1,38 @@
/* GStreamer audio helper functions for IEC 61937 payloading
* (c) 2011 Intel Corporation
* 2011 Collabora Multimedia
* 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:gstaudioiec61937
* @short_description: Utility functions for IEC 61937 payloading
*
* This module contains some helper functions for encapsulating various
* audio formats in IEC 61937 headers and padding.
*/
#ifndef __GST_AUDIO_IEC61937_H__
#define __GST_AUDIO_IEC61937_H__
#include <gst/audio/gstringbuffer.h>
guint gst_audio_iec61937_frame_size (const GstRingBufferSpec * spec);
gboolean gst_audio_iec61937_payload (const guint8 * src, guint src_n,
guint8 * dst, guint dst_n, const GstRingBufferSpec * spec);
#endif /* __GST_AUDIO_IEC61937_H__ */

View file

@ -1345,6 +1345,7 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
guint64 in_offset; guint64 in_offset;
GstClockTime time, stop, render_start, render_stop, sample_offset; GstClockTime time, stop, render_start, render_stop, sample_offset;
GstClockTimeDiff sync_offset, ts_offset; GstClockTimeDiff sync_offset, ts_offset;
GstBaseAudioSinkClass *bclass;
GstBaseAudioSink *sink; GstBaseAudioSink *sink;
GstRingBuffer *ringbuf; GstRingBuffer *ringbuf;
gint64 diff, align; gint64 diff, align;
@ -1362,8 +1363,10 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
GstFlowReturn ret; GstFlowReturn ret;
GstSegment clip_seg; GstSegment clip_seg;
gint64 time_offset; gint64 time_offset;
GstBuffer *out = NULL;
sink = GST_BASE_AUDIO_SINK (bsink); sink = GST_BASE_AUDIO_SINK (bsink);
bclass = GST_BASE_AUDIO_SINK_GET_CLASS (sink);
ringbuf = sink->ringbuffer; ringbuf = sink->ringbuffer;
@ -1387,6 +1390,17 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
GST_OBJECT_UNLOCK (sink); GST_OBJECT_UNLOCK (sink);
} }
/* Before we go on, let's see if we need to payload the data. If yes, we also
* need to unref the output buffer before leaving. */
if (bclass->payload) {
out = bclass->payload (sink, buf);
if (!out)
goto payload_failed;
buf = out;
}
bps = ringbuf->spec.bytes_per_sample; bps = ringbuf->spec.bytes_per_sample;
size = gst_buffer_get_size (buf); size = gst_buffer_get_size (buf);
@ -1669,7 +1683,13 @@ no_sync:
gst_ring_buffer_start (ringbuf); gst_ring_buffer_start (ringbuf);
} }
return GST_FLOW_OK; ret = GST_FLOW_OK;
done:
if (out)
gst_buffer_unref (out);
return ret;
/* SPECIAL cases */ /* SPECIAL cases */
out_of_segment: out_of_segment:
@ -1678,33 +1698,42 @@ out_of_segment:
"dropping sample out of segment time %" GST_TIME_FORMAT ", start %" "dropping sample out of segment time %" GST_TIME_FORMAT ", start %"
GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_FORMAT, GST_TIME_ARGS (time),
GST_TIME_ARGS (bsink->segment.start)); GST_TIME_ARGS (bsink->segment.start));
return GST_FLOW_OK; ret = GST_FLOW_OK;
goto done;
} }
/* ERRORS */ /* ERRORS */
payload_failed:
{
GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("failed to payload."));
ret = GST_FLOW_ERROR;
goto done;
}
wrong_state: wrong_state:
{ {
GST_DEBUG_OBJECT (sink, "ringbuffer not negotiated"); GST_DEBUG_OBJECT (sink, "ringbuffer not negotiated");
GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("sink not negotiated.")); GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("sink not negotiated."));
return GST_FLOW_NOT_NEGOTIATED; ret = GST_FLOW_NOT_NEGOTIATED;
goto done;
} }
wrong_size: wrong_size:
{ {
GST_DEBUG_OBJECT (sink, "wrong size"); GST_DEBUG_OBJECT (sink, "wrong size");
GST_ELEMENT_ERROR (sink, STREAM, WRONG_TYPE, GST_ELEMENT_ERROR (sink, STREAM, WRONG_TYPE,
(NULL), ("sink received buffer of wrong size.")); (NULL), ("sink received buffer of wrong size."));
return GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
goto done;
} }
stopping: stopping:
{ {
GST_DEBUG_OBJECT (sink, "preroll got interrupted: %d (%s)", ret, GST_DEBUG_OBJECT (sink, "preroll got interrupted: %d (%s)", ret,
gst_flow_get_name (ret)); gst_flow_get_name (ret));
gst_buffer_unmap (buf, data, size); gst_buffer_unmap (buf, data, size);
return ret; goto done;
} }
sync_latency_failed: sync_latency_failed:
{ {
GST_DEBUG_OBJECT (sink, "failed waiting for latency"); GST_DEBUG_OBJECT (sink, "failed waiting for latency");
return ret; goto done;
} }
} }

View file

@ -141,6 +141,10 @@ struct _GstBaseAudioSink {
* GstBaseAudioSinkClass: * GstBaseAudioSinkClass:
* @parent_class: the parent class. * @parent_class: the parent class.
* @create_ringbuffer: create and return a #GstRingBuffer to write to. * @create_ringbuffer: create and return a #GstRingBuffer to write to.
* @payload: payload data in a format suitable to write to the sink. If no
* payloading is required, returns a reffed copy of the original
* buffer, else returns the payloaded buffer with all other metadata
* copied. (Since: 0.10.35)
* *
* #GstBaseAudioSink class. Override the vmethod to implement * #GstBaseAudioSink class. Override the vmethod to implement
* functionality. * functionality.
@ -151,8 +155,12 @@ struct _GstBaseAudioSinkClass {
/* subclass ringbuffer allocation */ /* subclass ringbuffer allocation */
GstRingBuffer* (*create_ringbuffer) (GstBaseAudioSink *sink); GstRingBuffer* (*create_ringbuffer) (GstBaseAudioSink *sink);
/* subclass payloader */
GstBuffer* (*payload) (GstBaseAudioSink *sink,
GstBuffer *buffer);
/*< private >*/ /*< private >*/
gpointer _gst_reserved[GST_PADDING]; gpointer _gst_reserved[GST_PADDING - 1];
}; };
GType gst_base_audio_sink_get_type(void); GType gst_base_audio_sink_get_type(void);

View file

@ -327,7 +327,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
/* we have to differentiate between int and float formats */ /* we have to differentiate between int and float formats */
mimetype = gst_structure_get_name (structure); mimetype = gst_structure_get_name (structure);
if (!strncmp (mimetype, "audio/x-raw-int", 15)) { if (g_str_equal (mimetype, "audio/x-raw-int")) {
gint endianness; gint endianness;
const FormatDef *def; const FormatDef *def;
gint j, bytes; gint j, bytes;
@ -367,7 +367,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->silence_sample[i * bytes + j] = def->silence[j]; spec->silence_sample[i * bytes + j] = def->silence[j];
} }
} }
} else if (!strncmp (mimetype, "audio/x-raw-float", 17)) { } else if (g_str_equal (mimetype, "audio/x-raw-float")) {
spec->type = GST_BUFTYPE_FLOAT; spec->type = GST_BUFTYPE_FLOAT;
@ -392,7 +392,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
} }
/* float silence is all zeros.. */ /* float silence is all zeros.. */
memset (spec->silence_sample, 0, 32); memset (spec->silence_sample, 0, 32);
} else if (!strncmp (mimetype, "audio/x-alaw", 12)) { } else if (g_str_equal (mimetype, "audio/x-alaw")) {
/* extract the needed information from the cap */ /* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate) && if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels))) gst_structure_get_int (structure, "channels", &spec->channels)))
@ -404,7 +404,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->depth = 8; spec->depth = 8;
for (i = 0; i < spec->channels; i++) for (i = 0; i < spec->channels; i++)
spec->silence_sample[i] = 0xd5; spec->silence_sample[i] = 0xd5;
} else if (!strncmp (mimetype, "audio/x-mulaw", 13)) { } else if (g_str_equal (mimetype, "audio/x-mulaw")) {
/* extract the needed information from the cap */ /* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate) && if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels))) gst_structure_get_int (structure, "channels", &spec->channels)))
@ -416,7 +416,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->depth = 8; spec->depth = 8;
for (i = 0; i < spec->channels; i++) for (i = 0; i < spec->channels; i++)
spec->silence_sample[i] = 0xff; spec->silence_sample[i] = 0xff;
} else if (!strncmp (mimetype, "audio/x-iec958", 14)) { } else if (g_str_equal (mimetype, "audio/x-iec958")) {
/* extract the needed information from the cap */ /* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate))) if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error; goto parse_error;
@ -426,7 +426,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->width = 16; spec->width = 16;
spec->depth = 16; spec->depth = 16;
spec->channels = 2; spec->channels = 2;
} else if (!strncmp (mimetype, "audio/x-ac3", 11)) { } else if (g_str_equal (mimetype, "audio/x-ac3")) {
/* extract the needed information from the cap */ /* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate))) if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error; goto parse_error;
@ -436,6 +436,39 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->width = 16; spec->width = 16;
spec->depth = 16; spec->depth = 16;
spec->channels = 2; spec->channels = 2;
} else if (g_str_equal (mimetype, "audio/x-eac3")) {
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error;
spec->type = GST_BUFTYPE_EAC3;
spec->format = GST_EAC3;
spec->width = 64;
spec->depth = 64;
spec->channels = 2;
} else if (g_str_equal (mimetype, "audio/x-dts")) {
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error;
spec->type = GST_BUFTYPE_DTS;
spec->format = GST_DTS;
spec->width = 16;
spec->depth = 16;
spec->channels = 2;
} else if (g_str_equal (mimetype, "audio/mpeg") &&
gst_structure_get_int (structure, "mpegaudioversion", &i) &&
(i == 1 || i == 2)) {
/* Now we know this is MPEG-1 or MPEG-2 (non AAC) */
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error;
spec->type = GST_BUFTYPE_MPEG;
spec->format = GST_MPEG;
spec->width = 16;
spec->depth = 16;
spec->channels = 2;
} else { } else {
goto parse_error; goto parse_error;
} }

View file

@ -88,12 +88,14 @@ typedef enum {
* @GST_BUFTYPE_MU_LAW: samples in mulaw * @GST_BUFTYPE_MU_LAW: samples in mulaw
* @GST_BUFTYPE_A_LAW: samples in alaw * @GST_BUFTYPE_A_LAW: samples in alaw
* @GST_BUFTYPE_IMA_ADPCM: samples in ima adpcm * @GST_BUFTYPE_IMA_ADPCM: samples in ima adpcm
* @GST_BUFTYPE_MPEG: samples in mpeg audio format * @GST_BUFTYPE_MPEG: samples in mpeg audio (but not AAC) format
* @GST_BUFTYPE_GSM: samples in gsm format * @GST_BUFTYPE_GSM: samples in gsm format
* @GST_BUFTYPE_IEC958: samples in IEC958 frames (e.g. AC3) * @GST_BUFTYPE_IEC958: samples in IEC958 frames (e.g. AC3)
* @GST_BUFTYPE_AC3: samples in AC3 format * @GST_BUFTYPE_AC3: samples in AC3 format
* @GST_BUFTYPE_EAC3: samples in EAC3 format * @GST_BUFTYPE_EAC3: samples in EAC3 format
* @GST_BUFTYPE_DTS: samples in DTS format * @GST_BUFTYPE_DTS: samples in DTS format
* @GST_BUFTYPE_MPEG2_AAC: samples in MPEG-2 AAC format
* @GST_BUFTYPE_MPEG4_AAC: samples in MPEG-4 AAC format
* *
* The format of the samples in the ringbuffer. * The format of the samples in the ringbuffer.
*/ */
@ -109,7 +111,9 @@ typedef enum
GST_BUFTYPE_IEC958, GST_BUFTYPE_IEC958,
GST_BUFTYPE_AC3, GST_BUFTYPE_AC3,
GST_BUFTYPE_EAC3, GST_BUFTYPE_EAC3,
GST_BUFTYPE_DTS GST_BUFTYPE_DTS,
GST_BUFTYPE_MPEG2_AAC,
GST_BUFTYPE_MPEG4_AAC,
} GstBufferFormatType; } GstBufferFormatType;
typedef enum typedef enum
@ -161,7 +165,9 @@ typedef enum
GST_IEC958, GST_IEC958,
GST_AC3, GST_AC3,
GST_EAC3, GST_EAC3,
GST_DTS GST_DTS,
GST_MPEG2_AAC,
GST_MPEG4_AAC,
} GstBufferFormat; } GstBufferFormat;
/** /**

View file

@ -51,6 +51,7 @@ static const gchar *schema_list[] = {
"exif", "exif",
"photoshop", "photoshop",
"Iptc4xmpCore", "Iptc4xmpCore",
"Iptc4xmpExt",
NULL NULL
}; };
@ -112,14 +113,31 @@ xmp_serialization_data_use_schema (XmpSerializationData * serdata,
return FALSE; return FALSE;
} }
typedef enum
{
GstXmpTagTypeSimple = 0,
GstXmpTagTypeBag,
GstXmpTagTypeSeq,
GstXmpTagTypeStruct,
/* Not really a xmp type, this is a tag that in gst is represented with
* a single value and on xmp it needs 2 (or more) simple values
*
* e.g. GST_TAG_GEO_LOCATION_ELEVATION needs to be mapped into 2 complementary
* tags in the exif's schema. One of them stores the absolute elevation,
* and the other one stores if it is above of below sea level.
*/
GstXmpTagTypeCompound
} GstXmpTagType;
#define GST_XMP_TAG_TYPE_SIMPLE 0
#define GST_XMP_TAG_TYPE_BAG 1
#define GST_XMP_TAG_TYPE_SEQ 2
struct _XmpTag struct _XmpTag
{ {
const gchar *gst_tag;
const gchar *tag_name; const gchar *tag_name;
gint type; GstXmpTagType type;
/* Used for struct and compound types */
GSList *children;
XmpSerializationFunc serialize; XmpSerializationFunc serialize;
XmpDeserializationFunc deserialize; XmpDeserializationFunc deserialize;
@ -129,10 +147,10 @@ static GstTagMergeMode
xmp_tag_get_merge_mode (XmpTag * xmptag) xmp_tag_get_merge_mode (XmpTag * xmptag)
{ {
switch (xmptag->type) { switch (xmptag->type) {
case GST_XMP_TAG_TYPE_BAG: case GstXmpTagTypeBag:
case GST_XMP_TAG_TYPE_SEQ: case GstXmpTagTypeSeq:
return GST_TAG_MERGE_APPEND; return GST_TAG_MERGE_APPEND;
case GST_XMP_TAG_TYPE_SIMPLE: case GstXmpTagTypeSimple:
default: default:
return GST_TAG_MERGE_KEEP; return GST_TAG_MERGE_KEEP;
} }
@ -142,31 +160,25 @@ static const gchar *
xmp_tag_get_type_name (XmpTag * xmptag) xmp_tag_get_type_name (XmpTag * xmptag)
{ {
switch (xmptag->type) { switch (xmptag->type) {
case GST_XMP_TAG_TYPE_SEQ: case GstXmpTagTypeSeq:
return "rdf:Seq"; return "rdf:Seq";
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case GST_XMP_TAG_TYPE_BAG: case GstXmpTagTypeBag:
return "rdf:Bag"; return "rdf:Bag";
} }
} }
struct _PendingXmpTag struct _PendingXmpTag
{ {
const gchar *gst_tag;
XmpTag *xmp_tag; XmpTag *xmp_tag;
gchar *str; gchar *str;
}; };
typedef struct _PendingXmpTag PendingXmpTag; typedef struct _PendingXmpTag PendingXmpTag;
/* /*
* A schema is a mapping of strings (the tag name in gstreamer) to a list of * A schema is a mapping of strings (the tag name in gstreamer) to a list of
* tags in xmp (XmpTag). We need a list because some tags are split into 2 * tags in xmp (XmpTag).
* when serialized into xmp.
* e.g. GST_TAG_GEO_LOCATION_ELEVATION needs to be mapped into 2 complementary
* tags in the exif's schema. One of them stores the absolute elevation,
* and the other one stores if it is above of below sea level.
*/ */
typedef GHashTable GstXmpSchema; typedef GHashTable GstXmpSchema;
#define gst_xmp_schema_lookup g_hash_table_lookup #define gst_xmp_schema_lookup g_hash_table_lookup
@ -182,6 +194,21 @@ gst_xmp_schema_new ()
*/ */
static GHashTable *__xmp_schemas; static GHashTable *__xmp_schemas;
static GstXmpSchema *
_gst_xmp_get_schema (const gchar * name)
{
GQuark key;
GstXmpSchema *schema;
key = g_quark_from_string (name);
schema = g_hash_table_lookup (__xmp_schemas, GUINT_TO_POINTER (key));
if (!schema) {
GST_WARNING ("Schema %s doesn't exist", name);
}
return schema;
}
static void static void
_gst_xmp_add_schema (const gchar * name, GstXmpSchema * schema) _gst_xmp_add_schema (const gchar * name, GstXmpSchema * schema)
{ {
@ -199,19 +226,59 @@ _gst_xmp_add_schema (const gchar * name, GstXmpSchema * schema)
} }
static void static void
_gst_xmp_schema_add_mapping (GstXmpSchema * schema, const gchar * gst_tag, _gst_xmp_schema_add_mapping (GstXmpSchema * schema, XmpTag * tag)
GPtrArray * array)
{ {
GQuark key; GQuark key;
key = g_quark_from_string (gst_tag); key = g_quark_from_string (tag->gst_tag);
if (gst_xmp_schema_lookup (schema, GUINT_TO_POINTER (key))) { if (gst_xmp_schema_lookup (schema, GUINT_TO_POINTER (key))) {
GST_WARNING ("Tag %s already present for the schema", gst_tag); GST_WARNING ("Tag %s already present for the schema", tag->gst_tag);
g_assert_not_reached (); g_assert_not_reached ();
return; return;
} }
gst_xmp_schema_insert (schema, GUINT_TO_POINTER (key), array); gst_xmp_schema_insert (schema, GUINT_TO_POINTER (key), tag);
}
static XmpTag *
gst_xmp_tag_create (const gchar * gst_tag, const gchar * xmp_tag,
gint xmp_type, XmpSerializationFunc serialization_func,
XmpDeserializationFunc deserialization_func)
{
XmpTag *xmpinfo;
xmpinfo = g_slice_new (XmpTag);
xmpinfo->gst_tag = gst_tag;
xmpinfo->tag_name = xmp_tag;
xmpinfo->type = xmp_type;
xmpinfo->serialize = serialization_func;
xmpinfo->deserialize = deserialization_func;
xmpinfo->children = NULL;
return xmpinfo;
}
static XmpTag *
gst_xmp_tag_create_compound (const gchar * gst_tag, const gchar * xmp_tag_a,
const gchar * xmp_tag_b, XmpSerializationFunc serialization_func_a,
XmpSerializationFunc serialization_func_b,
XmpDeserializationFunc deserialization_func)
{
XmpTag *xmptag;
XmpTag *xmptag_a =
gst_xmp_tag_create (gst_tag, xmp_tag_a, GstXmpTagTypeSimple,
serialization_func_a, deserialization_func);
XmpTag *xmptag_b =
gst_xmp_tag_create (gst_tag, xmp_tag_b, GstXmpTagTypeSimple,
serialization_func_b, deserialization_func);
xmptag =
gst_xmp_tag_create (gst_tag, NULL, GstXmpTagTypeCompound, NULL, NULL);
xmptag->children = g_slist_prepend (xmptag->children, xmptag_b);
xmptag->children = g_slist_prepend (xmptag->children, xmptag_a);
return xmptag;
} }
static void static void
@ -220,19 +287,9 @@ _gst_xmp_schema_add_simple_mapping (GstXmpSchema * schema,
XmpSerializationFunc serialization_func, XmpSerializationFunc serialization_func,
XmpDeserializationFunc deserialization_func) XmpDeserializationFunc deserialization_func)
{ {
XmpTag *xmpinfo; _gst_xmp_schema_add_mapping (schema,
GPtrArray *array; gst_xmp_tag_create (gst_tag, xmp_tag, xmp_type, serialization_func,
deserialization_func));
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = xmp_tag;
xmpinfo->type = xmp_type;
xmpinfo->serialize = serialization_func;
xmpinfo->deserialize = deserialization_func;
array = g_ptr_array_sized_new (1);
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema, gst_tag, array);
} }
/* /*
@ -240,6 +297,7 @@ _gst_xmp_schema_add_simple_mapping (GstXmpSchema * schema,
* appended, and the API is not public, so we shouldn't * appended, and the API is not public, so we shouldn't
* have our lists modified during usage * have our lists modified during usage
*/ */
#if 0
static GPtrArray * static GPtrArray *
_xmp_tag_get_mapping (const gchar * gst_tag, XmpSerializationData * serdata) _xmp_tag_get_mapping (const gchar * gst_tag, XmpSerializationData * serdata)
{ {
@ -260,6 +318,7 @@ _xmp_tag_get_mapping (const gchar * gst_tag, XmpSerializationData * serdata)
} }
return ret; return ret;
} }
#endif
/* finds the gst tag that maps to this xmp tag in this schema */ /* finds the gst tag that maps to this xmp tag in this schema */
static const gchar * static const gchar *
@ -269,22 +328,30 @@ _gst_xmp_schema_get_mapping_reverse (GstXmpSchema * schema,
GHashTableIter iter; GHashTableIter iter;
gpointer key, value; gpointer key, value;
const gchar *ret = NULL; const gchar *ret = NULL;
gint index;
/* Iterate over the hashtable */ /* Iterate over the hashtable */
g_hash_table_iter_init (&iter, schema); g_hash_table_iter_init (&iter, schema);
while (!ret && g_hash_table_iter_next (&iter, &key, &value)) { while (!ret && g_hash_table_iter_next (&iter, &key, &value)) {
GPtrArray *array = (GPtrArray *) value; XmpTag *xmpinfo = (XmpTag *) value;
/* each mapping might contain complementary tags */
for (index = 0; index < array->len; index++) {
XmpTag *xmpinfo = (XmpTag *) g_ptr_array_index (array, index);
if (xmpinfo->tag_name) {
if (strcmp (xmpinfo->tag_name, xmp_tag) == 0) { if (strcmp (xmpinfo->tag_name, xmp_tag) == 0) {
*_xmp_tag = xmpinfo; *_xmp_tag = xmpinfo;
ret = g_quark_to_string (GPOINTER_TO_UINT (key)); ret = g_quark_to_string (GPOINTER_TO_UINT (key));
goto out; goto out;
} }
} else if (xmpinfo->children) {
GSList *iter;
for (iter = xmpinfo->children; iter; iter = g_slist_next (iter)) {
XmpTag *child = iter->data;
if (strcmp (child->tag_name, xmp_tag) == 0) {
*_xmp_tag = child;
ret = g_quark_to_string (GPOINTER_TO_UINT (key));
goto out;
}
}
} else {
g_assert_not_reached ();
} }
} }
@ -839,7 +906,6 @@ deserialize_tiff_orientation (XmpTag * xmptag, GstTagList * taglist,
static gpointer static gpointer
_init_xmp_tag_map (gpointer user_data) _init_xmp_tag_map (gpointer user_data)
{ {
GPtrArray *array;
XmpTag *xmpinfo; XmpTag *xmpinfo;
GstXmpSchema *schema; GstXmpSchema *schema;
@ -851,135 +917,115 @@ _init_xmp_tag_map (gpointer user_data)
*/ */
schema = gst_xmp_schema_new (); schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_ARTIST, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_ARTIST,
"dc:creator", GST_XMP_TAG_TYPE_SEQ, NULL, NULL); "dc:creator", GstXmpTagTypeSeq, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_COPYRIGHT, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_COPYRIGHT,
"dc:rights", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "dc:rights", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE, "dc:date", _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE, "dc:date",
GST_XMP_TAG_TYPE_SEQ, NULL, NULL); GstXmpTagTypeSeq, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DESCRIPTION, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DESCRIPTION,
"dc:description", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "dc:description", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_KEYWORDS, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_KEYWORDS,
"dc:subject", GST_XMP_TAG_TYPE_BAG, NULL, NULL); "dc:subject", GstXmpTagTypeBag, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_TITLE, "dc:title", _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_TITLE, "dc:title",
GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); GstXmpTagTypeSimple, NULL, NULL);
/* FIXME: we probably want GST_TAG_{,AUDIO_,VIDEO_}MIME_TYPE */ /* FIXME: we probably want GST_TAG_{,AUDIO_,VIDEO_}MIME_TYPE */
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_VIDEO_CODEC, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_VIDEO_CODEC,
"dc:format", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "dc:format", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_add_schema ("dc", schema); _gst_xmp_add_schema ("dc", schema);
/* xap (xmp) schema */ /* xap (xmp) schema */
schema = gst_xmp_schema_new (); schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_USER_RATING, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_USER_RATING,
"xmp:Rating", GST_XMP_TAG_TYPE_SIMPLE, NULL, deserialize_xmp_rating); "xmp:Rating", GstXmpTagTypeSimple, NULL, deserialize_xmp_rating);
_gst_xmp_add_schema ("xap", schema); _gst_xmp_add_schema ("xap", schema);
/* tiff */ /* tiff */
schema = gst_xmp_schema_new (); schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, _gst_xmp_schema_add_simple_mapping (schema,
GST_TAG_DEVICE_MANUFACTURER, "tiff:Make", GST_XMP_TAG_TYPE_SIMPLE, NULL, GST_TAG_DEVICE_MANUFACTURER, "tiff:Make", GstXmpTagTypeSimple, NULL,
NULL); NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DEVICE_MODEL, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DEVICE_MODEL,
"tiff:Model", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "tiff:Model", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_APPLICATION_NAME, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_APPLICATION_NAME,
"tiff:Software", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "tiff:Software", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_IMAGE_ORIENTATION, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_IMAGE_ORIENTATION,
"tiff:Orientation", GST_XMP_TAG_TYPE_SIMPLE, serialize_tiff_orientation, "tiff:Orientation", GstXmpTagTypeSimple, serialize_tiff_orientation,
deserialize_tiff_orientation); deserialize_tiff_orientation);
_gst_xmp_add_schema ("tiff", schema); _gst_xmp_add_schema ("tiff", schema);
/* exif schema */ /* exif schema */
schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE_TIME, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE_TIME,
"exif:DateTimeOriginal", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "exif:DateTimeOriginal", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, _gst_xmp_schema_add_simple_mapping (schema,
GST_TAG_GEO_LOCATION_LATITUDE, "exif:GPSLatitude", GST_TAG_GEO_LOCATION_LATITUDE, "exif:GPSLatitude",
GST_XMP_TAG_TYPE_SIMPLE, serialize_exif_latitude, GstXmpTagTypeSimple, serialize_exif_latitude, deserialize_exif_latitude);
deserialize_exif_latitude);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_LONGITUDE, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_LONGITUDE,
"exif:GPSLongitude", GST_XMP_TAG_TYPE_SIMPLE, serialize_exif_longitude, "exif:GPSLongitude", GstXmpTagTypeSimple, serialize_exif_longitude,
deserialize_exif_longitude); deserialize_exif_longitude);
_gst_xmp_schema_add_simple_mapping (schema, _gst_xmp_schema_add_simple_mapping (schema,
GST_TAG_CAPTURING_EXPOSURE_COMPENSATION, "exif:ExposureBiasValue", GST_TAG_CAPTURING_EXPOSURE_COMPENSATION, "exif:ExposureBiasValue",
GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); GstXmpTagTypeSimple, NULL, NULL);
/* compound exif tags */ /* compound exif tags */
array = g_ptr_array_sized_new (2); xmpinfo = gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_ELEVATION,
xmpinfo = g_slice_new (XmpTag); "exif:GPSAltitude", "exif:GPSAltitudeRef", serialize_exif_altitude,
xmpinfo->tag_name = "exif:GPSAltitude"; serialize_exif_altituderef, deserialize_exif_altitude);
xmpinfo->serialize = serialize_exif_altitude; _gst_xmp_schema_add_mapping (schema, xmpinfo);
xmpinfo->deserialize = deserialize_exif_altitude;
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSAltitudeRef";
xmpinfo->serialize = serialize_exif_altituderef;
xmpinfo->deserialize = deserialize_exif_altitude;
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema, GST_TAG_GEO_LOCATION_ELEVATION, array);
array = g_ptr_array_sized_new (2); xmpinfo = gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_MOVEMENT_SPEED,
xmpinfo = g_slice_new (XmpTag); "exif:GPSSpeed", "exif:GPSSpeedRef", serialize_exif_gps_speed,
xmpinfo->tag_name = "exif:GPSSpeed"; serialize_exif_gps_speedref, deserialize_exif_gps_speed);
xmpinfo->serialize = serialize_exif_gps_speed; _gst_xmp_schema_add_mapping (schema, xmpinfo);
xmpinfo->deserialize = deserialize_exif_gps_speed;
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSSpeedRef";
xmpinfo->serialize = serialize_exif_gps_speedref;
xmpinfo->deserialize = deserialize_exif_gps_speed;
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, array);
array = g_ptr_array_sized_new (2); xmpinfo =
xmpinfo = g_slice_new (XmpTag); gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION,
xmpinfo->tag_name = "exif:GPSTrack"; "exif:GPSTrack", "exif:GPSTrackRef", serialize_exif_gps_direction,
xmpinfo->serialize = serialize_exif_gps_direction; serialize_exif_gps_directionref, deserialize_exif_gps_track);
xmpinfo->deserialize = deserialize_exif_gps_track; _gst_xmp_schema_add_mapping (schema, xmpinfo);
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo); xmpinfo = gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION,
xmpinfo = g_slice_new (XmpTag); "exif:GPSImgDirection", "exif:GPSImgDirectionRef",
xmpinfo->tag_name = "exif:GPSTrackRef"; serialize_exif_gps_direction, serialize_exif_gps_directionref,
xmpinfo->serialize = serialize_exif_gps_directionref; deserialize_exif_gps_img_direction);
xmpinfo->deserialize = deserialize_exif_gps_track; _gst_xmp_schema_add_mapping (schema, xmpinfo);
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, array);
array = g_ptr_array_sized_new (2);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSImgDirection";
xmpinfo->serialize = serialize_exif_gps_direction;
xmpinfo->deserialize = deserialize_exif_gps_img_direction;
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSImgDirectionRef";
xmpinfo->serialize = serialize_exif_gps_directionref;
xmpinfo->deserialize = deserialize_exif_gps_img_direction;
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, array);
_gst_xmp_add_schema ("exif", schema); _gst_xmp_add_schema ("exif", schema);
/* photoshop schema */ /* photoshop schema */
schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, _gst_xmp_schema_add_simple_mapping (schema,
GST_TAG_GEO_LOCATION_COUNTRY, "photoshop:Country", GST_TAG_GEO_LOCATION_COUNTRY, "photoshop:Country",
GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_CITY, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_CITY,
"photoshop:City", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); "photoshop:City", GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_add_schema ("photoshop", schema); _gst_xmp_add_schema ("photoshop", schema);
/* iptc4xmpcore schema */ /* iptc4xmpcore schema */
schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, _gst_xmp_schema_add_simple_mapping (schema,
GST_TAG_GEO_LOCATION_SUBLOCATION, "Iptc4xmpCore:Location", GST_TAG_GEO_LOCATION_SUBLOCATION, "Iptc4xmpCore:Location",
GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL); GstXmpTagTypeSimple, NULL, NULL);
_gst_xmp_add_schema ("Iptc4xmpCore", schema); _gst_xmp_add_schema ("Iptc4xmpCore", schema);
/* iptc4xmpext schema */
schema = gst_xmp_schema_new ();
xmpinfo = gst_xmp_tag_create (NULL, "Iptc4xmpExt:LocationShown",
GstXmpTagTypeStruct, NULL, NULL);
xmpinfo->children = g_slist_prepend (xmpinfo->children,
gst_xmp_tag_create (GST_TAG_GEO_LOCATION_SUBLOCATION,
"LocationDetails:Sublocation", GstXmpTagTypeSimple, NULL, NULL));
xmpinfo->children =
g_slist_prepend (xmpinfo->children,
gst_xmp_tag_create (GST_TAG_GEO_LOCATION_CITY,
"LocationDetails:City", GstXmpTagTypeSimple, NULL, NULL));
xmpinfo->children =
g_slist_prepend (xmpinfo->children,
gst_xmp_tag_create (GST_TAG_GEO_LOCATION_COUNTRY,
"LocationDetails:Country", GstXmpTagTypeSimple, NULL, NULL));
_gst_xmp_schema_add_mapping (schema, xmpinfo);
_gst_xmp_add_schema ("Iptc4xmpExt", schema);
return NULL; return NULL;
} }
@ -1004,6 +1050,7 @@ static const GstXmpNamespaceMatch ns_match[] = {
{"xap", "http://ns.adobe.com/xap/1.0/"}, {"xap", "http://ns.adobe.com/xap/1.0/"},
{"photoshop", "http://ns.adobe.com/photoshop/1.0/"}, {"photoshop", "http://ns.adobe.com/photoshop/1.0/"},
{"Iptc4xmpCore", "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"}, {"Iptc4xmpCore", "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"},
{"Iptc4xmpExt", "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"},
{NULL, NULL} {NULL, NULL}
}; };
@ -1017,11 +1064,14 @@ struct _GstXmpNamespaceMap
/* parsing */ /* parsing */
static void static void
read_one_tag (GstTagList * list, const gchar * tag, XmpTag * xmptag, read_one_tag (GstTagList * list, XmpTag * xmptag,
const gchar * v, GSList ** pending_tags) const gchar * v, GSList ** pending_tags)
{ {
GType tag_type; GType tag_type;
GstTagMergeMode merge_mode; GstTagMergeMode merge_mode;
const gchar *tag = xmptag->gst_tag;
g_return_if_fail (tag != NULL);
if (xmptag && xmptag->deserialize) { if (xmptag && xmptag->deserialize) {
xmptag->deserialize (xmptag, list, tag, xmptag->tag_name, v, pending_tags); xmptag->deserialize (xmptag, list, tag, xmptag->tag_name, v, pending_tags);
@ -1208,17 +1258,27 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
gboolean in_tag; gboolean in_tag;
gchar *part, *pp; gchar *part, *pp;
guint i; guint i;
const gchar *last_tag = NULL;
XmpTag *last_xmp_tag = NULL; XmpTag *last_xmp_tag = NULL;
GSList *pending_tags = NULL; GSList *pending_tags = NULL;
/* Used for strucuture xmp tags */
XmpTag *context_tag = NULL;
GstXmpNamespaceMap ns_map[] = { GstXmpNamespaceMap ns_map[] = {
{"dc", NULL}, {"dc", NULL}
{"exif", NULL}, ,
{"tiff", NULL}, {"exif", NULL}
{"xap", NULL}, ,
{"photoshop", NULL}, {"tiff", NULL}
{"Iptc4xmpCore", NULL}, ,
{"xap", NULL}
,
{"photoshop", NULL}
,
{"Iptc4xmpCore", NULL}
,
{"Iptc4xmpExt", NULL}
,
{NULL, NULL} {NULL, NULL}
}; };
@ -1226,6 +1286,8 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
GST_LOG ("Starting xmp parsing");
xps = gst_buffer_map (buffer, &len, NULL, GST_MAP_READ); xps = gst_buffer_map (buffer, &len, NULL, GST_MAP_READ);
g_return_val_if_fail (len > 0, NULL); g_return_val_if_fail (len > 0, NULL);
@ -1326,19 +1388,41 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
} }
} }
} else { } else {
const gchar *gst_tag;
XmpTag *xmp_tag = NULL; XmpTag *xmp_tag = NULL;
/* FIXME: eventualy rewrite ns /* FIXME: eventualy rewrite ns
* find ':' * find ':'
* check if ns before ':' is in ns_map and ns_map[i].gstreamer_ns!=NULL * check if ns before ':' is in ns_map and ns_map[i].gstreamer_ns!=NULL
* do 2 stage filter in tag_matches * do 2 stage filter in tag_matches
*/ */
gst_tag = _gst_xmp_tag_get_mapping_reverse (as, &xmp_tag); if (context_tag) {
if (gst_tag) { GSList *iter;
for (iter = context_tag->children; iter;
iter = g_slist_next (iter)) {
XmpTag *child = iter->data;
GST_DEBUG ("Looking at child tag %s : %s", child->tag_name,
as);
if (strcmp (child->tag_name, as) == 0) {
xmp_tag = child;
break;
}
}
} else {
GST_LOG ("Looking for tag: %s", as);
_gst_xmp_tag_get_mapping_reverse (as, &xmp_tag);
}
if (xmp_tag) {
PendingXmpTag *ptag; PendingXmpTag *ptag;
GST_DEBUG ("Found xmp tag: %s -> %s", xmp_tag->tag_name,
xmp_tag->gst_tag);
/* we shouldn't find a xmp structure here */
g_assert (xmp_tag->gst_tag != NULL);
ptag = g_slice_new (PendingXmpTag); ptag = g_slice_new (PendingXmpTag);
ptag->gst_tag = gst_tag;
ptag->xmp_tag = xmp_tag; ptag->xmp_tag = xmp_tag;
ptag->str = g_strdup (v); ptag->str = g_strdup (v);
@ -1365,15 +1449,42 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
/* skip rdf tags for now */ /* skip rdf tags for now */
if (strncmp (part, "rdf:", 4)) { if (strncmp (part, "rdf:", 4)) {
const gchar *parttag; /* if we're inside some struct, we look only on its children */
if (context_tag) {
GSList *iter;
parttag = _gst_xmp_tag_get_mapping_reverse (part, &last_xmp_tag); /* check if this is the closing of the context */
if (parttag) { if (part[0] == '/'
last_tag = parttag; && strcmp (part + 1, context_tag->tag_name) == 0) {
GST_DEBUG ("Closing context tag %s", part);
context_tag = NULL;
} else {
for (iter = context_tag->children; iter;
iter = g_slist_next (iter)) {
XmpTag *child = iter->data;
GST_DEBUG ("Looking at child tag %s : %s", child->tag_name,
part);
if (strcmp (child->tag_name, part) == 0) {
last_xmp_tag = child;
break;
}
}
}
} else {
GST_LOG ("Looking for tag: %s", part);
_gst_xmp_tag_get_mapping_reverse (part, &last_xmp_tag);
if (last_xmp_tag && last_xmp_tag->type == GstXmpTagTypeStruct) {
context_tag = last_xmp_tag;
last_xmp_tag = NULL;
}
} }
} }
} }
} }
GST_LOG ("Next cycle");
/* next cycle */ /* next cycle */
ne++; ne++;
if (ne < xp2) { if (ne < xp2) {
@ -1393,15 +1504,23 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
if (ns[0] != '\n' && &ns[1] <= ne) { if (ns[0] != '\n' && &ns[1] <= ne) {
/* only log non-newline nodes, we still have to parse them */ /* only log non-newline nodes, we still have to parse them */
GST_INFO ("txt: %s", part); GST_INFO ("txt: %s", part);
if (last_tag) { if (last_xmp_tag) {
PendingXmpTag *ptag; PendingXmpTag *ptag;
ptag = g_slice_new (PendingXmpTag); GST_DEBUG ("Found tag %s -> %s", last_xmp_tag->tag_name,
ptag->gst_tag = last_tag; last_xmp_tag->gst_tag);
ptag->xmp_tag = last_xmp_tag;
ptag->str = g_strdup (part);
pending_tags = g_slist_append (pending_tags, ptag); if (last_xmp_tag->type == GstXmpTagTypeStruct) {
g_assert (context_tag == NULL); /* we can't handle struct nesting currently */
context_tag = last_xmp_tag;
} else {
ptag = g_slice_new (PendingXmpTag);
ptag->xmp_tag = last_xmp_tag;
ptag->str = g_strdup (part);
pending_tags = g_slist_append (pending_tags, ptag);
}
} }
} }
/* next cycle */ /* next cycle */
@ -1416,7 +1535,7 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
pending_tags = g_slist_delete_link (pending_tags, pending_tags); pending_tags = g_slist_delete_link (pending_tags, pending_tags);
read_one_tag (list, ptag->gst_tag, ptag->xmp_tag, ptag->str, &pending_tags); read_one_tag (list, ptag->xmp_tag, ptag->str, &pending_tags);
g_free (ptag->str); g_free (ptag->str);
g_slice_free (PendingXmpTag, ptag); g_slice_free (PendingXmpTag, ptag);
@ -1522,70 +1641,93 @@ gst_value_serialize_xmp (const GValue * value)
} }
static void static void
write_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data) write_one_tag (const GstTagList * list, XmpTag * xmp_tag, gpointer user_data)
{ {
guint i = 0, ct = gst_tag_list_get_tag_size (list, tag), tag_index; guint i = 0, ct;
XmpSerializationData *serialization_data = user_data; XmpSerializationData *serialization_data = user_data;
GString *data = serialization_data->data; GString *data = serialization_data->data;
GPtrArray *xmp_tag_array = NULL;
char *s; char *s;
/* map gst-tag to xmp tag */ /* struct type handled differently */
xmp_tag_array = _xmp_tag_get_mapping (tag, serialization_data); if (xmp_tag->type == GstXmpTagTypeStruct ||
xmp_tag->type == GstXmpTagTypeCompound) {
GSList *iter;
gboolean use_it = FALSE;
if (!xmp_tag_array) { /* check if any of the inner tags are present on the taglist */
GST_WARNING ("no mapping for %s to xmp", tag); for (iter = xmp_tag->children; iter && !use_it; iter = g_slist_next (iter)) {
XmpTag *child_tag = iter->data;
if (gst_tag_list_get_value_index (list, child_tag->gst_tag, 0) != NULL) {
use_it = TRUE;
break;
}
}
if (use_it) {
if (xmp_tag->tag_name)
string_open_tag (data, xmp_tag->tag_name);
/* now write it */
for (iter = xmp_tag->children; iter; iter = g_slist_next (iter)) {
write_one_tag (list, iter->data, user_data);
}
if (xmp_tag->tag_name)
string_close_tag (data, xmp_tag->tag_name);
}
return; return;
} }
for (tag_index = 0; tag_index < xmp_tag_array->len; tag_index++) { /* at this point we must have a gst_tag */
XmpTag *xmp_tag; g_assert (xmp_tag->gst_tag);
if (gst_tag_list_get_value_index (list, xmp_tag->gst_tag, 0) == NULL)
return;
xmp_tag = g_ptr_array_index (xmp_tag_array, tag_index); ct = gst_tag_list_get_tag_size (list, xmp_tag->gst_tag);
string_open_tag (data, xmp_tag->tag_name); string_open_tag (data, xmp_tag->tag_name);
/* fast path for single valued tag */ /* fast path for single valued tag */
if (ct == 1 || xmp_tag->type == GST_XMP_TAG_TYPE_SIMPLE) { if (ct == 1 || xmp_tag->type == GstXmpTagTypeSimple) {
if (xmp_tag->serialize) {
s = xmp_tag->serialize (gst_tag_list_get_value_index (list,
xmp_tag->gst_tag, 0));
} else {
s = gst_value_serialize_xmp (gst_tag_list_get_value_index (list,
xmp_tag->gst_tag, 0));
}
if (s) {
g_string_append (data, s);
g_free (s);
} else {
GST_WARNING ("unhandled type for %s to xmp", xmp_tag->gst_tag);
}
} else {
const gchar *typename;
typename = xmp_tag_get_type_name (xmp_tag);
string_open_tag (data, typename);
for (i = 0; i < ct; i++) {
GST_DEBUG ("mapping %s[%u/%u] to xmp", xmp_tag->gst_tag, i, ct);
if (xmp_tag->serialize) { if (xmp_tag->serialize) {
s = xmp_tag->serialize (gst_tag_list_get_value_index (list, tag, 0)); s = xmp_tag->serialize (gst_tag_list_get_value_index (list,
xmp_tag->gst_tag, i));
} else { } else {
s = gst_value_serialize_xmp (gst_tag_list_get_value_index (list, tag, s = gst_value_serialize_xmp (gst_tag_list_get_value_index (list,
0)); xmp_tag->gst_tag, i));
} }
if (s) { if (s) {
string_open_tag (data, "rdf:li");
g_string_append (data, s); g_string_append (data, s);
string_close_tag (data, "rdf:li");
g_free (s); g_free (s);
} else { } else {
GST_WARNING ("unhandled type for %s to xmp", tag); GST_WARNING ("unhandled type for %s to xmp", xmp_tag->gst_tag);
} }
} else {
const gchar *typename;
typename = xmp_tag_get_type_name (xmp_tag);
string_open_tag (data, typename);
for (i = 0; i < ct; i++) {
GST_DEBUG ("mapping %s[%u/%u] to xmp", tag, i, ct);
if (xmp_tag->serialize) {
s = xmp_tag->serialize (gst_tag_list_get_value_index (list, tag, i));
} else {
s = gst_value_serialize_xmp (gst_tag_list_get_value_index (list, tag,
i));
}
if (s) {
string_open_tag (data, "rdf:li");
g_string_append (data, s);
string_close_tag (data, "rdf:li");
g_free (s);
} else {
GST_WARNING ("unhandled type for %s to xmp", tag);
}
}
string_close_tag (data, typename);
} }
string_close_tag (data, typename);
string_close_tag (data, xmp_tag->tag_name);
} }
string_close_tag (data, xmp_tag->tag_name);
} }
/** /**
@ -1639,8 +1781,25 @@ gst_tag_list_to_xmp_buffer_full (const GstTagList * list, gboolean read_only,
g_string_append (data, ">\n"); g_string_append (data, ">\n");
g_string_append (data, "<rdf:Description rdf:about=\"\">\n"); g_string_append (data, "<rdf:Description rdf:about=\"\">\n");
/* iterate the taglist */ /* iterate the schemas */
gst_tag_list_foreach (list, write_one_tag, &serialization_data); if (schemas == NULL) {
/* use all schemas */
schemas = gst_tag_xmp_list_schemas ();
}
for (i = 0; schemas[i] != NULL; i++) {
GstXmpSchema *schema = _gst_xmp_get_schema (schemas[i]);
GHashTableIter iter;
gpointer key, value;
if (schema == NULL)
continue;
/* Iterate over the hashtable */
g_hash_table_iter_init (&iter, schema);
while (g_hash_table_iter_next (&iter, &key, &value)) {
write_one_tag (list, value, (gpointer) & serialization_data);
}
}
/* xmp footer */ /* xmp footer */
g_string_append (data, "</rdf:Description>\n"); g_string_append (data, "</rdf:Description>\n");

View file

@ -1,4 +1,4 @@
/* GStreamer XmpConfig /* GStreamer TagXmpWriter
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk> * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
@ -18,7 +18,7 @@
*/ */
/** /**
* SECTION:gstxmpconfig * SECTION:gsttagxmpwriter
* @short_description: Interface for elements that provide XMP serialization * @short_description: Interface for elements that provide XMP serialization
* *
* <refsect2> * <refsect2>

View file

@ -34,6 +34,28 @@ A wide range of video and audio decoders, encoders, and filters are included.
</GitRepository> </GitRepository>
</repository> </repository>
<release>
<Version>
<revision>0.10.34</revision>
<branch>0.10</branch>
<name>Lemmings</name>
<created>2011-05-13</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-0.10.34.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-0.10.34.tar.gz" />
</Version>
</release>
<release>
<Version>
<revision>0.10.33</revision>
<branch>0.10</branch>
<name>Relaxing Distractions</name>
<created>2011-05-10</created>
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-0.10.33.tar.bz2" />
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-0.10.33.tar.gz" />
</Version>
</release>
<release> <release>
<Version> <Version>
<revision>0.10.32</revision> <revision>0.10.32</revision>

View file

@ -928,6 +928,7 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
GList *tmp, *tosync = NULL; GList *tmp, *tosync = NULL;
const GstCaps *format; const GstCaps *format;
const GstCaps *restriction; const GstCaps *restriction;
const gchar *missing_element_name;
format = gst_encoding_profile_get_format (sprof); format = gst_encoding_profile_get_format (sprof);
restriction = gst_encoding_profile_get_restriction (sprof); restriction = gst_encoding_profile_get_restriction (sprof);
@ -1177,10 +1178,19 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
cspace = gst_element_factory_make ("ffmpegcolorspace", NULL); cspace = gst_element_factory_make ("ffmpegcolorspace", NULL);
scale = gst_element_factory_make ("videoscale", NULL); scale = gst_element_factory_make ("videoscale", NULL);
if (!scale) {
missing_element_name = "videoscale";
goto missing_element;
}
/* 4-tap scaling and black borders */ /* 4-tap scaling and black borders */
g_object_set (scale, "method", 2, "add-borders", TRUE, NULL); g_object_set (scale, "method", 2, "add-borders", TRUE, NULL);
cspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL); cspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
if (!cspace || !cspace2) {
missing_element_name = "ffmpegcolorspace";
goto missing_element;
}
gst_bin_add_many ((GstBin *) ebin, cspace, scale, cspace2, NULL); gst_bin_add_many ((GstBin *) ebin, cspace, scale, cspace2, NULL);
tosync = g_list_append (tosync, cspace); tosync = g_list_append (tosync, cspace);
tosync = g_list_append (tosync, scale); tosync = g_list_append (tosync, scale);
@ -1197,6 +1207,11 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
if (!gst_encoding_video_profile_get_variableframerate if (!gst_encoding_video_profile_get_variableframerate
(GST_ENCODING_VIDEO_PROFILE (sprof))) { (GST_ENCODING_VIDEO_PROFILE (sprof))) {
vrate = gst_element_factory_make ("videorate", NULL); vrate = gst_element_factory_make ("videorate", NULL);
if (!vrate) {
missing_element_name = "videorate";
goto missing_element;
}
gst_bin_add ((GstBin *) ebin, vrate); gst_bin_add ((GstBin *) ebin, vrate);
tosync = g_list_prepend (tosync, vrate); tosync = g_list_prepend (tosync, vrate);
sgroup->converters = g_list_prepend (sgroup->converters, vrate); sgroup->converters = g_list_prepend (sgroup->converters, vrate);
@ -1215,9 +1230,21 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
arate = gst_element_factory_make ("audiorate", NULL); arate = gst_element_factory_make ("audiorate", NULL);
g_object_set (arate, "tolerance", (guint64) ebin->tolerance, NULL); g_object_set (arate, "tolerance", (guint64) ebin->tolerance, NULL);
if (!arate) {
missing_element_name = "audiorate";
goto missing_element;
}
aconv = gst_element_factory_make ("audioconvert", NULL); aconv = gst_element_factory_make ("audioconvert", NULL);
aconv2 = gst_element_factory_make ("audioconvert", NULL); aconv2 = gst_element_factory_make ("audioconvert", NULL);
ares = gst_element_factory_make ("audioresample", NULL); ares = gst_element_factory_make ("audioresample", NULL);
if (!aconv || !aconv2) {
missing_element_name = "audioconvert";
goto missing_element;
}
if (!ares) {
missing_element_name = "audioresample";
goto missing_element;
}
gst_bin_add_many ((GstBin *) ebin, arate, aconv, ares, aconv2, NULL); gst_bin_add_many ((GstBin *) ebin, arate, aconv, ares, aconv2, NULL);
tosync = g_list_append (tosync, arate); tosync = g_list_append (tosync, arate);
@ -1288,6 +1315,15 @@ no_muxer_pad:
"Couldn't find a compatible muxer pad to link encoder to"); "Couldn't find a compatible muxer pad to link encoder to");
goto cleanup; goto cleanup;
missing_element:
gst_element_post_message (GST_ELEMENT_CAST (ebin),
gst_missing_element_message_new (GST_ELEMENT_CAST (ebin),
missing_element_name));
GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
missing_element_name), (NULL));
goto cleanup;
encoder_link_failure: encoder_link_failure:
GST_ERROR_OBJECT (ebin, "Failed to link the encoder"); GST_ERROR_OBJECT (ebin, "Failed to link the encoder");
goto cleanup; goto cleanup;

View file

@ -18,6 +18,8 @@ libgstplaybin_la_SOURCES = \
gststreaminfo.c \ gststreaminfo.c \
gststreamselector.c \ gststreamselector.c \
gstsubtitleoverlay.c \ gstsubtitleoverlay.c \
gstplaysinkvideoconvert.c \
gstplaysinkaudioconvert.c \
gststreamsynchronizer.c gststreamsynchronizer.c
nodist_libgstplaybin_la_SOURCES = $(built_sources) nodist_libgstplaybin_la_SOURCES = $(built_sources)
@ -57,6 +59,8 @@ noinst_HEADERS = \
gststreamselector.h \ gststreamselector.h \
gstrawcaps.h \ gstrawcaps.h \
gstsubtitleoverlay.h \ gstsubtitleoverlay.h \
gstplaysinkvideoconvert.h \
gstplaysinkaudioconvert.h \
gststreamsynchronizer.h gststreamsynchronizer.h
BUILT_SOURCES = $(built_headers) $(built_sources) BUILT_SOURCES = $(built_headers) $(built_sources)

View file

@ -170,6 +170,8 @@ struct _GstDecodeBin
GList *blocked_pads; /* pads that have set to block */ GList *blocked_pads; /* pads that have set to block */
gboolean expose_allstreams; /* Whether to expose unknow type streams or not */ gboolean expose_allstreams; /* Whether to expose unknow type streams or not */
gboolean upstream_seekable; /* if upstream is seekable */
}; };
struct _GstDecodeBinClass struct _GstDecodeBinClass
@ -216,9 +218,10 @@ enum
/* automatic sizes, while prerolling we buffer up to 2MB, we ignore time /* automatic sizes, while prerolling we buffer up to 2MB, we ignore time
* and buffers in this case. */ * and buffers in this case. */
#define AUTO_PREROLL_SIZE_BYTES 2 * 1024 * 1024 #define AUTO_PREROLL_SIZE_BYTES 2 * 1024 * 1024
#define AUTO_PREROLL_SIZE_BUFFERS 0 #define AUTO_PREROLL_SIZE_BUFFERS 0
#define AUTO_PREROLL_SIZE_TIME 0 #define AUTO_PREROLL_NOT_SEEKABLE_SIZE_TIME 0
#define AUTO_PREROLL_SEEKABLE_SIZE_TIME 10 * GST_SECOND
/* whan playing, keep a max of 2MB of data but try to keep the number of buffers /* whan playing, keep a max of 2MB of data but try to keep the number of buffers
* as low as possible (try to aim for 5 buffers) */ * as low as possible (try to aim for 5 buffers) */
@ -2042,6 +2045,48 @@ beach:
return; return;
} }
/* check_upstream_seekable:
*
* Check if upstream is seekable.
*/
static void
check_upstream_seekable (GstDecodeBin * dbin, GstPad * pad)
{
GstQuery *query;
gint64 start = -1, stop = -1;
dbin->upstream_seekable = FALSE;
query = gst_query_new_seeking (GST_FORMAT_BYTES);
if (!gst_pad_peer_query (pad, query)) {
GST_DEBUG_OBJECT (dbin, "seeking query failed");
gst_query_unref (query);
return;
}
gst_query_parse_seeking (query, NULL, &dbin->upstream_seekable,
&start, &stop);
gst_query_unref (query);
/* try harder to query upstream size if we didn't get it the first time */
if (dbin->upstream_seekable && stop == -1) {
GstFormat fmt = GST_FORMAT_BYTES;
GST_DEBUG_OBJECT (dbin, "doing duration query to fix up unset stop");
gst_pad_query_peer_duration (pad, &fmt, &stop);
}
/* if upstream doesn't know the size, it's likely that it's not seekable in
* practice even if it technically may be seekable */
if (dbin->upstream_seekable && (start != 0 || stop <= start)) {
GST_DEBUG_OBJECT (dbin, "seekable but unknown start/stop -> disable");
dbin->upstream_seekable = FALSE;
}
GST_DEBUG_OBJECT (dbin, "upstream seekable: %d", dbin->upstream_seekable);
}
static void static void
type_found (GstElement * typefind, guint probability, type_found (GstElement * typefind, guint probability,
GstCaps * caps, GstDecodeBin * decode_bin) GstCaps * caps, GstDecodeBin * decode_bin)
@ -2070,6 +2115,10 @@ type_found (GstElement * typefind, guint probability,
pad = gst_element_get_static_pad (typefind, "src"); pad = gst_element_get_static_pad (typefind, "src");
sink_pad = gst_element_get_static_pad (typefind, "sink"); sink_pad = gst_element_get_static_pad (typefind, "sink");
/* if upstream is seekable we can safely set a limit in time to the queues so
* that streams at low bitrates can preroll */
check_upstream_seekable (decode_bin, sink_pad);
/* need some lock here to prevent race with shutdown state change /* need some lock here to prevent race with shutdown state change
* which might yank away e.g. decode_chain while building stuff here. * which might yank away e.g. decode_chain while building stuff here.
* In typical cases, STREAM_LOCK is held and handles that, it need not * In typical cases, STREAM_LOCK is held and handles that, it need not
@ -2653,7 +2702,8 @@ decodebin_set_queue_size (GstDecodeBin * dbin, GstElement * multiqueue,
if ((max_buffers = dbin->max_size_buffers) == 0) if ((max_buffers = dbin->max_size_buffers) == 0)
max_buffers = AUTO_PREROLL_SIZE_BUFFERS; max_buffers = AUTO_PREROLL_SIZE_BUFFERS;
if ((max_time = dbin->max_size_time) == 0) if ((max_time = dbin->max_size_time) == 0)
max_time = AUTO_PREROLL_SIZE_TIME; max_time = dbin->upstream_seekable ? AUTO_PREROLL_SEEKABLE_SIZE_TIME :
AUTO_PREROLL_NOT_SEEKABLE_SIZE_TIME;
} else { } else {
/* update runtime limits. At runtime, we try to keep the amount of buffers /* update runtime limits. At runtime, we try to keep the amount of buffers
* in the queues as low as possible (but at least 5 buffers). */ * in the queues as low as possible (but at least 5 buffers). */

View file

@ -1,5 +1,6 @@
/* GStreamer /* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -271,6 +272,14 @@ struct _GstSourceSelect
#define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group))) #define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
#define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group))) #define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
enum
{
PLAYBIN_STREAM_AUDIO = 0,
PLAYBIN_STREAM_VIDEO,
PLAYBIN_STREAM_TEXT,
PLAYBIN_STREAM_LAST
};
/* a structure to hold the objects for decoding a uri and the subtitle uri /* a structure to hold the objects for decoding a uri and the subtitle uri
*/ */
struct _GstSourceGroup struct _GstSourceGroup
@ -319,7 +328,7 @@ struct _GstSourceGroup
GList *stream_changed_pending; GList *stream_changed_pending;
/* selectors for different streams */ /* selectors for different streams */
GstSourceSelect selector[GST_PLAY_SINK_TYPE_LAST]; GstSourceSelect selector[PLAYBIN_STREAM_LAST];
}; };
#define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock) #define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock)
@ -1089,30 +1098,25 @@ init_group (GstPlayBin * playbin, GstSourceGroup * group)
/* If you add any items to these lists, check that media_list[] is defined /* If you add any items to these lists, check that media_list[] is defined
* above to be large enough to hold MAX(items)+1, so as to accomodate a * above to be large enough to hold MAX(items)+1, so as to accomodate a
* NULL terminator (set when the memory is zeroed on allocation) */ * NULL terminator (set when the memory is zeroed on allocation) */
group->selector[0].media_list[0] = "audio/x-raw-"; group->selector[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
group->selector[0].type = GST_PLAY_SINK_TYPE_AUDIO_RAW; group->selector[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
group->selector[0].channels = group->audio_channels; group->selector[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
group->selector[1].media_list[0] = "audio/"; group->selector[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
group->selector[1].type = GST_PLAY_SINK_TYPE_AUDIO; group->selector[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
group->selector[1].channels = group->audio_channels; group->selector[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
group->selector[2].media_list[0] = "text/"; group->selector[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
group->selector[2].media_list[1] = "application/x-subtitle"; group->selector[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
group->selector[2].media_list[2] = "application/x-ssa"; group->selector[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
group->selector[2].media_list[3] = "application/x-ass"; group->selector[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
group->selector[2].media_list[4] = "video/x-dvd-subpicture"; group->selector[PLAYBIN_STREAM_TEXT].media_list[4] = "video/x-dvd-subpicture";
group->selector[2].media_list[5] = "subpicture/"; group->selector[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
group->selector[2].media_list[6] = "subtitle/"; group->selector[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
group->selector[2].get_media_caps = gst_subtitle_overlay_create_factory_caps; group->selector[PLAYBIN_STREAM_TEXT].get_media_caps =
group->selector[2].type = GST_PLAY_SINK_TYPE_TEXT; gst_subtitle_overlay_create_factory_caps;
group->selector[2].channels = group->text_channels; group->selector[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
group->selector[3].media_list[0] = "video/x-raw-"; group->selector[PLAYBIN_STREAM_TEXT].channels = group->text_channels;
group->selector[3].type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
group->selector[3].channels = group->video_channels;
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++) { for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
GstSourceSelect *select = &group->selector[n]; GstSourceSelect *select = &group->selector[n];
select->sinkpad_delayed_event = NULL; select->sinkpad_delayed_event = NULL;
select->sinkpad_data_probe = 0; select->sinkpad_data_probe = 0;
@ -1124,7 +1128,7 @@ free_group (GstPlayBin * playbin, GstSourceGroup * group)
{ {
int n; int n;
for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) { for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
GstSourceSelect *select = &group->selector[n]; GstSourceSelect *select = &group->selector[n];
if (select->sinkpad && select->sinkpad_data_probe) if (select->sinkpad && select->sinkpad_data_probe)
gst_pad_remove_data_probe (select->sinkpad, select->sinkpad_data_probe); gst_pad_remove_data_probe (select->sinkpad, select->sinkpad_data_probe);
@ -1493,6 +1497,10 @@ gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
GstPad *sinkpad; GstPad *sinkpad;
GST_PLAY_BIN_LOCK (playbin); GST_PLAY_BIN_LOCK (playbin);
GST_DEBUG_OBJECT (playbin, "Changing current video stream %d -> %d",
playbin->current_video, stream);
group = get_group (playbin); group = get_group (playbin);
if (!(channels = group->video_channels)) if (!(channels = group->video_channels))
goto no_channels; goto no_channels;
@ -1536,6 +1544,10 @@ gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
GstPad *sinkpad; GstPad *sinkpad;
GST_PLAY_BIN_LOCK (playbin); GST_PLAY_BIN_LOCK (playbin);
GST_DEBUG_OBJECT (playbin, "Changing current audio stream %d -> %d",
playbin->current_audio, stream);
group = get_group (playbin); group = get_group (playbin);
if (!(channels = group->audio_channels)) if (!(channels = group->audio_channels))
goto no_channels; goto no_channels;
@ -1651,6 +1663,10 @@ gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
GstPad *sinkpad; GstPad *sinkpad;
GST_PLAY_BIN_LOCK (playbin); GST_PLAY_BIN_LOCK (playbin);
GST_DEBUG_OBJECT (playbin, "Changing current text stream %d -> %d",
playbin->current_text, stream);
group = get_group (playbin); group = get_group (playbin);
if (!(channels = group->text_channels)) if (!(channels = group->text_channels))
goto no_channels; goto no_channels;
@ -2313,7 +2329,7 @@ selector_active_pad_changed (GObject * selector, GParamSpec * pspec,
GST_PLAY_BIN_LOCK (playbin); GST_PLAY_BIN_LOCK (playbin);
group = get_group (playbin); group = get_group (playbin);
for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) { for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
if (selector == G_OBJECT (group->selector[i].selector)) { if (selector == G_OBJECT (group->selector[i].selector)) {
select = &group->selector[i]; select = &group->selector[i];
} }
@ -2399,7 +2415,7 @@ array_has_value (const gchar * values[], const gchar * value)
gint i; gint i;
for (i = 0; values[i]; i++) { for (i = 0; values[i]; i++) {
if (g_str_has_prefix (value, values[i])) if (values[i] && g_str_has_prefix (value, values[i]))
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
@ -2471,7 +2487,7 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
GST_DEBUG_PAD_NAME (pad), caps, group); GST_DEBUG_PAD_NAME (pad), caps, group);
/* major type of the pad, this determines the selector to use */ /* major type of the pad, this determines the selector to use */
for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) { for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
if (array_has_value (group->selector[i].media_list, name)) { if (array_has_value (group->selector[i].media_list, name)) {
select = &group->selector[i]; select = &group->selector[i];
break; break;
@ -2730,7 +2746,7 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown); GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
GST_SOURCE_GROUP_LOCK (group); GST_SOURCE_GROUP_LOCK (group);
for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) { for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i]; GstSourceSelect *select = &group->selector[i];
/* check if the specific media type was detected and thus has a selector /* check if the specific media type was detected and thus has a selector
@ -2793,14 +2809,10 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
GST_SOURCE_GROUP_UNLOCK (group); GST_SOURCE_GROUP_UNLOCK (group);
GST_LOG_OBJECT (playbin, "reconfigure sink");
/* we configure the modes if we were the last decodebin to complete. */
gst_play_sink_reconfigure (playbin->playsink);
/* signal the other decodebins that they can continue now. */ /* signal the other decodebins that they can continue now. */
GST_SOURCE_GROUP_LOCK (group); GST_SOURCE_GROUP_LOCK (group);
/* unblock all selectors */ /* unblock all selectors */
for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) { for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i]; GstSourceSelect *select = &group->selector[i];
/* All streamsynchronizer streams should see stream-changed message, /* All streamsynchronizer streams should see stream-changed message,
@ -2866,7 +2878,7 @@ shutdown:
* instead of a NOT_LINKED error. * instead of a NOT_LINKED error.
*/ */
GST_SOURCE_GROUP_LOCK (group); GST_SOURCE_GROUP_LOCK (group);
for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) { for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i]; GstSourceSelect *select = &group->selector[i];
if (select->srcpad) { if (select->srcpad) {
@ -3464,7 +3476,7 @@ deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
GST_SOURCE_GROUP_LOCK (group); GST_SOURCE_GROUP_LOCK (group);
group->active = FALSE; group->active = FALSE;
for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) { for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i]; GstSourceSelect *select = &group->selector[i];
GST_DEBUG_OBJECT (playbin, "unlinking selector %s", select->media_list[0]); GST_DEBUG_OBJECT (playbin, "unlinking selector %s", select->media_list[0]);

View file

@ -1,5 +1,6 @@
/* GStreamer /* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -30,6 +31,8 @@
#include "gstplaysink.h" #include "gstplaysink.h"
#include "gststreamsynchronizer.h" #include "gststreamsynchronizer.h"
#include "gstplaysinkvideoconvert.h"
#include "gstplaysinkaudioconvert.h"
GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug); GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
#define GST_CAT_DEFAULT gst_play_sink_debug #define GST_CAT_DEFAULT gst_play_sink_debug
@ -58,7 +61,6 @@ typedef struct
GstPad *sinkpad; GstPad *sinkpad;
GstElement *queue; GstElement *queue;
GstElement *conv; GstElement *conv;
GstElement *resample;
GstElement *volume; /* element with the volume property */ GstElement *volume; /* element with the volume property */
gboolean sink_volume; /* if the volume was provided by the sink */ gboolean sink_volume; /* if the volume was provided by the sink */
GstElement *mute; /* element with the mute property */ GstElement *mute; /* element with the mute property */
@ -80,7 +82,6 @@ typedef struct
GstPad *sinkpad; GstPad *sinkpad;
GstElement *queue; GstElement *queue;
GstElement *conv; GstElement *conv;
GstElement *scale;
GstElement *sink; GstElement *sink;
gboolean async; gboolean async;
GstElement *ts_offset; GstElement *ts_offset;
@ -149,6 +150,7 @@ struct _GstPlaySink
/* audio */ /* audio */
GstPad *audio_pad; GstPad *audio_pad;
gboolean audio_pad_raw; gboolean audio_pad_raw;
gboolean audio_pad_blocked;
GstPad *audio_srcpad_stream_synchronizer; GstPad *audio_srcpad_stream_synchronizer;
GstPad *audio_sinkpad_stream_synchronizer; GstPad *audio_sinkpad_stream_synchronizer;
/* audio tee */ /* audio tee */
@ -159,10 +161,12 @@ struct _GstPlaySink
/* video */ /* video */
GstPad *video_pad; GstPad *video_pad;
gboolean video_pad_raw; gboolean video_pad_raw;
gboolean video_pad_blocked;
GstPad *video_srcpad_stream_synchronizer; GstPad *video_srcpad_stream_synchronizer;
GstPad *video_sinkpad_stream_synchronizer; GstPad *video_sinkpad_stream_synchronizer;
/* text */ /* text */
GstPad *text_pad; GstPad *text_pad;
gboolean text_pad_blocked;
GstPad *text_srcpad_stream_synchronizer; GstPad *text_srcpad_stream_synchronizer;
GstPad *text_sinkpad_stream_synchronizer; GstPad *text_sinkpad_stream_synchronizer;
@ -191,21 +195,12 @@ struct _GstPlaySinkClass
GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps); GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
}; };
static GstStaticPadTemplate audiorawtemplate =
GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate audiotemplate = static GstStaticPadTemplate audiotemplate =
GST_STATIC_PAD_TEMPLATE ("audio_sink", GST_STATIC_PAD_TEMPLATE ("audio_sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_REQUEST, GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY); GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate videorawtemplate =
GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate videotemplate = static GstStaticPadTemplate videotemplate =
GST_STATIC_PAD_TEMPLATE ("video_sink", GST_STATIC_PAD_TEMPLATE ("video_sink",
GST_PAD_SINK, GST_PAD_SINK,
@ -216,6 +211,19 @@ static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
GST_PAD_REQUEST, GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY); GST_STATIC_CAPS_ANY);
/* FIXME 0.11: Remove */
static GstStaticPadTemplate audiorawtemplate =
GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate videorawtemplate =
GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
/* props */ /* props */
enum enum
{ {
@ -613,6 +621,7 @@ gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
GST_PLAY_SINK_LOCK (playsink); GST_PLAY_SINK_LOCK (playsink);
switch (type) { switch (type) {
case GST_PLAY_SINK_TYPE_AUDIO: case GST_PLAY_SINK_TYPE_AUDIO:
case GST_PLAY_SINK_TYPE_AUDIO_RAW:
{ {
GstPlayAudioChain *chain; GstPlayAudioChain *chain;
if ((chain = (GstPlayAudioChain *) playsink->audiochain)) if ((chain = (GstPlayAudioChain *) playsink->audiochain))
@ -621,6 +630,7 @@ gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
break; break;
} }
case GST_PLAY_SINK_TYPE_VIDEO: case GST_PLAY_SINK_TYPE_VIDEO:
case GST_PLAY_SINK_TYPE_VIDEO_RAW:
{ {
GstPlayVideoChain *chain; GstPlayVideoChain *chain;
if ((chain = (GstPlayVideoChain *) playsink->videochain)) if ((chain = (GstPlayVideoChain *) playsink->videochain))
@ -1084,7 +1094,7 @@ try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
} }
/* make the element (bin) that contains the elements needed to perform /* make the element (bin) that contains the elements needed to perform
* video display. * video display. Only used for *raw* video streams.
* *
* +------------------------------------------------------------+ * +------------------------------------------------------------+
* | vbin | * | vbin |
@ -1275,46 +1285,19 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
head = prev = chain->queue; head = prev = chain->queue;
} }
if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) { if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace"); GST_DEBUG_OBJECT (playsink, "creating videoconverter");
chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); chain->conv =
if (chain->conv == NULL) { g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
post_missing_element_message (playsink, "ffmpegcolorspace"); gst_bin_add (bin, chain->conv);
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, if (prev) {
(_("Missing element '%s' - check your GStreamer installation."), if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
"ffmpegcolorspace"), ("video rendering might fail")); GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else { } else {
gst_bin_add (bin, chain->conv); head = chain->conv;
if (prev) {
if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = chain->conv;
}
prev = chain->conv;
}
GST_DEBUG_OBJECT (playsink, "creating videoscale");
chain->scale = gst_element_factory_make ("videoscale", "vscale");
if (chain->scale == NULL) {
post_missing_element_message (playsink, "videoscale");
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"videoscale"), ("possibly a liboil version mismatch?"));
} else {
/* Add black borders if necessary to keep the DAR */
g_object_set (chain->scale, "add-borders", TRUE, NULL);
gst_bin_add (bin, chain->scale);
if (prev) {
if (!gst_element_link_pads_full (prev, "src", chain->scale, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = chain->scale;
}
prev = chain->scale;
} }
prev = chain->conv;
} }
if (prev) { if (prev) {
@ -1385,13 +1368,12 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
chain = playsink->videochain; chain = playsink->videochain;
chain->chain.raw = raw;
/* if the chain was active we don't do anything */ /* if the chain was active we don't do anything */
if (GST_PLAY_CHAIN (chain)->activated == TRUE) if (GST_PLAY_CHAIN (chain)->activated == TRUE)
return TRUE; return TRUE;
if (chain->chain.raw != raw)
return FALSE;
/* try to set the sink element to READY again */ /* try to set the sink element to READY again */
ret = gst_element_set_state (chain->sink, GST_STATE_READY); ret = gst_element_set_state (chain->sink, GST_STATE_READY);
if (ret == GST_STATE_CHANGE_FAILURE) if (ret == GST_STATE_CHANGE_FAILURE)
@ -1420,6 +1402,7 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
} }
/* make an element for playback of video with subtitles embedded. /* make an element for playback of video with subtitles embedded.
* Only used for *raw* video streams.
* *
* +--------------------------------------------+ * +--------------------------------------------+
* | tbin | * | tbin |
@ -1764,54 +1747,32 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
chain->sink_volume = FALSE; chain->sink_volume = FALSE;
} }
if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) { if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
&& playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
GST_DEBUG_OBJECT (playsink, "creating audioconvert"); GST_DEBUG_OBJECT (playsink, "creating audioconvert");
chain->conv = gst_element_factory_make ("audioconvert", "aconv"); chain->conv =
if (chain->conv == NULL) { g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
post_missing_element_message (playsink, "audioconvert"); gst_bin_add (bin, chain->conv);
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, if (prev) {
(_("Missing element '%s' - check your GStreamer installation."), if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
"audioconvert"), ("possibly a liboil version mismatch?")); GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else { } else {
gst_bin_add (bin, chain->conv); head = chain->conv;
if (prev) {
if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = chain->conv;
}
prev = chain->conv;
} }
prev = chain->conv;
GST_DEBUG_OBJECT (playsink, "creating audioresample"); GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
chain->resample = gst_element_factory_make ("audioresample", "aresample"); !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
if (chain->resample == NULL) { GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
post_missing_element_message (playsink, "audioresample"); && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"audioresample"), ("possibly a liboil version mismatch?"));
} else {
gst_bin_add (bin, chain->resample);
if (prev) {
if (!gst_element_link_pads_full (prev, "src", chain->resample, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = chain->resample;
}
prev = chain->resample;
}
if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) { if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
GST_DEBUG_OBJECT (playsink, "creating volume"); GstPlaySinkAudioConvert *conv =
chain->volume = gst_element_factory_make ("volume", "volume"); GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
if (chain->volume == NULL) {
post_missing_element_message (playsink, "volume"); if (conv->volume) {
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN, chain->volume = conv->volume;
(_("Missing element '%s' - check your GStreamer installation."),
"volume"), ("possibly a liboil version mismatch?"));
} else {
have_volume = TRUE; have_volume = TRUE;
g_signal_connect (chain->volume, "notify::volume", g_signal_connect (chain->volume, "notify::volume",
@ -1826,16 +1787,6 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
NULL); NULL);
g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL); g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
gst_bin_add (bin, chain->volume);
if (prev) {
if (!gst_element_link_pads_full (prev, "src", chain->volume, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = chain->volume;
}
prev = chain->volume;
} }
} }
} }
@ -1917,13 +1868,12 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
chain = playsink->audiochain; chain = playsink->audiochain;
chain->chain.raw = raw;
/* if the chain was active we don't do anything */ /* if the chain was active we don't do anything */
if (GST_PLAY_CHAIN (chain)->activated == TRUE) if (GST_PLAY_CHAIN (chain)->activated == TRUE)
return TRUE; return TRUE;
if (chain->chain.raw != raw)
return FALSE;
/* try to set the sink element to READY again */ /* try to set the sink element to READY again */
ret = gst_element_set_state (chain->sink, GST_STATE_READY); ret = gst_element_set_state (chain->sink, GST_STATE_READY);
if (ret == GST_STATE_CHANGE_FAILURE) if (ret == GST_STATE_CHANGE_FAILURE)
@ -1963,29 +1913,35 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
g_signal_connect (chain->mute, "notify::mute", g_signal_connect (chain->mute, "notify::mute",
G_CALLBACK (notify_mute_cb), playsink); G_CALLBACK (notify_mute_cb), playsink);
} }
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
} else { } else {
GstPlaySinkAudioConvert *conv =
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
/* no volume, we need to add a volume element when we can */ /* no volume, we need to add a volume element when we can */
conv->use_volume = TRUE;
GST_DEBUG_OBJECT (playsink, "the sink has no volume property"); GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
if (!raw) {
GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
disconnect_chain (chain, playsink); /* Disconnect signals */
chain->volume = NULL; disconnect_chain (chain, playsink);
chain->mute = NULL;
} else {
/* both last and current chain are raw audio, there should be a volume
* element already, unless the sink changed from one with a volume
* property to one that hasn't got a volume property, in which case we
* 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;
}
GST_DEBUG_OBJECT (playsink, "reusing existing volume element"); if (conv->volume) {
chain->volume = conv->volume;
chain->mute = chain->volume;
g_signal_connect (chain->volume, "notify::volume",
G_CALLBACK (notify_volume_cb), playsink);
g_signal_connect (chain->mute, "notify::mute",
G_CALLBACK (notify_mute_cb), playsink);
/* configure with the latest volume and mute */
g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
} }
GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
} }
return TRUE; return TRUE;
} }
@ -2864,6 +2820,196 @@ gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
return result; return result;
} }
static gboolean
is_raw_structure (GstStructure * s)
{
const gchar *name;
name = gst_structure_get_name (s);
if (g_str_has_prefix (name, "video/x-raw-") ||
g_str_has_prefix (name, "audio/x-raw-"))
return TRUE;
return FALSE;
}
static gboolean
is_raw_pad (GstPad * pad)
{
GstPad *peer = gst_pad_get_peer (pad);
GstCaps *caps;
gboolean raw = TRUE;
if (!peer)
return raw;
caps = gst_pad_get_negotiated_caps (peer);
if (!caps) {
guint i, n;
caps = gst_pad_get_caps_reffed (peer);
n = gst_caps_get_size (caps);
for (i = 0; i < n; i++) {
gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
if (i == 0) {
raw = r;
} else if (raw != r) {
GST_ERROR_OBJECT (pad,
"Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
raw = FALSE;
break;
}
}
} else {
raw = is_raw_structure (gst_caps_get_structure (caps, 0));
}
gst_caps_unref (caps);
gst_object_unref (peer);
return raw;
}
static void
sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
{
GstPlaySink *playsink = (GstPlaySink *) user_data;
GstPad *pad;
GST_PLAY_SINK_LOCK (playsink);
pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
if (pad == playsink->video_pad) {
playsink->video_pad_blocked = blocked;
GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
} else if (pad == playsink->audio_pad) {
playsink->audio_pad_blocked = blocked;
GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
} else if (pad == playsink->text_pad) {
playsink->text_pad_blocked = blocked;
GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
}
if (!blocked) {
gst_object_unref (pad);
GST_PLAY_SINK_UNLOCK (playsink);
return;
}
if ((!playsink->video_pad || playsink->video_pad_blocked) &&
(!playsink->audio_pad || playsink->audio_pad_blocked) &&
(!playsink->text_pad || playsink->text_pad_blocked)) {
GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
if (playsink->video_pad) {
playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
playsink->video_pad_raw);
}
if (playsink->audio_pad) {
playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
playsink->audio_pad_raw);
}
gst_play_sink_reconfigure (playsink);
if (playsink->video_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->video_pad)));
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (opad);
}
if (playsink->audio_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->audio_pad)));
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (opad);
}
if (playsink->text_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->text_pad)));
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (opad);
}
}
gst_object_unref (pad);
GST_PLAY_SINK_UNLOCK (playsink);
}
static void
caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
{
gboolean reconfigure = FALSE;
GstCaps *caps;
gboolean raw;
g_object_get (pad, "caps", &caps, NULL);
if (!caps)
return;
if (pad == playsink->audio_pad) {
raw = is_raw_pad (pad);
reconfigure = (! !playsink->audio_pad_raw != ! !raw)
&& playsink->audiochain;
GST_DEBUG_OBJECT (pad,
"Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
reconfigure, caps);
} else if (pad == playsink->video_pad) {
raw = is_raw_pad (pad);
reconfigure = (! !playsink->video_pad_raw != ! !raw)
&& playsink->videochain;
GST_DEBUG_OBJECT (pad,
"Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
reconfigure, caps);
}
gst_caps_unref (caps);
if (reconfigure) {
GST_PLAY_SINK_LOCK (playsink);
if (playsink->video_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->video_pad)));
gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (opad);
}
if (playsink->audio_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->audio_pad)));
gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (opad);
}
if (playsink->text_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->text_pad)));
gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (opad);
}
GST_PLAY_SINK_UNLOCK (playsink);
}
}
/** /**
* gst_play_sink_request_pad * gst_play_sink_request_pad
* @playsink: a #GstPlaySink * @playsink: a #GstPlaySink
@ -2878,7 +3024,6 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
{ {
GstPad *res = NULL; GstPad *res = NULL;
gboolean created = FALSE; gboolean created = FALSE;
gboolean raw = FALSE;
gboolean activate = TRUE; gboolean activate = TRUE;
const gchar *pad_name = NULL; const gchar *pad_name = NULL;
@ -2887,11 +3032,8 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
GST_PLAY_SINK_LOCK (playsink); GST_PLAY_SINK_LOCK (playsink);
switch (type) { switch (type) {
case GST_PLAY_SINK_TYPE_AUDIO_RAW: case GST_PLAY_SINK_TYPE_AUDIO_RAW:
pad_name = "audio_raw_sink";
raw = TRUE;
case GST_PLAY_SINK_TYPE_AUDIO: case GST_PLAY_SINK_TYPE_AUDIO:
if (pad_name == NULL) pad_name = "audio_sink";
pad_name = "audio_sink";
if (!playsink->audio_tee) { if (!playsink->audio_tee) {
GST_LOG_OBJECT (playsink, "creating tee"); GST_LOG_OBJECT (playsink, "creating tee");
/* create tee when needed. This element will feed the audio sink chain /* create tee when needed. This element will feed the audio sink chain
@ -2917,24 +3059,25 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
GST_LOG_OBJECT (playsink, "ghosting tee sinkpad"); GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
playsink->audio_pad = playsink->audio_pad =
gst_ghost_pad_new (pad_name, playsink->audio_tee_sink); gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
G_CALLBACK (caps_notify_cb), playsink);
created = TRUE; created = TRUE;
} }
playsink->audio_pad_raw = raw; playsink->audio_pad_raw = FALSE;
res = playsink->audio_pad; res = playsink->audio_pad;
break; break;
case GST_PLAY_SINK_TYPE_VIDEO_RAW: case GST_PLAY_SINK_TYPE_VIDEO_RAW:
pad_name = "video_raw_sink";
raw = TRUE;
case GST_PLAY_SINK_TYPE_VIDEO: case GST_PLAY_SINK_TYPE_VIDEO:
if (pad_name == NULL) pad_name = "video_sink";
pad_name = "video_sink";
if (!playsink->video_pad) { if (!playsink->video_pad) {
GST_LOG_OBJECT (playsink, "ghosting videosink"); GST_LOG_OBJECT (playsink, "ghosting videosink");
playsink->video_pad = playsink->video_pad =
gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK); gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
G_CALLBACK (caps_notify_cb), playsink);
created = TRUE; created = TRUE;
} }
playsink->video_pad_raw = raw; playsink->video_pad_raw = FALSE;
res = playsink->video_pad; res = playsink->video_pad;
break; break;
case GST_PLAY_SINK_TYPE_TEXT: case GST_PLAY_SINK_TYPE_TEXT:
@ -2970,6 +3113,14 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
* element is 'running' */ * element is 'running' */
gst_pad_set_active (res, TRUE); gst_pad_set_active (res, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (playsink), res); gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
GstPad *blockpad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (blockpad);
}
if (!activate) if (!activate)
gst_pad_set_active (res, activate); gst_pad_set_active (res, activate);
} }
@ -3026,8 +3177,12 @@ gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
GST_PLAY_SINK_LOCK (playsink); GST_PLAY_SINK_LOCK (playsink);
if (pad == playsink->video_pad) { if (pad == playsink->video_pad) {
res = &playsink->video_pad; res = &playsink->video_pad;
g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
playsink);
} else if (pad == playsink->audio_pad) { } else if (pad == playsink->audio_pad) {
res = &playsink->audio_pad; res = &playsink->audio_pad;
g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
playsink);
} else if (pad == playsink->text_pad) { } else if (pad == playsink->text_pad) {
res = &playsink->text_pad; res = &playsink->text_pad;
} else { } else {
@ -3208,6 +3363,46 @@ gst_play_sink_change_state (GstElement * element, GstStateChange transition)
ret = GST_STATE_CHANGE_ASYNC; ret = GST_STATE_CHANGE_ASYNC;
break; break;
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
/* unblock all pads here */
GST_PLAY_SINK_LOCK (playsink);
if (playsink->video_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->video_pad)));
if (gst_pad_is_blocked (opad)) {
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
}
gst_object_unref (opad);
playsink->video_pad_blocked = FALSE;
}
if (playsink->audio_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->audio_pad)));
if (gst_pad_is_blocked (opad)) {
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
}
gst_object_unref (opad);
playsink->audio_pad_blocked = FALSE;
}
if (playsink->text_pad) {
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->text_pad)));
if (gst_pad_is_blocked (opad)) {
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
}
gst_object_unref (opad);
playsink->text_pad_blocked = FALSE;
}
GST_PLAY_SINK_UNLOCK (playsink);
/* fall through */
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
if (playsink->audiochain && playsink->audiochain->sink_volume) { if (playsink->audiochain && playsink->audiochain->sink_volume) {
/* remove our links to the mute and volume elements when they were /* remove our links to the mute and volume elements when they were

View file

@ -41,11 +41,11 @@ G_BEGIN_DECLS
/** /**
* GstPlaySinkType: * GstPlaySinkType:
* @GST_PLAY_SINK_TYPE_AUDIO: A non-raw audio pad * @GST_PLAY_SINK_TYPE_AUDIO: an audio pad
* @GST_PLAY_SINK_TYPE_AUDIO_RAW: a raw audio pad * @GST_PLAY_SINK_TYPE_AUDIO_RAW: a raw audio pad. Deprecated.
* @GST_PLAY_SINK_TYPE_VIDEO: a non-raw video pad * @GST_PLAY_SINK_TYPE_VIDEO: a video pad
* @GST_PLAY_SINK_TYPE_VIDEO_RAW: a raw video pad * @GST_PLAY_SINK_TYPE_VIDEO_RAW: a raw video pad. Deprecated.
* @GST_PLAY_SINK_TYPE_TEXT: a raw text pad * @GST_PLAY_SINK_TYPE_TEXT: a text pad
* @GST_PLAY_SINK_TYPE_LAST: the last type * @GST_PLAY_SINK_TYPE_LAST: the last type
* @GST_PLAY_SINK_TYPE_FLUSHING: a flushing pad, used when shutting down * @GST_PLAY_SINK_TYPE_FLUSHING: a flushing pad, used when shutting down
* *

View file

@ -0,0 +1,516 @@
/* GStreamer
* Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstplaysinkaudioconvert.h"
#include <gst/pbutils/pbutils.h>
#include <gst/gst-i18n-plugin.h>
GST_DEBUG_CATEGORY_STATIC (gst_play_sink_audio_convert_debug);
#define GST_CAT_DEFAULT gst_play_sink_audio_convert_debug
#define parent_class gst_play_sink_audio_convert_parent_class
G_DEFINE_TYPE (GstPlaySinkAudioConvert, gst_play_sink_audio_convert,
GST_TYPE_BIN);
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static gboolean
is_raw_caps (GstCaps * caps)
{
gint i, n;
GstStructure *s;
const gchar *name;
n = gst_caps_get_size (caps);
for (i = 0; i < n; i++) {
s = gst_caps_get_structure (caps, i);
name = gst_structure_get_name (s);
if (!g_str_has_prefix (name, "audio/x-raw"))
return FALSE;
}
return TRUE;
}
static void
post_missing_element_message (GstPlaySinkAudioConvert * self,
const gchar * name)
{
GstMessage *msg;
msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
gst_element_post_message (GST_ELEMENT_CAST (self), msg);
}
static void
distribute_running_time (GstElement * element, const GstSegment * segment)
{
GstEvent *event;
GstPad *pad;
pad = gst_element_get_static_pad (element, "sink");
if (segment->accum) {
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format, 0, segment->accum, 0);
gst_pad_push_event (pad, event);
}
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format,
segment->start, segment->stop, segment->time);
gst_pad_push_event (pad, event);
gst_object_unref (pad);
}
static void
pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self)
{
GstPad *peer;
GstCaps *caps;
gboolean raw;
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
self->sink_proxypad_blocked = blocked;
GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
if (!blocked)
goto done;
/* There must be a peer at this point */
peer = gst_pad_get_peer (self->sinkpad);
caps = gst_pad_get_negotiated_caps (peer);
if (!caps)
caps = gst_pad_get_caps_reffed (peer);
gst_object_unref (peer);
raw = is_raw_caps (caps);
GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
gst_caps_unref (caps);
if (raw == self->raw)
goto unblock;
self->raw = raw;
if (raw) {
GstBin *bin = GST_BIN_CAST (self);
GstElement *head = NULL, *prev = NULL;
GstPad *pad;
GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
if (self->use_converters) {
self->conv = gst_element_factory_make ("audioconvert", "conv");
if (self->conv == NULL) {
post_missing_element_message (self, "audioconvert");
GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"audioconvert"), ("audio rendering might fail"));
} else {
gst_bin_add (bin, self->conv);
gst_element_sync_state_with_parent (self->conv);
distribute_running_time (self->conv, &self->segment);
prev = head = self->conv;
}
self->resample = gst_element_factory_make ("audioresample", "resample");
if (self->resample == NULL) {
post_missing_element_message (self, "audioresample");
GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"audioresample"), ("possibly a liboil version mismatch?"));
} else {
gst_bin_add (bin, self->resample);
gst_element_sync_state_with_parent (self->resample);
distribute_running_time (self->resample, &self->segment);
if (prev) {
if (!gst_element_link_pads_full (prev, "src", self->resample, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = self->resample;
}
prev = self->resample;
}
}
if (self->use_volume && self->volume) {
gst_bin_add (bin, gst_object_ref (self->volume));
gst_element_sync_state_with_parent (self->volume);
distribute_running_time (self->volume, &self->segment);
if (prev) {
if (!gst_element_link_pads_full (prev, "src", self->volume, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = self->volume;
}
prev = self->volume;
}
if (head) {
pad = gst_element_get_static_pad (head, "sink");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
gst_object_unref (pad);
}
if (prev) {
pad = gst_element_get_static_pad (prev, "src");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
gst_object_unref (pad);
}
if (!head && !prev) {
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
}
GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
} else {
GstBin *bin = GST_BIN_CAST (self);
GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
if (self->conv) {
gst_element_set_state (self->conv, GST_STATE_NULL);
gst_bin_remove (bin, self->conv);
self->conv = NULL;
}
if (self->resample) {
gst_element_set_state (self->resample, GST_STATE_NULL);
gst_bin_remove (bin, self->resample);
self->resample = NULL;
}
if (self->volume) {
gst_element_set_state (self->volume, GST_STATE_NULL);
if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) {
gst_bin_remove (GST_BIN_CAST (self), self->volume);
}
}
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
}
unblock:
gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
done:
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
return;
link_failed:
{
GST_ELEMENT_ERROR (self, CORE, PAD,
(NULL), ("Failed to configure the audio converter."));
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
return;
}
}
static gboolean
gst_play_sink_audio_convert_sink_event (GstPad * pad, GstEvent * event)
{
GstPlaySinkAudioConvert *self =
GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
gboolean ret;
ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
gboolean update;
gdouble rate, applied_rate;
GstFormat format;
gint64 start, stop, position;
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
&format, &start, &stop, &position);
GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
&self->segment);
gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate,
format, start, stop, position);
GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT,
&self->segment);
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
} else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
GST_DEBUG_OBJECT (self, "Resetting segment");
gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
}
gst_event_unref (event);
gst_object_unref (self);
return ret;
}
static gboolean
gst_play_sink_audio_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstPlaySinkAudioConvert *self =
GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
gboolean ret;
GstStructure *s;
const gchar *name;
gboolean reconfigure = FALSE;
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
s = gst_caps_get_structure (caps, 0);
name = gst_structure_get_name (s);
if (g_str_has_prefix (name, "audio/x-raw-")) {
if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
reconfigure = TRUE;
gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
}
} else {
if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
reconfigure = TRUE;
gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
}
}
/* Otherwise the setcaps below fails */
if (reconfigure) {
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
}
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
ret = gst_ghost_pad_setcaps_default (pad, caps);
GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
ret);
gst_object_unref (self);
return ret;
}
static GstCaps *
gst_play_sink_audio_convert_getcaps (GstPad * pad)
{
GstPlaySinkAudioConvert *self =
GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
GstCaps *ret;
GstPad *otherpad, *peer;
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
if (pad == self->srcpad)
otherpad = gst_object_ref (self->sinkpad);
else
otherpad = gst_object_ref (self->srcpad);
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
peer = gst_pad_get_peer (otherpad);
if (peer) {
ret = gst_pad_get_caps_reffed (peer);
gst_object_unref (peer);
} else {
ret = gst_caps_new_any ();
}
gst_object_unref (otherpad);
gst_object_unref (self);
return ret;
}
static void
gst_play_sink_audio_convert_finalize (GObject * object)
{
GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object);
if (self->volume)
gst_object_unref (self->volume);
gst_object_unref (self->sink_proxypad);
g_mutex_free (self->lock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstStateChangeReturn
gst_play_sink_audio_convert_change_state (GstElement * element,
GstStateChange transition)
{
GstStateChangeReturn ret;
GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (element);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
if (gst_pad_is_blocked (self->sink_proxypad))
gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
if (self->conv) {
gst_element_set_state (self->conv, GST_STATE_NULL);
gst_bin_remove (GST_BIN_CAST (self), self->conv);
self->conv = NULL;
}
if (self->resample) {
gst_element_set_state (self->resample, GST_STATE_NULL);
gst_bin_remove (GST_BIN_CAST (self), self->resample);
self->resample = NULL;
}
if (self->volume) {
gst_element_set_state (self->volume, GST_STATE_NULL);
if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) {
gst_bin_remove (GST_BIN_CAST (self), self->volume);
}
}
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
self->raw = FALSE;
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
if (!gst_pad_is_blocked (self->sink_proxypad))
gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
default:
break;
}
return ret;
}
static void
gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GST_DEBUG_CATEGORY_INIT (gst_play_sink_audio_convert_debug,
"playsinkaudioconvert", 0, "play bin");
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_play_sink_audio_convert_finalize;
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&srctemplate));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sinktemplate));
gst_element_class_set_details_simple (gstelement_class,
"Player Sink Audio Converter", "Audio/Bin/Converter",
"Convenience bin for audio conversion",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_change_state);
}
static void
gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self)
{
GstPadTemplate *templ;
self->lock = g_mutex_new ();
gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
templ = gst_static_pad_template_get (&sinktemplate);
self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
gst_pad_set_event_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_event));
gst_pad_set_setcaps_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_setcaps));
gst_pad_set_getcaps_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps));
self->sink_proxypad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad)));
gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
gst_object_unref (templ);
templ = gst_static_pad_template_get (&srctemplate);
self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
gst_pad_set_getcaps_function (self->srcpad,
GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps));
gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
gst_object_unref (templ);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
/* FIXME: Only create this on demand but for now we need
* it to always exist because of playsink's volume proxying
* logic.
*/
self->volume = gst_element_factory_make ("volume", "volume");
if (self->volume)
gst_object_ref_sink (self->volume);
}

View file

@ -0,0 +1,89 @@
/* GStreamer
* Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/gst.h>
#ifndef __GST_PLAY_SINK_AUDIO_CONVERT_H__
#define __GST_PLAY_SINK_AUDIO_CONVERT_H__
G_BEGIN_DECLS
#define GST_TYPE_PLAY_SINK_AUDIO_CONVERT \
(gst_play_sink_audio_convert_get_type())
#define GST_PLAY_SINK_AUDIO_CONVERT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY_SINK_AUDIO_CONVERT, GstPlaySinkAudioConvert))
#define GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj) \
((GstPlaySinkAudioConvert *) obj)
#define GST_PLAY_SINK_AUDIO_CONVERT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY_SINK_AUDIO_CONVERT, GstPlaySinkAudioConvertClass))
#define GST_IS_PLAY_SINK_AUDIO_CONVERT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY_SINK_AUDIO_CONVERT))
#define GST_IS_PLAY_SINK_AUDIO_CONVERT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_AUDIO_CONVERT))
#define GST_PLAY_SINK_AUDIO_CONVERT_LOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"locking from thread %p", \
g_thread_self ()); \
g_mutex_lock (GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj)->lock); \
GST_LOG_OBJECT (obj, \
"locked from thread %p", \
g_thread_self ()); \
} G_STMT_END
#define GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"unlocking from thread %p", \
g_thread_self ()); \
g_mutex_unlock (GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj)->lock); \
} G_STMT_END
typedef struct _GstPlaySinkAudioConvert GstPlaySinkAudioConvert;
typedef struct _GstPlaySinkAudioConvertClass GstPlaySinkAudioConvertClass;
struct _GstPlaySinkAudioConvert
{
GstBin parent;
/* < private > */
GMutex *lock;
GstPad *sinkpad, *sink_proxypad;
gboolean sink_proxypad_blocked;
GstSegment segment;
GstPad *srcpad;
gboolean raw;
GstElement *conv, *resample;
/* < pseudo public > */
GstElement *volume;
gboolean use_volume;
gboolean use_converters;
};
struct _GstPlaySinkAudioConvertClass
{
GstBinClass parent;
};
GType gst_play_sink_audio_convert_get_type (void);
G_END_DECLS
#endif /* __GST_PLAY_SINK_AUDIO_CONVERT_H__ */

View file

@ -0,0 +1,479 @@
/* GStreamer
* Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstplaysinkvideoconvert.h"
#include <gst/pbutils/pbutils.h>
#include <gst/gst-i18n-plugin.h>
GST_DEBUG_CATEGORY_STATIC (gst_play_sink_video_convert_debug);
#define GST_CAT_DEFAULT gst_play_sink_video_convert_debug
#define parent_class gst_play_sink_video_convert_parent_class
G_DEFINE_TYPE (GstPlaySinkVideoConvert, gst_play_sink_video_convert,
GST_TYPE_BIN);
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static gboolean
is_raw_caps (GstCaps * caps)
{
gint i, n;
GstStructure *s;
const gchar *name;
n = gst_caps_get_size (caps);
for (i = 0; i < n; i++) {
s = gst_caps_get_structure (caps, i);
name = gst_structure_get_name (s);
if (!g_str_has_prefix (name, "video/x-raw"))
return FALSE;
}
return TRUE;
}
static void
post_missing_element_message (GstPlaySinkVideoConvert * self,
const gchar * name)
{
GstMessage *msg;
msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
gst_element_post_message (GST_ELEMENT_CAST (self), msg);
}
static void
distribute_running_time (GstElement * element, const GstSegment * segment)
{
GstEvent *event;
GstPad *pad;
pad = gst_element_get_static_pad (element, "sink");
if (segment->accum) {
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format, 0, segment->accum, 0);
gst_pad_push_event (pad, event);
}
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format,
segment->start, segment->stop, segment->time);
gst_pad_push_event (pad, event);
gst_object_unref (pad);
}
static void
pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self)
{
GstPad *peer;
GstCaps *caps;
gboolean raw;
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
self->sink_proxypad_blocked = blocked;
GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
if (!blocked)
goto done;
/* There must be a peer at this point */
peer = gst_pad_get_peer (self->sinkpad);
caps = gst_pad_get_negotiated_caps (peer);
if (!caps)
caps = gst_pad_get_caps_reffed (peer);
gst_object_unref (peer);
raw = is_raw_caps (caps);
GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
gst_caps_unref (caps);
if (raw == self->raw)
goto unblock;
self->raw = raw;
if (raw) {
GstBin *bin = GST_BIN_CAST (self);
GstElement *head = NULL, *prev = NULL;
GstPad *pad;
GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
self->conv = gst_element_factory_make ("ffmpegcolorspace", "conv");
if (self->conv == NULL) {
post_missing_element_message (self, "ffmpegcolorspace");
GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"ffmpegcolorspace"), ("video rendering might fail"));
} else {
gst_bin_add (bin, self->conv);
gst_element_sync_state_with_parent (self->conv);
distribute_running_time (self->conv, &self->segment);
prev = head = self->conv;
}
self->scale = gst_element_factory_make ("videoscale", "scale");
if (self->scale == NULL) {
post_missing_element_message (self, "videoscale");
GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"videoscale"), ("possibly a liboil version mismatch?"));
} else {
/* Add black borders if necessary to keep the DAR */
g_object_set (self->scale, "add-borders", TRUE, NULL);
gst_bin_add (bin, self->scale);
gst_element_sync_state_with_parent (self->scale);
distribute_running_time (self->scale, &self->segment);
if (prev) {
if (!gst_element_link_pads_full (prev, "src", self->scale, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
} else {
head = self->scale;
}
prev = self->scale;
}
if (head) {
pad = gst_element_get_static_pad (head, "sink");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
gst_object_unref (pad);
}
if (prev) {
pad = gst_element_get_static_pad (prev, "src");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
gst_object_unref (pad);
}
if (!head && !prev) {
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
}
GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
} else {
GstBin *bin = GST_BIN_CAST (self);
GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
if (self->conv) {
gst_element_set_state (self->conv, GST_STATE_NULL);
gst_bin_remove (bin, self->conv);
self->conv = NULL;
}
if (self->scale) {
gst_element_set_state (self->scale, GST_STATE_NULL);
gst_bin_remove (bin, self->scale);
self->scale = NULL;
}
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
}
unblock:
gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
done:
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
return;
link_failed:
{
GST_ELEMENT_ERROR (self, CORE, PAD,
(NULL), ("Failed to configure the video converter."));
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
return;
}
}
static gboolean
gst_play_sink_video_convert_sink_event (GstPad * pad, GstEvent * event)
{
GstPlaySinkVideoConvert *self =
GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
gboolean ret;
ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
gboolean update;
gdouble rate, applied_rate;
GstFormat format;
gint64 start, stop, position;
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
&format, &start, &stop, &position);
GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
&self->segment);
gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate,
format, start, stop, position);
GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT,
&self->segment);
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
} else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
GST_DEBUG_OBJECT (self, "Resetting segment");
gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
}
gst_event_unref (event);
gst_object_unref (self);
return ret;
}
static gboolean
gst_play_sink_video_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstPlaySinkVideoConvert *self =
GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
gboolean ret;
GstStructure *s;
const gchar *name;
gboolean reconfigure = FALSE;
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
s = gst_caps_get_structure (caps, 0);
name = gst_structure_get_name (s);
if (g_str_has_prefix (name, "video/x-raw-")) {
if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
reconfigure = TRUE;
gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
}
} else {
if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
reconfigure = TRUE;
gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
}
}
/* Otherwise the setcaps below fails */
if (reconfigure) {
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
}
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
ret = gst_ghost_pad_setcaps_default (pad, caps);
GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
ret);
gst_object_unref (self);
return ret;
}
static GstCaps *
gst_play_sink_video_convert_getcaps (GstPad * pad)
{
GstPlaySinkVideoConvert *self =
GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
GstCaps *ret;
GstPad *otherpad, *peer;
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
if (pad == self->srcpad)
otherpad = gst_object_ref (self->sinkpad);
else
otherpad = gst_object_ref (self->srcpad);
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
peer = gst_pad_get_peer (otherpad);
if (peer) {
ret = gst_pad_get_caps_reffed (peer);
gst_object_unref (peer);
} else {
ret = gst_caps_new_any ();
}
gst_object_unref (otherpad);
gst_object_unref (self);
return ret;
}
static void
gst_play_sink_video_convert_finalize (GObject * object)
{
GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object);
gst_object_unref (self->sink_proxypad);
g_mutex_free (self->lock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstStateChangeReturn
gst_play_sink_video_convert_change_state (GstElement * element,
GstStateChange transition)
{
GstStateChangeReturn ret;
GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (element);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
if (gst_pad_is_blocked (self->sink_proxypad))
gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
if (self->conv) {
gst_element_set_state (self->conv, GST_STATE_NULL);
gst_bin_remove (GST_BIN_CAST (self), self->conv);
self->conv = NULL;
}
if (self->scale) {
gst_element_set_state (self->scale, GST_STATE_NULL);
gst_bin_remove (GST_BIN_CAST (self), self->scale);
self->scale = NULL;
}
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
self->raw = FALSE;
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
if (!gst_pad_is_blocked (self->sink_proxypad))
gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
(GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
(GDestroyNotify) gst_object_unref);
GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
default:
break;
}
return ret;
}
static void
gst_play_sink_video_convert_class_init (GstPlaySinkVideoConvertClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GST_DEBUG_CATEGORY_INIT (gst_play_sink_video_convert_debug,
"playsinkvideoconvert", 0, "play bin");
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_play_sink_video_convert_finalize;
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&srctemplate));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sinktemplate));
gst_element_class_set_details_simple (gstelement_class,
"Player Sink Video Converter", "Video/Bin/Converter",
"Convenience bin for video conversion",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_change_state);
}
static void
gst_play_sink_video_convert_init (GstPlaySinkVideoConvert * self)
{
GstPadTemplate *templ;
self->lock = g_mutex_new ();
gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
templ = gst_static_pad_template_get (&sinktemplate);
self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
gst_pad_set_event_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_event));
gst_pad_set_setcaps_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_setcaps));
gst_pad_set_getcaps_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps));
self->sink_proxypad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad)));
gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
gst_object_unref (templ);
templ = gst_static_pad_template_get (&srctemplate);
self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
gst_pad_set_getcaps_function (self->srcpad,
GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps));
gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
gst_object_unref (templ);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
self->sink_proxypad);
}

View file

@ -0,0 +1,84 @@
/* GStreamer
* Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/gst.h>
#ifndef __GST_PLAY_SINK_VIDEO_CONVERT_H__
#define __GST_PLAY_SINK_VIDEO_CONVERT_H__
G_BEGIN_DECLS
#define GST_TYPE_PLAY_SINK_VIDEO_CONVERT \
(gst_play_sink_video_convert_get_type())
#define GST_PLAY_SINK_VIDEO_CONVERT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY_SINK_VIDEO_CONVERT, GstPlaySinkVideoConvert))
#define GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj) \
((GstPlaySinkVideoConvert *) obj)
#define GST_PLAY_SINK_VIDEO_CONVERT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY_SINK_VIDEO_CONVERT, GstPlaySinkVideoConvertClass))
#define GST_IS_PLAY_SINK_VIDEO_CONVERT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY_SINK_VIDEO_CONVERT))
#define GST_IS_PLAY_SINK_VIDEO_CONVERT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_VIDEO_CONVERT))
#define GST_PLAY_SINK_VIDEO_CONVERT_LOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"locking from thread %p", \
g_thread_self ()); \
g_mutex_lock (GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj)->lock); \
GST_LOG_OBJECT (obj, \
"locked from thread %p", \
g_thread_self ()); \
} G_STMT_END
#define GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"unlocking from thread %p", \
g_thread_self ()); \
g_mutex_unlock (GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj)->lock); \
} G_STMT_END
typedef struct _GstPlaySinkVideoConvert GstPlaySinkVideoConvert;
typedef struct _GstPlaySinkVideoConvertClass GstPlaySinkVideoConvertClass;
struct _GstPlaySinkVideoConvert
{
GstBin parent;
/* < private > */
GMutex *lock;
GstPad *sinkpad, *sink_proxypad;
gboolean sink_proxypad_blocked;
GstSegment segment;
GstPad *srcpad;
gboolean raw;
GstElement *conv, *scale;
};
struct _GstPlaySinkVideoConvertClass
{
GstBinClass parent;
};
GType gst_play_sink_video_convert_get_type (void);
G_END_DECLS
#endif /* __GST_PLAY_SINK_VIDEO_CONVERT_H__ */

View file

@ -1544,7 +1544,7 @@ gst_subtitle_overlay_src_proxy_chain (GstPad * proxypad, GstBuffer * buffer)
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
ret = self->src_proxy_chain (proxypad, buffer); ret = gst_proxy_pad_chain_default (proxypad, buffer);
if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) { if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) {
GST_ERROR_OBJECT (self, "Downstream chain error: %s", GST_ERROR_OBJECT (self, "Downstream chain error: %s",
@ -1581,7 +1581,7 @@ gst_subtitle_overlay_src_proxy_event (GstPad * proxypad, GstEvent * event)
event = NULL; event = NULL;
ret = TRUE; ret = TRUE;
} else { } else {
ret = self->src_proxy_event (proxypad, event); ret = gst_proxy_pad_event_default (proxypad, event);
event = NULL; event = NULL;
} }
@ -1619,7 +1619,7 @@ gst_subtitle_overlay_video_sink_setcaps (GstPad * pad, GstCaps * caps)
} }
GST_SUBTITLE_OVERLAY_UNLOCK (self); GST_SUBTITLE_OVERLAY_UNLOCK (self);
ret = self->video_sink_setcaps (pad, caps); ret = gst_ghost_pad_setcaps_default (pad, caps);
out: out:
gst_object_unref (self); gst_object_unref (self);
@ -1639,7 +1639,7 @@ gst_subtitle_overlay_video_sink_event (GstPad * pad, GstEvent * event)
self->fps_n = self->fps_d = 0; self->fps_n = self->fps_d = 0;
} }
ret = self->video_sink_event (pad, gst_event_ref (event)); ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
@ -1664,7 +1664,7 @@ static GstFlowReturn
gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstBuffer * buffer) gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstBuffer * buffer)
{ {
GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (GST_PAD_PARENT (pad)); GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (GST_PAD_PARENT (pad));
GstFlowReturn ret = self->video_sink_chain (pad, buffer); GstFlowReturn ret = gst_proxy_pad_chain_default (pad, buffer);
if (G_UNLIKELY (self->downstream_chain_error) || self->passthrough_identity) { if (G_UNLIKELY (self->downstream_chain_error) || self->passthrough_identity) {
return ret; return ret;
@ -1697,7 +1697,7 @@ gst_subtitle_overlay_subtitle_sink_chain (GstPad * pad, GstBuffer * buffer)
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
return GST_FLOW_OK; return GST_FLOW_OK;
} else { } else {
GstFlowReturn ret = self->subtitle_sink_chain (pad, buffer); GstFlowReturn ret = gst_proxy_pad_chain_default (pad, buffer);
if (IS_SUBTITLE_CHAIN_IGNORE_ERROR (ret)) { if (IS_SUBTITLE_CHAIN_IGNORE_ERROR (ret)) {
GST_DEBUG_OBJECT (self, "Subtitle chain error: %s", GST_DEBUG_OBJECT (self, "Subtitle chain error: %s",
@ -1772,7 +1772,7 @@ gst_subtitle_overlay_subtitle_sink_setcaps (GstPad * pad, GstCaps * caps)
if (target && gst_pad_accept_caps (target, caps)) { if (target && gst_pad_accept_caps (target, caps)) {
GST_DEBUG_OBJECT (pad, "Target accepts caps"); GST_DEBUG_OBJECT (pad, "Target accepts caps");
ret = self->subtitle_sink_setcaps (pad, caps); ret = gst_ghost_pad_setcaps_default (pad, caps);
GST_SUBTITLE_OVERLAY_UNLOCK (self); GST_SUBTITLE_OVERLAY_UNLOCK (self);
goto out; goto out;
} }
@ -1833,7 +1833,7 @@ gst_subtitle_overlay_subtitle_sink_link (GstPad * pad, GstPad * peer)
gst_caps_unref (caps); gst_caps_unref (caps);
} }
ret = self->subtitle_sink_link (pad, peer); ret = gst_ghost_pad_link_default (pad, peer);
gst_object_unref (self); gst_object_unref (self);
return ret; return ret;
@ -1852,7 +1852,7 @@ gst_subtitle_overlay_subtitle_sink_unlink (GstPad * pad)
GST_DEBUG_OBJECT (pad, "Pad unlinking"); GST_DEBUG_OBJECT (pad, "Pad unlinking");
gst_caps_replace (&self->subcaps, NULL); gst_caps_replace (&self->subcaps, NULL);
self->subtitle_sink_unlink (pad); gst_ghost_pad_unlink_default (pad);
GST_SUBTITLE_OVERLAY_LOCK (self); GST_SUBTITLE_OVERLAY_LOCK (self);
self->subtitle_error = FALSE; self->subtitle_error = FALSE;
@ -1924,7 +1924,7 @@ gst_subtitle_overlay_subtitle_sink_event (GstPad * pad, GstEvent * event)
break; break;
} }
ret = self->subtitle_sink_event (pad, gst_event_ref (event)); ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event); GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event);
@ -1943,8 +1943,6 @@ static void
gst_subtitle_overlay_init (GstSubtitleOverlay * self) gst_subtitle_overlay_init (GstSubtitleOverlay * self)
{ {
GstPadTemplate *templ; GstPadTemplate *templ;
GstIterator *it;
GValue item = { 0, };
GstPad *proxypad = NULL; GstPad *proxypad = NULL;
self->lock = g_mutex_new (); self->lock = g_mutex_new ();
@ -1952,68 +1950,45 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self)
templ = gst_static_pad_template_get (&srctemplate); templ = gst_static_pad_template_get (&srctemplate);
self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ); self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
it = gst_pad_iterate_internal_links (self->srcpad);
if (G_UNLIKELY (!it proxypad =
|| gst_iterator_next (it, &item) != GST_ITERATOR_OK GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->srcpad)));
|| ((proxypad = g_value_get_object (&item)) == NULL))) { gst_pad_set_event_function (proxypad,
GST_ERROR_OBJECT (self, "Failed to get proxypad of srcpad"); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
} else { gst_pad_set_chain_function (proxypad,
self->src_proxy_event = GST_PAD_EVENTFUNC (proxypad); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
gst_pad_set_event_function (proxypad, gst_object_unref (proxypad);
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
self->src_proxy_chain = GST_PAD_CHAINFUNC (proxypad);
gst_pad_set_chain_function (proxypad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
g_value_unset (&item);
}
if (it)
gst_iterator_free (it);
gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad); gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
templ = gst_static_pad_template_get (&video_sinktemplate); templ = gst_static_pad_template_get (&video_sinktemplate);
self->video_sinkpad = self->video_sinkpad =
gst_ghost_pad_new_no_target_from_template ("video_sink", templ); gst_ghost_pad_new_no_target_from_template ("video_sink", templ);
self->video_sink_event = GST_PAD_EVENTFUNC (self->video_sinkpad);
gst_pad_set_event_function (self->video_sinkpad, gst_pad_set_event_function (self->video_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event));
self->video_sink_setcaps = GST_PAD_SETCAPSFUNC (self->video_sinkpad);
gst_pad_set_setcaps_function (self->video_sinkpad, gst_pad_set_setcaps_function (self->video_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_setcaps)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_setcaps));
self->video_sink_chain = GST_PAD_CHAINFUNC (self->video_sinkpad);
gst_pad_set_chain_function (self->video_sinkpad, gst_pad_set_chain_function (self->video_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
proxypad = NULL; proxypad =
it = gst_pad_iterate_internal_links (self->video_sinkpad); GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
if (G_UNLIKELY (!it (self->video_sinkpad)));
|| gst_iterator_next (it, &item) != GST_ITERATOR_OK
|| ((proxypad = g_value_dup_object (&item)) == NULL))) {
GST_ERROR_OBJECT (self,
"Failed to get internally linked pad from video sinkpad");
}
if (it)
gst_iterator_free (it);
self->video_block_pad = proxypad; self->video_block_pad = proxypad;
g_value_unset (&item);
gst_element_add_pad (GST_ELEMENT_CAST (self), self->video_sinkpad); gst_element_add_pad (GST_ELEMENT_CAST (self), self->video_sinkpad);
templ = gst_static_pad_template_get (&subtitle_sinktemplate); templ = gst_static_pad_template_get (&subtitle_sinktemplate);
self->subtitle_sinkpad = self->subtitle_sinkpad =
gst_ghost_pad_new_no_target_from_template ("subtitle_sink", templ); gst_ghost_pad_new_no_target_from_template ("subtitle_sink", templ);
self->subtitle_sink_link = GST_PAD_LINKFUNC (self->subtitle_sinkpad);
gst_pad_set_link_function (self->subtitle_sinkpad, gst_pad_set_link_function (self->subtitle_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_link)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_link));
self->subtitle_sink_unlink = GST_PAD_UNLINKFUNC (self->subtitle_sinkpad);
gst_pad_set_unlink_function (self->subtitle_sinkpad, gst_pad_set_unlink_function (self->subtitle_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_unlink)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_unlink));
self->subtitle_sink_event = GST_PAD_EVENTFUNC (self->subtitle_sinkpad);
gst_pad_set_event_function (self->subtitle_sinkpad, gst_pad_set_event_function (self->subtitle_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_event)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_event));
self->subtitle_sink_setcaps = GST_PAD_SETCAPSFUNC (self->subtitle_sinkpad);
gst_pad_set_setcaps_function (self->subtitle_sinkpad, gst_pad_set_setcaps_function (self->subtitle_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_setcaps)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_setcaps));
self->subtitle_sink_chain = GST_PAD_CHAINFUNC (self->subtitle_sinkpad);
gst_pad_set_chain_function (self->subtitle_sinkpad, gst_pad_set_chain_function (self->subtitle_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_chain)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_chain));
gst_pad_set_getcaps_function (self->subtitle_sinkpad, gst_pad_set_getcaps_function (self->subtitle_sinkpad,
@ -2021,17 +1996,9 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self)
gst_pad_set_acceptcaps_function (self->subtitle_sinkpad, gst_pad_set_acceptcaps_function (self->subtitle_sinkpad,
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_acceptcaps)); GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_acceptcaps));
proxypad = NULL; proxypad =
it = gst_pad_iterate_internal_links (self->subtitle_sinkpad); GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
if (G_UNLIKELY (!it (self->subtitle_sinkpad)));
|| gst_iterator_next (it, &item) != GST_ITERATOR_OK
|| ((proxypad = g_value_dup_object (&item)) == NULL))) {
GST_ERROR_OBJECT (self,
"Failed to get internally linked pad from subtitle sinkpad");
}
if (it)
gst_iterator_free (it);
g_value_unset (&item);
self->subtitle_block_pad = proxypad; self->subtitle_block_pad = proxypad;
gst_element_add_pad (GST_ELEMENT_CAST (self), self->subtitle_sinkpad); gst_element_add_pad (GST_ELEMENT_CAST (self), self->subtitle_sinkpad);

View file

@ -68,26 +68,16 @@ struct _GstSubtitleOverlay
gboolean do_async; gboolean do_async;
GstPad *srcpad; GstPad *srcpad;
GstPadEventFunction src_proxy_event;
GstPadChainFunction src_proxy_chain;
gboolean downstream_chain_error; gboolean downstream_chain_error;
GstPad *video_sinkpad; GstPad *video_sinkpad;
GstPad *video_block_pad; GstPad *video_block_pad;
GstPadSetCapsFunction video_sink_setcaps;
GstPadEventFunction video_sink_event;
GstPadChainFunction video_sink_chain;
gboolean video_sink_blocked; gboolean video_sink_blocked;
GstSegment video_segment; GstSegment video_segment;
gint fps_n, fps_d; gint fps_n, fps_d;
GstPad *subtitle_sinkpad; GstPad *subtitle_sinkpad;
GstPad *subtitle_block_pad; GstPad *subtitle_block_pad;
GstPadLinkFunction subtitle_sink_link;
GstPadUnlinkFunction subtitle_sink_unlink;
GstPadEventFunction subtitle_sink_event;
GstPadChainFunction subtitle_sink_chain;
GstPadSetCapsFunction subtitle_sink_setcaps;
gboolean subtitle_sink_blocked; gboolean subtitle_sink_blocked;
GstSegment subtitle_segment; GstSegment subtitle_segment;
gboolean subtitle_flush; gboolean subtitle_flush;

View file

@ -57,6 +57,12 @@ typedef struct _GstURIDecodeBinClass GstURIDecodeBinClass;
#define GST_URI_DECODE_BIN_LOCK(dec) (g_mutex_lock(GST_URI_DECODE_BIN_GET_LOCK(dec))) #define GST_URI_DECODE_BIN_LOCK(dec) (g_mutex_lock(GST_URI_DECODE_BIN_GET_LOCK(dec)))
#define GST_URI_DECODE_BIN_UNLOCK(dec) (g_mutex_unlock(GST_URI_DECODE_BIN_GET_LOCK(dec))) #define GST_URI_DECODE_BIN_UNLOCK(dec) (g_mutex_unlock(GST_URI_DECODE_BIN_GET_LOCK(dec)))
typedef struct _GstURIDecodeBinStream
{
gulong probe_id;
guint bitrate;
} GstURIDecodeBinStream;
/** /**
* GstURIDecodeBin * GstURIDecodeBin
* *
@ -91,7 +97,7 @@ struct _GstURIDecodeBin
guint have_type_id; /* have-type signal id from typefind */ guint have_type_id; /* have-type signal id from typefind */
GSList *decodebins; GSList *decodebins;
GSList *pending_decodebins; GSList *pending_decodebins;
GSList *srcpads; GHashTable *streams;
gint numpads; gint numpads;
/* for dynamic sources */ /* for dynamic sources */
@ -930,7 +936,99 @@ source_no_more_pads (GstElement * element, GstURIDecodeBin * bin)
no_more_pads_full (element, FALSE, bin); no_more_pads_full (element, FALSE, bin);
} }
/* Called by the signal handlers when a decodebin has static void
configure_stream_buffering (GstURIDecodeBin * decoder)
{
GstElement *queue = NULL;
GHashTableIter iter;
gpointer key, value;
gint bitrate = 0;
/* automatic configuration enabled ? */
if (decoder->buffer_size != -1)
return;
GST_URI_DECODE_BIN_LOCK (decoder);
if (decoder->queue)
queue = gst_object_ref (decoder->queue);
g_hash_table_iter_init (&iter, decoder->streams);
while (g_hash_table_iter_next (&iter, &key, &value)) {
GstURIDecodeBinStream *stream = value;
if (stream->bitrate && bitrate >= 0)
bitrate += stream->bitrate;
else
bitrate = -1;
}
GST_URI_DECODE_BIN_UNLOCK (decoder);
GST_DEBUG_OBJECT (decoder, "overall bitrate %d", bitrate);
if (!queue)
return;
if (bitrate > 0) {
guint64 time;
guint bytes;
/* all streams have a bitrate;
* configure queue size based on queue duration using combined bitrate */
g_object_get (queue, "max-size-time", &time, NULL);
GST_DEBUG_OBJECT (decoder, "queue buffering time %" GST_TIME_FORMAT,
GST_TIME_ARGS (time));
if (time > 0) {
bytes = gst_util_uint64_scale (time, bitrate, 8 * GST_SECOND);
GST_DEBUG_OBJECT (decoder, "corresponds to buffer size %d", bytes);
g_object_set (queue, "max-size-bytes", bytes, NULL);
}
}
gst_object_unref (queue);
}
static gboolean
decoded_pad_event_probe (GstPad * pad, GstEvent * event,
GstURIDecodeBin * decoder)
{
GST_LOG_OBJECT (pad, "%s, decoder %p", GST_EVENT_TYPE_NAME (event), decoder);
/* look for a bitrate tag */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_TAG:
{
GstTagList *list;
guint bitrate = 0;
GstURIDecodeBinStream *stream;
gst_event_parse_tag (event, &list);
if (!gst_tag_list_get_uint_index (list, GST_TAG_NOMINAL_BITRATE, 0,
&bitrate)) {
gst_tag_list_get_uint_index (list, GST_TAG_BITRATE, 0, &bitrate);
}
GST_DEBUG_OBJECT (pad, "found bitrate %u", bitrate);
if (bitrate) {
GST_URI_DECODE_BIN_LOCK (decoder);
stream = g_hash_table_lookup (decoder->streams, pad);
GST_URI_DECODE_BIN_UNLOCK (decoder);
if (stream) {
stream->bitrate = bitrate;
/* no longer need this probe now */
gst_pad_remove_event_probe (pad, stream->probe_id);
/* configure buffer if possible */
configure_stream_buffering (decoder);
}
}
break;
}
default:
break;
}
/* never drop */
return TRUE;
}
/* Called by the signal handlers when a decodebin has
* found a new raw pad. * found a new raw pad.
*/ */
static void static void
@ -940,6 +1038,7 @@ new_decoded_pad_cb (GstElement * element, GstPad * pad, gboolean last,
GstPad *newpad; GstPad *newpad;
GstPadTemplate *pad_tmpl; GstPadTemplate *pad_tmpl;
gchar *padname; gchar *padname;
GstURIDecodeBinStream *stream;
GST_DEBUG_OBJECT (element, "new decoded pad, name: <%s>. Last: %d", GST_DEBUG_OBJECT (element, "new decoded pad, name: <%s>. Last: %d",
GST_PAD_NAME (pad), last); GST_PAD_NAME (pad), last);
@ -957,11 +1056,19 @@ new_decoded_pad_cb (GstElement * element, GstPad * pad, gboolean last,
/* store ref to the ghostpad so we can remove it */ /* store ref to the ghostpad so we can remove it */
g_object_set_data (G_OBJECT (pad), "uridecodebin.ghostpad", newpad); g_object_set_data (G_OBJECT (pad), "uridecodebin.ghostpad", newpad);
/* add event probe to monitor tags */
stream = g_slice_alloc0 (sizeof (GstURIDecodeBinStream));
stream->probe_id =
gst_pad_add_event_probe (pad, G_CALLBACK (decoded_pad_event_probe),
decoder);
GST_URI_DECODE_BIN_LOCK (decoder);
g_hash_table_insert (decoder->streams, pad, stream);
GST_URI_DECODE_BIN_UNLOCK (decoder);
gst_pad_set_active (newpad, TRUE); gst_pad_set_active (newpad, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (decoder), newpad); gst_element_add_pad (GST_ELEMENT_CAST (decoder), newpad);
} }
static gboolean static gboolean
source_pad_event_probe (GstPad * pad, GstEvent * event, source_pad_event_probe (GstPad * pad, GstEvent * event,
GstURIDecodeBin * decoder) GstURIDecodeBin * decoder)
@ -1422,22 +1529,6 @@ remove_decoders (GstURIDecodeBin * bin, gboolean force)
GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SOURCE); GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SOURCE);
} }
static void
remove_pads (GstURIDecodeBin * bin)
{
GSList *walk;
for (walk = bin->srcpads; walk; walk = g_slist_next (walk)) {
GstPad *pad = GST_PAD_CAST (walk->data);
GST_DEBUG_OBJECT (bin, "removing old pad");
gst_pad_set_active (pad, FALSE);
gst_element_remove_pad (GST_ELEMENT_CAST (bin), pad);
}
g_slist_free (bin->srcpads);
bin->srcpads = NULL;
}
static void static void
proxy_unknown_type_signal (GstElement * element, GstPad * pad, GstCaps * caps, proxy_unknown_type_signal (GstElement * element, GstPad * pad, GstCaps * caps,
GstURIDecodeBin * dec) GstURIDecodeBin * dec)
@ -1789,6 +1880,12 @@ could_not_link:
} }
} }
static void
free_stream (gpointer value)
{
g_slice_free (GstURIDecodeBinStream, value);
}
/* remove source and all related elements */ /* remove source and all related elements */
static void static void
remove_source (GstURIDecodeBin * bin) remove_source (GstURIDecodeBin * bin)
@ -1822,6 +1919,10 @@ remove_source (GstURIDecodeBin * bin)
gst_bin_remove (GST_BIN_CAST (bin), bin->typefind); gst_bin_remove (GST_BIN_CAST (bin), bin->typefind);
bin->typefind = NULL; bin->typefind = NULL;
} }
if (bin->streams) {
g_hash_table_destroy (bin->streams);
bin->streams = NULL;
}
/* Don't loose the SOURCE flag */ /* Don't loose the SOURCE flag */
GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SOURCE); GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SOURCE);
} }
@ -1916,6 +2017,9 @@ setup_source (GstURIDecodeBin * decoder)
/* remove the old decoders now, if any */ /* remove the old decoders now, if any */
remove_decoders (decoder, FALSE); remove_decoders (decoder, FALSE);
/* stream admin setup */
decoder->streams = g_hash_table_new_full (NULL, NULL, NULL, free_stream);
/* see if the source element emits raw audio/video all by itself, /* see if the source element emits raw audio/video all by itself,
* if so, we can create streams for the pads and be done with it. * if so, we can create streams for the pads and be done with it.
* Also check that is has source pads, if not, we assume it will * Also check that is has source pads, if not, we assume it will
@ -2408,14 +2512,12 @@ gst_uri_decode_bin_change_state (GstElement * element,
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG ("paused to ready"); GST_DEBUG ("paused to ready");
remove_decoders (decoder, FALSE); remove_decoders (decoder, FALSE);
remove_pads (decoder);
remove_source (decoder); remove_source (decoder);
do_async_done (decoder); do_async_done (decoder);
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
GST_DEBUG ("ready to null"); GST_DEBUG ("ready to null");
remove_decoders (decoder, TRUE); remove_decoders (decoder, TRUE);
remove_pads (decoder);
remove_source (decoder); remove_source (decoder);
break; break;
default: default:

View file

@ -86,6 +86,8 @@ enum
#define DEFAULT_SILENT TRUE #define DEFAULT_SILENT TRUE
#define DEFAULT_NEW_PREF 1.0 #define DEFAULT_NEW_PREF 1.0
#define DEFAULT_SKIP_TO_FIRST FALSE #define DEFAULT_SKIP_TO_FIRST FALSE
#define DEFAULT_DROP_ONLY FALSE
#define DEFAULT_AVERAGE_PERIOD 0
enum enum
{ {
@ -96,7 +98,9 @@ enum
ARG_DROP, ARG_DROP,
ARG_SILENT, ARG_SILENT,
ARG_NEW_PREF, ARG_NEW_PREF,
ARG_SKIP_TO_FIRST ARG_SKIP_TO_FIRST,
ARG_DROP_ONLY,
ARG_AVERAGE_PERIOD
/* FILL ME */ /* FILL ME */
}; };
@ -194,6 +198,33 @@ gst_video_rate_class_init (GstVideoRateClass * klass)
gst_element_class_add_pad_template (element_class, gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_video_rate_src_template)); gst_static_pad_template_get (&gst_video_rate_src_template));
/**
* GstVideoRate:drop-only:
*
* Only drop frames, no duplicates are produced.
*
* Since: 0.10.34
*/
g_object_class_install_property (object_class, ARG_DROP_ONLY,
g_param_spec_boolean ("drop-only", "Only Drop",
"Only drop frames, no duplicates are produced",
DEFAULT_DROP_ONLY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVideoRate:average-period:
*
* Arrange for maximum framerate by dropping frames beyond a certain framerate,
* where the framerate is calculated using a moving average over the
* configured.
*
* Since: 0.10.34
*/
g_object_class_install_property (object_class, ARG_AVERAGE_PERIOD,
g_param_spec_uint64 ("average-period", "Period over which to average",
"Period over which to average the framerate (in ns) (0 = disabled)",
0, G_MAXINT64, DEFAULT_AVERAGE_PERIOD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
element_class->change_state = GST_DEBUG_FUNCPTR (gst_video_rate_change_state); element_class->change_state = GST_DEBUG_FUNCPTR (gst_video_rate_change_state);
} }
@ -321,6 +352,8 @@ gst_video_rate_setcaps (GstPad * pad, GstCaps * caps)
videorate->out_frame_count = 0; videorate->out_frame_count = 0;
videorate->to_rate_numerator = rate_numerator; videorate->to_rate_numerator = rate_numerator;
videorate->to_rate_denominator = rate_denominator; videorate->to_rate_denominator = rate_denominator;
videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
rate_denominator, rate_numerator);
otherpad = videorate->sinkpad; otherpad = videorate->sinkpad;
} else { } else {
videorate->from_rate_numerator = rate_numerator; videorate->from_rate_numerator = rate_numerator;
@ -430,6 +463,7 @@ gst_video_rate_reset (GstVideoRate * videorate)
videorate->next_ts = GST_CLOCK_TIME_NONE; videorate->next_ts = GST_CLOCK_TIME_NONE;
videorate->last_ts = GST_CLOCK_TIME_NONE; videorate->last_ts = GST_CLOCK_TIME_NONE;
videorate->discont = TRUE; videorate->discont = TRUE;
videorate->average = 0;
gst_video_rate_swap_prev (videorate, NULL, 0); gst_video_rate_swap_prev (videorate, NULL, 0);
gst_segment_init (&videorate->segment, GST_FORMAT_TIME); gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
@ -463,6 +497,8 @@ gst_video_rate_init (GstVideoRate * videorate)
gst_video_rate_reset (videorate); gst_video_rate_reset (videorate);
videorate->silent = DEFAULT_SILENT; videorate->silent = DEFAULT_SILENT;
videorate->new_pref = DEFAULT_NEW_PREF; videorate->new_pref = DEFAULT_NEW_PREF;
videorate->drop_only = DEFAULT_DROP_ONLY;
videorate->average_period = DEFAULT_AVERAGE_PERIOD;
videorate->from_rate_numerator = 0; videorate->from_rate_numerator = 0;
videorate->from_rate_denominator = 0; videorate->from_rate_denominator = 0;
@ -513,8 +549,11 @@ gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate)
GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts; GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
} }
/* adapt for looping, bring back to time in current segment. */ /* We do not need to update time in VFR (variable frame rate) mode */
GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.base; if (!videorate->drop_only) {
/* adapt for looping, bring back to time in current segment. */
GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.accum;
}
GST_LOG_OBJECT (videorate, GST_LOG_OBJECT (videorate,
"old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT, "old is best, dup, pushing buffer outgoing ts %" GST_TIME_FORMAT,
@ -762,12 +801,69 @@ gst_video_rate_query (GstPad * pad, GstQuery ** query)
return res; return res;
} }
static GstFlowReturn
gst_video_rate_chain_max_avg (GstVideoRate * videorate, GstBuffer * buf)
{
GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
videorate->in++;
if (!GST_CLOCK_TIME_IS_VALID (ts) || videorate->wanted_diff == 0)
goto push;
/* drop frames if they exceed our output rate */
if (GST_CLOCK_TIME_IS_VALID (videorate->last_ts)) {
GstClockTimeDiff diff = ts - videorate->last_ts;
/* Drop buffer if its early compared to the desired frame rate and
* the current average is higher than the desired average
*/
if (diff < videorate->wanted_diff &&
videorate->average < videorate->wanted_diff)
goto drop;
/* Update average */
if (videorate->average) {
GstClockTimeDiff wanted_diff;
if (G_LIKELY (videorate->average_period > videorate->wanted_diff))
wanted_diff = videorate->wanted_diff;
else
wanted_diff = videorate->average_period * 10;
videorate->average =
gst_util_uint64_scale_round (videorate->average,
videorate->average_period - wanted_diff,
videorate->average_period) +
gst_util_uint64_scale_round (diff, wanted_diff,
videorate->average_period);
} else {
videorate->average = diff;
}
}
videorate->last_ts = ts;
push:
videorate->out++;
return gst_pad_push (videorate->srcpad, buf);
drop:
gst_buffer_unref (buf);
if (!videorate->silent)
gst_video_rate_notify_drop (videorate);
return GST_FLOW_OK;
}
static GstFlowReturn static GstFlowReturn
gst_video_rate_chain (GstPad * pad, GstBuffer * buffer) gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
{ {
GstVideoRate *videorate; GstVideoRate *videorate;
GstFlowReturn res = GST_FLOW_OK; GstFlowReturn res = GST_FLOW_OK;
GstClockTime intime, in_ts, in_dur; GstClockTime intime, in_ts, in_dur;
GstClockTime avg_period;
gboolean skip = FALSE;
videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad)); videorate = GST_VIDEO_RATE (GST_PAD_PARENT (pad));
@ -776,6 +872,29 @@ gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
videorate->to_rate_denominator == 0) videorate->to_rate_denominator == 0)
goto not_negotiated; goto not_negotiated;
GST_OBJECT_LOCK (videorate);
avg_period = videorate->average_period_set;
GST_OBJECT_UNLOCK (videorate);
/* MT-safe switching between modes */
if (G_UNLIKELY (avg_period != videorate->average_period)) {
videorate->average_period = avg_period;
videorate->last_ts = GST_CLOCK_TIME_NONE;
if (avg_period && !videorate->average) {
/* enabling average mode */
videorate->average = 0;
} else {
/* enable regular mode */
gst_video_rate_swap_prev (videorate, NULL, 0);
/* arrange for skip-to-first behaviour */
videorate->next_ts = GST_CLOCK_TIME_NONE;
skip = TRUE;
}
}
if (videorate->average_period > 0)
return gst_video_rate_chain_max_avg (videorate, buffer);
in_ts = GST_BUFFER_TIMESTAMP (buffer); in_ts = GST_BUFFER_TIMESTAMP (buffer);
in_dur = GST_BUFFER_DURATION (buffer); in_dur = GST_BUFFER_DURATION (buffer);
@ -805,7 +924,7 @@ gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) { if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) {
/* new buffer, we expect to output a buffer that matches the first /* new buffer, we expect to output a buffer that matches the first
* timestamp in the segment */ * timestamp in the segment */
if (videorate->skip_to_first) { if (videorate->skip_to_first || skip) {
videorate->next_ts = intime; videorate->next_ts = intime;
videorate->base_ts = in_ts - videorate->segment.start; videorate->base_ts = in_ts - videorate->segment.start;
videorate->out_frame_count = 0; videorate->out_frame_count = 0;
@ -871,6 +990,10 @@ gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
goto done; goto done;
} }
} }
/* Do not produce any dups. We can exit loop now */
if (videorate->drop_only)
break;
/* continue while the first one was the best, if they were equal avoid /* continue while the first one was the best, if they were equal avoid
* going into an infinite loop */ * going into an infinite loop */
} }
@ -931,6 +1054,7 @@ gst_video_rate_set_property (GObject * object,
{ {
GstVideoRate *videorate = GST_VIDEO_RATE (object); GstVideoRate *videorate = GST_VIDEO_RATE (object);
GST_OBJECT_LOCK (videorate);
switch (prop_id) { switch (prop_id) {
case ARG_SILENT: case ARG_SILENT:
videorate->silent = g_value_get_boolean (value); videorate->silent = g_value_get_boolean (value);
@ -941,10 +1065,17 @@ gst_video_rate_set_property (GObject * object,
case ARG_SKIP_TO_FIRST: case ARG_SKIP_TO_FIRST:
videorate->skip_to_first = g_value_get_boolean (value); videorate->skip_to_first = g_value_get_boolean (value);
break; break;
case ARG_DROP_ONLY:
videorate->drop_only = g_value_get_boolean (value);
break;
case ARG_AVERAGE_PERIOD:
videorate->average_period = g_value_get_uint64 (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
GST_OBJECT_UNLOCK (videorate);
} }
static void static void
@ -953,6 +1084,7 @@ gst_video_rate_get_property (GObject * object,
{ {
GstVideoRate *videorate = GST_VIDEO_RATE (object); GstVideoRate *videorate = GST_VIDEO_RATE (object);
GST_OBJECT_LOCK (videorate);
switch (prop_id) { switch (prop_id) {
case ARG_IN: case ARG_IN:
g_value_set_uint64 (value, videorate->in); g_value_set_uint64 (value, videorate->in);
@ -975,10 +1107,17 @@ gst_video_rate_get_property (GObject * object,
case ARG_SKIP_TO_FIRST: case ARG_SKIP_TO_FIRST:
g_value_set_boolean (value, videorate->skip_to_first); g_value_set_boolean (value, videorate->skip_to_first);
break; break;
case ARG_DROP_ONLY:
g_value_set_boolean (value, videorate->drop_only);
break;
case ARG_AVERAGE_PERIOD:
g_value_set_uint64 (value, videorate->average_period);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
GST_OBJECT_UNLOCK (videorate);
} }
static GstStateChangeReturn static GstStateChangeReturn

View file

@ -63,6 +63,10 @@ struct _GstVideoRate
gboolean discont; gboolean discont;
guint64 last_ts; /* Timestamp of last input buffer */ guint64 last_ts; /* Timestamp of last input buffer */
guint64 average_period;
GstClockTimeDiff wanted_diff; /* target average diff */
GstClockTimeDiff average; /* moving average period */
/* segment handling */ /* segment handling */
GstSegment segment; GstSegment segment;
@ -71,6 +75,8 @@ struct _GstVideoRate
gboolean silent; gboolean silent;
gdouble new_pref; gdouble new_pref;
gboolean skip_to_first; gboolean skip_to_first;
gboolean drop_only;
guint64 average_period_set;
}; };
struct _GstVideoRateClass struct _GstVideoRateClass

View file

@ -129,6 +129,7 @@ check_PROGRAMS = \
elements/multifdsink \ elements/multifdsink \
elements/playbin \ elements/playbin \
elements/playbin2 \ elements/playbin2 \
elements/playbin2-compressed \
$(check_subparse) \ $(check_subparse) \
elements/videorate \ elements/videorate \
elements/videoscale \ elements/videoscale \
@ -376,6 +377,9 @@ elements_playbin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_playbin2_LDADD = $(GST_BASE_LIBS) $(LDADD) elements_playbin2_LDADD = $(GST_BASE_LIBS) $(LDADD)
elements_playbin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_playbin2_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_playbin2_compressed_LDADD = $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la $(GST_BASE_LIBS) $(LDADD)
elements_playbin2_compressed_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD) elements_decodebin_LDADD = $(GST_BASE_LIBS) $(LDADD)
elements_decodebin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_decodebin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)

File diff suppressed because it is too large Load diff

View file

@ -925,12 +925,13 @@ tag_list_equals (GstTagList * taglist, GstTagList * taglist2)
} }
static void static void
do_xmp_tag_serialization_deserialization (GstTagList * taglist) do_xmp_tag_serialization_deserialization (GstTagList * taglist,
const gchar ** schemas)
{ {
GstTagList *taglist2; GstTagList *taglist2;
GstBuffer *buf; GstBuffer *buf;
buf = gst_tag_list_to_xmp_buffer (taglist, TRUE); buf = gst_tag_list_to_xmp_buffer_full (taglist, TRUE, schemas);
taglist2 = gst_tag_list_from_xmp_buffer (buf); taglist2 = gst_tag_list_from_xmp_buffer (buf);
tag_list_equals (taglist, taglist2); tag_list_equals (taglist, taglist2);
@ -947,7 +948,7 @@ do_simple_xmp_tag_serialization_deserialization (const gchar * gsttag,
gst_tag_list_add_value (taglist, GST_TAG_MERGE_REPLACE, gsttag, value); gst_tag_list_add_value (taglist, GST_TAG_MERGE_REPLACE, gsttag, value);
do_xmp_tag_serialization_deserialization (taglist); do_xmp_tag_serialization_deserialization (taglist, NULL);
gst_tag_list_free (taglist); gst_tag_list_free (taglist);
} }
@ -1134,13 +1135,19 @@ GST_END_TEST;
GST_START_TEST (test_xmp_compound_tags) GST_START_TEST (test_xmp_compound_tags)
{ {
const gchar *schemas[] = { "Iptc4xmpExt", NULL };
GstTagList *taglist = gst_tag_list_new (); GstTagList *taglist = gst_tag_list_new ();
gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_KEYWORDS, "k1", gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_KEYWORDS, "k1",
GST_TAG_KEYWORDS, "k2", GST_TAG_TITLE, "title", GST_TAG_KEYWORDS, "k3", GST_TAG_KEYWORDS, "k2", GST_TAG_TITLE, "title", GST_TAG_KEYWORDS, "k3",
NULL); NULL);
do_xmp_tag_serialization_deserialization (taglist, NULL);
gst_tag_list_free (taglist);
do_xmp_tag_serialization_deserialization (taglist); taglist = gst_tag_list_new ();
gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_GEO_LOCATION_COUNTRY,
"Brazil", GST_TAG_GEO_LOCATION_CITY, "Campina Grande", NULL);
do_xmp_tag_serialization_deserialization (taglist, schemas);
gst_tag_list_free (taglist); gst_tag_list_free (taglist);
} }

View file

@ -6,7 +6,9 @@ EXTRA_DIST = gstcapslist.h
noinst_PROGRAMS = $(examples) noinst_PROGRAMS = $(examples)
LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \ LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
$(GST_BASE_LIBS) \
$(GST_LIBS) $(GST_LIBS)
AM_CFLAGS = -I$(top_builddir)/gst-libs \ AM_CFLAGS = -I$(top_builddir)/gst-libs \
$(GST_PLUGINS_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) $(GST_CFLAGS)

View file

@ -1,7 +1,7 @@
#ifndef _GST_PLUGINS_BASE__STDINT_H #ifndef _GST_PLUGINS_BASE__STDINT_H
#define _GST_PLUGINS_BASE__STDINT_H 1 #define _GST_PLUGINS_BASE__STDINT_H 1
#ifndef _GENERATED_STDINT_H #ifndef _GENERATED_STDINT_H
#define _GENERATED_STDINT_H "gst-plugins-base 0.10.32.4" #define _GENERATED_STDINT_H "gst-plugins-base 0.10.34.1"
/* generated using gnu compiler gcc (Debian 4.5.2-8) 4.5.2 */ /* generated using gnu compiler gcc (Debian 4.5.2-8) 4.5.2 */
#define _STDINT_HAVE_STDINT_H 1 #define _STDINT_HAVE_STDINT_H 1
#include <stdint.h> #include <stdint.h>

View file

@ -75,13 +75,13 @@
#define GST_MAJORMINOR "0.10" #define GST_MAJORMINOR "0.10"
/* package name in plugins */ /* package name in plugins */
#define GST_PACKAGE_NAME "GStreamer Base Plug-ins prerelease" #define GST_PACKAGE_NAME "GStreamer Base Plug-ins git"
/* package origin */ /* package origin */
#define GST_PACKAGE_ORIGIN "Unknown package origin" #define GST_PACKAGE_ORIGIN "Unknown package origin"
/* GStreamer package release date/time for plugins as YYYY-MM-DD */ /* GStreamer package release date/time for plugins as YYYY-MM-DD */
#define GST_PACKAGE_RELEASE_DATETIME "2011-04-30T15:57Z" #define GST_PACKAGE_RELEASE_DATETIME "2011-05-14T08:32Z"
/* I know the API is subject to change. */ /* I know the API is subject to change. */
#undef G_UDEV_API_IS_SUBJECT_TO_CHANGE #undef G_UDEV_API_IS_SUBJECT_TO_CHANGE
@ -337,7 +337,7 @@
#define PACKAGE_NAME "GStreamer Base Plug-ins" #define PACKAGE_NAME "GStreamer Base Plug-ins"
/* Define to the full name and version of this package. */ /* Define to the full name and version of this package. */
#define PACKAGE_STRING "GStreamer Base Plug-ins 0.10.32.4" #define PACKAGE_STRING "GStreamer Base Plug-ins 0.10.34.1"
/* Define to the one symbol short name of this package. */ /* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "gst-plugins-base" #define PACKAGE_TARNAME "gst-plugins-base"
@ -346,7 +346,7 @@
#undef PACKAGE_URL #undef PACKAGE_URL
/* Define to the version of this package. */ /* Define to the version of this package. */
#define PACKAGE_VERSION "0.10.32.4" #define PACKAGE_VERSION "0.10.34.1"
/* directory where plugins are located */ /* directory where plugins are located */
#ifdef _DEBUG #ifdef _DEBUG
@ -377,7 +377,7 @@
#undef USE_TREMOLO #undef USE_TREMOLO
/* Version number of package */ /* Version number of package */
#define VERSION "0.10.32.4" #define VERSION "0.10.34.1"
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */ significant byte first (like Motorola and SPARC, unlike Intel). */

View file

@ -17,6 +17,8 @@ EXPORTS
gst_audio_frame_byte_size gst_audio_frame_byte_size
gst_audio_frame_length gst_audio_frame_length
gst_audio_get_channel_positions gst_audio_get_channel_positions
gst_audio_iec61937_frame_size
gst_audio_iec61937_payload
gst_audio_is_buffer_framed gst_audio_is_buffer_framed
gst_audio_set_caps_channel_positions_list gst_audio_set_caps_channel_positions_list
gst_audio_set_channel_positions gst_audio_set_channel_positions