mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-29 20:35:40 +00:00
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:
commit
d0362c2b87
70 changed files with 7903 additions and 711 deletions
163
NEWS
163
NEWS
|
@ -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:
|
||||
|
||||
|
|
159
RELEASE
159
RELEASE
|
@ -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.
|
||||
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
|
||||
|
||||
* GLib requirement is now >= 2.22, gobject-introspection >= 0.9.12
|
||||
* New encodebin element
|
||||
* New encoding profile and encoding targets API in pbutils
|
||||
* audioresample: corrected buffer duration calculation to account for nonzero initial timestamp
|
||||
* audioresample: provide as much valid output ts and offset as valid input
|
||||
* audioresample: push half a history length, instead of a full history length, at end-of-stream so that output segment and input segment have same duration
|
||||
* decodebin2: deprecate new-decoded-pad and removed-decoded-pad signals (use "pad-added" and "pad-removed" instead)
|
||||
* multifdsink: add first and last buffer's timestamp to the stats; only keep last valid timestamp
|
||||
* oggdemux: extract more tags (vorbis comment headers, Kate)
|
||||
* oggdemux: ignore header pages when looking for keyframe; set headers on caps
|
||||
* oggdemux: fix interpretation of Theora granule position and parsing of Theora size
|
||||
* oggparse: Set DELTA_UNIT on buffers
|
||||
* playbin2: delay stream-changed messages, fixing KATE subtitle recognition
|
||||
* textoverlay: make text, xpos, ypos, color, and silent properties controllable
|
||||
* typefinding: (E)AC-3 and ISO typefinder improvements; add yuv4mpeg typefinder
|
||||
* typefinding: add "stream-format" to h264 caps, and framed=false to DTS caps
|
||||
* typefinding: assume EBML files without doctype are matroska
|
||||
* videorate: fix behaviour for frame rate cap changes
|
||||
* vorbisdec: avoid using invalid timestamps; keep timestamps when no decoded output
|
||||
* ximagesink, xvimagesink: add read-only window-width and window-height properties
|
||||
* baseaudiopay: fix timestamps on buffer lists
|
||||
* baseaudiosink: protect against ringbuffer disappearing while in a query
|
||||
* basedepay: add support for buffer lists in the depayloader
|
||||
* basertppay: use RTP base time when invalid timestamps
|
||||
* rtpbuffer: relax arrangement for RTP bufferlists
|
||||
* rtpdepayloader: add support for getting events
|
||||
* rtppayload: copy applied rate to segment
|
||||
* sdp: add method to check for multicast addresses
|
||||
* sdp: only parse TTL for IP4 addresses
|
||||
* video: add 8-bit paletted RGB, YUV9, YVU9 and IYU1 video formats
|
||||
* video: return correct component width/height for A420
|
||||
|
||||
Bugs fixed in this release
|
||||
|
||||
* 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:
|
||||
* This release is identical to 0.10.33 (to keep core/base versions in sync)
|
||||
There were no bugs fixed in this release
|
||||
|
||||
* 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
|
||||
|
||||
|
@ -205,35 +86,5 @@ Applications
|
|||
|
||||
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
|
||||
* Vincent Penquerc'h
|
||||
* Wim Taymans
|
||||
* Yang Xichuan
|
||||
* Zeeshan Ali (Khattak)
|
||||
* christian schaller
|
||||
|
|
@ -49,7 +49,7 @@ dnl - interfaces added/removed/changed -> increment CURRENT, REVISION = 0
|
|||
dnl - interfaces added -> increment AGE
|
||||
dnl - interfaces removed -> AGE = 0
|
||||
dnl sets GST_LT_LDFLAGS
|
||||
AS_LIBTOOL(GST, 23, 0, 23)
|
||||
AS_LIBTOOL(GST, 24, 0, 24)
|
||||
|
||||
dnl FIXME: this macro doesn't actually work;
|
||||
dnl the generated libtool script has no support for the listed tags.
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
<xi:include href="xml/gstbaseaudiosrc.xml" />
|
||||
<xi:include href="xml/gstmultichannel.xml" />
|
||||
<xi:include href="xml/gstringbuffer.xml" />
|
||||
<xi:include href="xml/gstaudioiec61937.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="gstreamer-cdda">
|
||||
|
|
|
@ -317,6 +317,13 @@ gst_ring_buffer_debug_spec_buff
|
|||
gst_ring_buffer_debug_spec_caps
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gstaudioiec61937</FILE>
|
||||
<INCLUDE>gst/audio/gstaudioiec61937.h</INCLUDE>
|
||||
gst_audio_iec61937_frame_size
|
||||
gst_audio_iec61937_payload
|
||||
</SECTION>
|
||||
|
||||
|
||||
# cdda
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Adds multiple streams</description>
|
||||
<filename>../../gst/adder/.libs/libgstadder.so</filename>
|
||||
<basename>libgstadder.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>ALSA plugin library</description>
|
||||
<filename>../../ext/alsa/.libs/libgstalsa.so</filename>
|
||||
<basename>libgstalsa.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Elements used to communicate with applications</description>
|
||||
<filename>../../gst/app/.libs/libgstapp.so</filename>
|
||||
<basename>libgstapp.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Convert audio to different formats</description>
|
||||
<filename>../../gst/audioconvert/.libs/libgstaudioconvert.so</filename>
|
||||
<basename>libgstaudioconvert.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Adjusts audio frames</description>
|
||||
<filename>../../gst/audiorate/.libs/libgstaudiorate.so</filename>
|
||||
<basename>libgstaudiorate.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Resamples audio</description>
|
||||
<filename>../../gst/audioresample/.libs/libgstaudioresample.so</filename>
|
||||
<basename>libgstaudioresample.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Creates audio test signals of given frequency and volume</description>
|
||||
<filename>../../gst/audiotestsrc/.libs/libgstaudiotestsrc.so</filename>
|
||||
<basename>libgstaudiotestsrc.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Read audio from CD in paranoid mode</description>
|
||||
<filename>../../ext/cdparanoia/.libs/libgstcdparanoia.so</filename>
|
||||
<basename>libgstcdparanoia.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>decoder bin</description>
|
||||
<filename>../../gst/playback/.libs/libgstdecodebin.so</filename>
|
||||
<basename>libgstdecodebin.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>various encoding-related elements</description>
|
||||
<filename>../../gst/encoding/.libs/libgstencodebin.so</filename>
|
||||
<basename>libgstencodebin.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<description>colorspace conversion copied from FFMpeg 0.4.9-pre1</description>
|
||||
<filename>../../gst/ffmpegcolorspace/.libs/libgstffmpegcolorspace.so</filename>
|
||||
<basename>libgstffmpegcolorspace.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>FFMpeg</package>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Payload/depayload GDP packets</description>
|
||||
<filename>../../gst/gdp/.libs/libgstgdp.so</filename>
|
||||
<basename>libgstgdp.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>GIO elements</description>
|
||||
<filename>../../ext/gio/.libs/libgstgio.so</filename>
|
||||
<basename>libgstgio.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>elements to read from and write to Gnome-VFS uri's</description>
|
||||
<filename>../../ext/gnomevfs/.libs/libgstgnomevfs.so</filename>
|
||||
<basename>libgstgnomevfs.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>libvisual visualization plugins</description>
|
||||
<filename>../../ext/libvisual/.libs/libgstlibvisual.so</filename>
|
||||
<basename>libgstlibvisual.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>ogg stream manipulation (info about ogg: http://xiph.org)</description>
|
||||
<filename>../../ext/ogg/.libs/libgstogg.so</filename>
|
||||
<basename>libgstogg.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Pango-based text rendering and overlay</description>
|
||||
<filename>../../ext/pango/.libs/libgstpango.so</filename>
|
||||
<basename>libgstpango.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>various playback elements</description>
|
||||
<filename>../../gst/playback/.libs/libgstplaybin.so</filename>
|
||||
<basename>libgstplaybin.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Subtitle parsing</description>
|
||||
<filename>../../gst/subparse/.libs/libgstsubparse.so</filename>
|
||||
<basename>libgstsubparse.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>transfer data over the network via TCP</description>
|
||||
<filename>../../gst/tcp/.libs/libgsttcp.so</filename>
|
||||
<basename>libgsttcp.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Theora plugin library</description>
|
||||
<filename>../../ext/theora/.libs/libgsttheora.so</filename>
|
||||
<basename>libgsttheora.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>default typefind functions</description>
|
||||
<filename>../../gst/typefind/.libs/libgsttypefindfunctions.so</filename>
|
||||
<basename>libgsttypefindfunctions.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
</elements>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>URI Decoder bin</description>
|
||||
<filename>../../gst/playback/.libs/libgstdecodebin2.so</filename>
|
||||
<basename>libgstdecodebin2.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Adjusts video frames</description>
|
||||
<filename>../../gst/videorate/.libs/libgstvideorate.so</filename>
|
||||
<basename>libgstvideorate.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Resizes video</description>
|
||||
<filename>../../gst/videoscale/.libs/libgstvideoscale.so</filename>
|
||||
<basename>libgstvideoscale.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Creates a test video stream</description>
|
||||
<filename>../../gst/videotestsrc/.libs/libgstvideotestsrc.so</filename>
|
||||
<basename>libgstvideotestsrc.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>plugin for controlling audio volume</description>
|
||||
<filename>../../gst/volume/.libs/libgstvolume.so</filename>
|
||||
<basename>libgstvolume.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>Vorbis plugin library</description>
|
||||
<filename>../../ext/vorbis/.libs/libgstvorbis.so</filename>
|
||||
<basename>libgstvorbis.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>X11 video output element based on standard Xlib calls</description>
|
||||
<filename>../../sys/ximage/.libs/libgstximagesink.so</filename>
|
||||
<basename>libgstximagesink.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<description>XFree86 video output plugin using Xv extension</description>
|
||||
<filename>../../sys/xvimage/.libs/libgstxvimagesink.so</filename>
|
||||
<basename>libgstxvimagesink.so</basename>
|
||||
<version>0.10.32.4</version>
|
||||
<version>0.10.34.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-base</source>
|
||||
<package>GStreamer Base Plug-ins prerelease</package>
|
||||
<package>GStreamer Base Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
|
|
|
@ -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_delay (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 */
|
||||
enum
|
||||
|
@ -189,11 +195,13 @@ gst_alsasrc_class_init (GstAlsaSrcClass * klass)
|
|||
GstElementClass *gstelement_class;
|
||||
GstBaseSrcClass *gstbasesrc_class;
|
||||
GstAudioSrcClass *gstaudiosrc_class;
|
||||
GstBaseAudioSrcClass *gstbaseaudiosrc_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
||||
gstaudiosrc_class = (GstAudioSrcClass *) klass;
|
||||
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_alsasrc_finalize;
|
||||
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_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->create = GST_DEBUG_FUNCPTR (gst_alsasrc_create);
|
||||
|
||||
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open);
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
gst_alsasrc_set_property (GObject * object, guint prop_id,
|
||||
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
|
||||
gst_alsasrc_init (GstAlsaSrc * alsasrc)
|
||||
{
|
||||
|
@ -289,6 +394,7 @@ gst_alsasrc_init (GstAlsaSrc * alsasrc)
|
|||
|
||||
alsasrc->device = g_strdup (DEFAULT_PROP_DEVICE);
|
||||
alsasrc->cached_caps = NULL;
|
||||
alsasrc->driver_timestamps = FALSE;
|
||||
|
||||
alsasrc->alsa_lock = g_mutex_new ();
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ struct _GstAlsaSrc {
|
|||
guint rate;
|
||||
guint channels;
|
||||
gint bytes_per_sample;
|
||||
gboolean driver_timestamps;
|
||||
|
||||
guint buffer_time;
|
||||
guint period_time;
|
||||
|
|
|
@ -26,7 +26,8 @@ libgstaudio_@GST_MAJORMINOR@_la_SOURCES = \
|
|||
gstbaseaudiosrc.c \
|
||||
gstaudiofilter.c \
|
||||
gstaudiosink.c \
|
||||
gstaudiosrc.c
|
||||
gstaudiosrc.c \
|
||||
gstaudioiec61937.c
|
||||
nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers)
|
||||
|
||||
libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
|
||||
|
@ -40,7 +41,8 @@ libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
|
|||
gstaudiosink.h \
|
||||
gstaudiosrc.h \
|
||||
mixerutils.h \
|
||||
multichannel.h
|
||||
multichannel.h \
|
||||
gstaudioiec61937.h
|
||||
|
||||
nodist_libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
|
||||
audio-enumtypes.h
|
||||
|
|
319
gst-libs/gst/audio/gstaudioiec61937.c
Normal file
319
gst-libs/gst/audio/gstaudioiec61937.c
Normal 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;
|
||||
}
|
38
gst-libs/gst/audio/gstaudioiec61937.h
Normal file
38
gst-libs/gst/audio/gstaudioiec61937.h
Normal 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__ */
|
|
@ -1345,6 +1345,7 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
|
|||
guint64 in_offset;
|
||||
GstClockTime time, stop, render_start, render_stop, sample_offset;
|
||||
GstClockTimeDiff sync_offset, ts_offset;
|
||||
GstBaseAudioSinkClass *bclass;
|
||||
GstBaseAudioSink *sink;
|
||||
GstRingBuffer *ringbuf;
|
||||
gint64 diff, align;
|
||||
|
@ -1362,8 +1363,10 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
|
|||
GstFlowReturn ret;
|
||||
GstSegment clip_seg;
|
||||
gint64 time_offset;
|
||||
GstBuffer *out = NULL;
|
||||
|
||||
sink = GST_BASE_AUDIO_SINK (bsink);
|
||||
bclass = GST_BASE_AUDIO_SINK_GET_CLASS (sink);
|
||||
|
||||
ringbuf = sink->ringbuffer;
|
||||
|
||||
|
@ -1387,6 +1390,17 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
|
|||
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;
|
||||
|
||||
size = gst_buffer_get_size (buf);
|
||||
|
@ -1669,7 +1683,13 @@ no_sync:
|
|||
gst_ring_buffer_start (ringbuf);
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
done:
|
||||
if (out)
|
||||
gst_buffer_unref (out);
|
||||
|
||||
return ret;
|
||||
|
||||
/* SPECIAL cases */
|
||||
out_of_segment:
|
||||
|
@ -1678,33 +1698,42 @@ out_of_segment:
|
|||
"dropping sample out of segment time %" GST_TIME_FORMAT ", start %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (time),
|
||||
GST_TIME_ARGS (bsink->segment.start));
|
||||
return GST_FLOW_OK;
|
||||
ret = GST_FLOW_OK;
|
||||
goto done;
|
||||
}
|
||||
/* ERRORS */
|
||||
payload_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("failed to payload."));
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto done;
|
||||
}
|
||||
wrong_state:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink, "ringbuffer 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:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink, "wrong size");
|
||||
GST_ELEMENT_ERROR (sink, STREAM, WRONG_TYPE,
|
||||
(NULL), ("sink received buffer of wrong size."));
|
||||
return GST_FLOW_ERROR;
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto done;
|
||||
}
|
||||
stopping:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink, "preroll got interrupted: %d (%s)", ret,
|
||||
gst_flow_get_name (ret));
|
||||
gst_buffer_unmap (buf, data, size);
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
sync_latency_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink, "failed waiting for latency");
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -141,6 +141,10 @@ struct _GstBaseAudioSink {
|
|||
* GstBaseAudioSinkClass:
|
||||
* @parent_class: the parent class.
|
||||
* @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
|
||||
* functionality.
|
||||
|
@ -151,8 +155,12 @@ struct _GstBaseAudioSinkClass {
|
|||
/* subclass ringbuffer allocation */
|
||||
GstRingBuffer* (*create_ringbuffer) (GstBaseAudioSink *sink);
|
||||
|
||||
/* subclass payloader */
|
||||
GstBuffer* (*payload) (GstBaseAudioSink *sink,
|
||||
GstBuffer *buffer);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
gpointer _gst_reserved[GST_PADDING - 1];
|
||||
};
|
||||
|
||||
GType gst_base_audio_sink_get_type(void);
|
||||
|
|
|
@ -327,7 +327,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
|
|||
/* we have to differentiate between int and float formats */
|
||||
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;
|
||||
const FormatDef *def;
|
||||
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];
|
||||
}
|
||||
}
|
||||
} else if (!strncmp (mimetype, "audio/x-raw-float", 17)) {
|
||||
} else if (g_str_equal (mimetype, "audio/x-raw-float")) {
|
||||
|
||||
spec->type = GST_BUFTYPE_FLOAT;
|
||||
|
||||
|
@ -392,7 +392,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
|
|||
}
|
||||
/* float silence is all zeros.. */
|
||||
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 */
|
||||
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
|
||||
gst_structure_get_int (structure, "channels", &spec->channels)))
|
||||
|
@ -404,7 +404,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
|
|||
spec->depth = 8;
|
||||
for (i = 0; i < spec->channels; i++)
|
||||
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 */
|
||||
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
|
||||
gst_structure_get_int (structure, "channels", &spec->channels)))
|
||||
|
@ -416,7 +416,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
|
|||
spec->depth = 8;
|
||||
for (i = 0; i < spec->channels; i++)
|
||||
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 */
|
||||
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
|
||||
goto parse_error;
|
||||
|
@ -426,7 +426,7 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
|
|||
spec->width = 16;
|
||||
spec->depth = 16;
|
||||
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 */
|
||||
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
|
||||
goto parse_error;
|
||||
|
@ -436,6 +436,39 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
|
|||
spec->width = 16;
|
||||
spec->depth = 16;
|
||||
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 {
|
||||
goto parse_error;
|
||||
}
|
||||
|
|
|
@ -88,12 +88,14 @@ typedef enum {
|
|||
* @GST_BUFTYPE_MU_LAW: samples in mulaw
|
||||
* @GST_BUFTYPE_A_LAW: samples in alaw
|
||||
* @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_IEC958: samples in IEC958 frames (e.g. AC3)
|
||||
* @GST_BUFTYPE_AC3: samples in AC3 format
|
||||
* @GST_BUFTYPE_EAC3: samples in EAC3 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.
|
||||
*/
|
||||
|
@ -109,7 +111,9 @@ typedef enum
|
|||
GST_BUFTYPE_IEC958,
|
||||
GST_BUFTYPE_AC3,
|
||||
GST_BUFTYPE_EAC3,
|
||||
GST_BUFTYPE_DTS
|
||||
GST_BUFTYPE_DTS,
|
||||
GST_BUFTYPE_MPEG2_AAC,
|
||||
GST_BUFTYPE_MPEG4_AAC,
|
||||
} GstBufferFormatType;
|
||||
|
||||
typedef enum
|
||||
|
@ -161,7 +165,9 @@ typedef enum
|
|||
GST_IEC958,
|
||||
GST_AC3,
|
||||
GST_EAC3,
|
||||
GST_DTS
|
||||
GST_DTS,
|
||||
GST_MPEG2_AAC,
|
||||
GST_MPEG4_AAC,
|
||||
} GstBufferFormat;
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,6 +51,7 @@ static const gchar *schema_list[] = {
|
|||
"exif",
|
||||
"photoshop",
|
||||
"Iptc4xmpCore",
|
||||
"Iptc4xmpExt",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -112,14 +113,31 @@ xmp_serialization_data_use_schema (XmpSerializationData * serdata,
|
|||
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
|
||||
{
|
||||
const gchar *gst_tag;
|
||||
const gchar *tag_name;
|
||||
gint type;
|
||||
GstXmpTagType type;
|
||||
|
||||
/* Used for struct and compound types */
|
||||
GSList *children;
|
||||
|
||||
XmpSerializationFunc serialize;
|
||||
XmpDeserializationFunc deserialize;
|
||||
|
@ -129,10 +147,10 @@ static GstTagMergeMode
|
|||
xmp_tag_get_merge_mode (XmpTag * xmptag)
|
||||
{
|
||||
switch (xmptag->type) {
|
||||
case GST_XMP_TAG_TYPE_BAG:
|
||||
case GST_XMP_TAG_TYPE_SEQ:
|
||||
case GstXmpTagTypeBag:
|
||||
case GstXmpTagTypeSeq:
|
||||
return GST_TAG_MERGE_APPEND;
|
||||
case GST_XMP_TAG_TYPE_SIMPLE:
|
||||
case GstXmpTagTypeSimple:
|
||||
default:
|
||||
return GST_TAG_MERGE_KEEP;
|
||||
}
|
||||
|
@ -142,31 +160,25 @@ static const gchar *
|
|||
xmp_tag_get_type_name (XmpTag * xmptag)
|
||||
{
|
||||
switch (xmptag->type) {
|
||||
case GST_XMP_TAG_TYPE_SEQ:
|
||||
case GstXmpTagTypeSeq:
|
||||
return "rdf:Seq";
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
case GST_XMP_TAG_TYPE_BAG:
|
||||
case GstXmpTagTypeBag:
|
||||
return "rdf:Bag";
|
||||
}
|
||||
}
|
||||
|
||||
struct _PendingXmpTag
|
||||
{
|
||||
const gchar *gst_tag;
|
||||
XmpTag *xmp_tag;
|
||||
gchar *str;
|
||||
};
|
||||
typedef struct _PendingXmpTag PendingXmpTag;
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
* tags in xmp (XmpTag).
|
||||
*/
|
||||
typedef GHashTable GstXmpSchema;
|
||||
#define gst_xmp_schema_lookup g_hash_table_lookup
|
||||
|
@ -182,6 +194,21 @@ gst_xmp_schema_new ()
|
|||
*/
|
||||
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
|
||||
_gst_xmp_add_schema (const gchar * name, GstXmpSchema * schema)
|
||||
{
|
||||
|
@ -199,19 +226,59 @@ _gst_xmp_add_schema (const gchar * name, GstXmpSchema * schema)
|
|||
}
|
||||
|
||||
static void
|
||||
_gst_xmp_schema_add_mapping (GstXmpSchema * schema, const gchar * gst_tag,
|
||||
GPtrArray * array)
|
||||
_gst_xmp_schema_add_mapping (GstXmpSchema * schema, XmpTag * tag)
|
||||
{
|
||||
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))) {
|
||||
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 ();
|
||||
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
|
||||
|
@ -220,19 +287,9 @@ _gst_xmp_schema_add_simple_mapping (GstXmpSchema * schema,
|
|||
XmpSerializationFunc serialization_func,
|
||||
XmpDeserializationFunc deserialization_func)
|
||||
{
|
||||
XmpTag *xmpinfo;
|
||||
GPtrArray *array;
|
||||
|
||||
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);
|
||||
_gst_xmp_schema_add_mapping (schema,
|
||||
gst_xmp_tag_create (gst_tag, xmp_tag, xmp_type, serialization_func,
|
||||
deserialization_func));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -240,6 +297,7 @@ _gst_xmp_schema_add_simple_mapping (GstXmpSchema * schema,
|
|||
* appended, and the API is not public, so we shouldn't
|
||||
* have our lists modified during usage
|
||||
*/
|
||||
#if 0
|
||||
static GPtrArray *
|
||||
_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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* finds the gst tag that maps to this xmp tag in this schema */
|
||||
static const gchar *
|
||||
|
@ -269,22 +328,30 @@ _gst_xmp_schema_get_mapping_reverse (GstXmpSchema * schema,
|
|||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
const gchar *ret = NULL;
|
||||
gint index;
|
||||
|
||||
/* Iterate over the hashtable */
|
||||
g_hash_table_iter_init (&iter, schema);
|
||||
while (!ret && g_hash_table_iter_next (&iter, &key, &value)) {
|
||||
GPtrArray *array = (GPtrArray *) value;
|
||||
|
||||
/* each mapping might contain complementary tags */
|
||||
for (index = 0; index < array->len; index++) {
|
||||
XmpTag *xmpinfo = (XmpTag *) g_ptr_array_index (array, index);
|
||||
XmpTag *xmpinfo = (XmpTag *) value;
|
||||
|
||||
if (xmpinfo->tag_name) {
|
||||
if (strcmp (xmpinfo->tag_name, xmp_tag) == 0) {
|
||||
*_xmp_tag = xmpinfo;
|
||||
ret = g_quark_to_string (GPOINTER_TO_UINT (key));
|
||||
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
|
||||
_init_xmp_tag_map (gpointer user_data)
|
||||
{
|
||||
GPtrArray *array;
|
||||
XmpTag *xmpinfo;
|
||||
GstXmpSchema *schema;
|
||||
|
||||
|
@ -851,135 +917,115 @@ _init_xmp_tag_map (gpointer user_data)
|
|||
*/
|
||||
schema = gst_xmp_schema_new ();
|
||||
_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,
|
||||
"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_TAG_TYPE_SEQ, NULL, NULL);
|
||||
GstXmpTagTypeSeq, NULL, NULL);
|
||||
_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,
|
||||
"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_TAG_TYPE_SIMPLE, NULL, NULL);
|
||||
GstXmpTagTypeSimple, NULL, NULL);
|
||||
/* FIXME: we probably want GST_TAG_{,AUDIO_,VIDEO_}MIME_TYPE */
|
||||
_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);
|
||||
|
||||
/* xap (xmp) schema */
|
||||
schema = gst_xmp_schema_new ();
|
||||
_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);
|
||||
|
||||
/* tiff */
|
||||
schema = gst_xmp_schema_new ();
|
||||
_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);
|
||||
_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,
|
||||
"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,
|
||||
"tiff:Orientation", GST_XMP_TAG_TYPE_SIMPLE, serialize_tiff_orientation,
|
||||
"tiff:Orientation", GstXmpTagTypeSimple, serialize_tiff_orientation,
|
||||
deserialize_tiff_orientation);
|
||||
_gst_xmp_add_schema ("tiff", schema);
|
||||
|
||||
/* exif schema */
|
||||
schema = gst_xmp_schema_new ();
|
||||
_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_TAG_GEO_LOCATION_LATITUDE, "exif:GPSLatitude",
|
||||
GST_XMP_TAG_TYPE_SIMPLE, serialize_exif_latitude,
|
||||
deserialize_exif_latitude);
|
||||
GstXmpTagTypeSimple, serialize_exif_latitude, deserialize_exif_latitude);
|
||||
_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);
|
||||
_gst_xmp_schema_add_simple_mapping (schema,
|
||||
GST_TAG_CAPTURING_EXPOSURE_COMPENSATION, "exif:ExposureBiasValue",
|
||||
GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
|
||||
GstXmpTagTypeSimple, NULL, NULL);
|
||||
|
||||
/* compound exif tags */
|
||||
array = g_ptr_array_sized_new (2);
|
||||
xmpinfo = g_slice_new (XmpTag);
|
||||
xmpinfo->tag_name = "exif:GPSAltitude";
|
||||
xmpinfo->serialize = serialize_exif_altitude;
|
||||
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);
|
||||
xmpinfo = gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_ELEVATION,
|
||||
"exif:GPSAltitude", "exif:GPSAltitudeRef", serialize_exif_altitude,
|
||||
serialize_exif_altituderef, deserialize_exif_altitude);
|
||||
_gst_xmp_schema_add_mapping (schema, xmpinfo);
|
||||
|
||||
array = g_ptr_array_sized_new (2);
|
||||
xmpinfo = g_slice_new (XmpTag);
|
||||
xmpinfo->tag_name = "exif:GPSSpeed";
|
||||
xmpinfo->serialize = serialize_exif_gps_speed;
|
||||
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);
|
||||
xmpinfo = gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_MOVEMENT_SPEED,
|
||||
"exif:GPSSpeed", "exif:GPSSpeedRef", serialize_exif_gps_speed,
|
||||
serialize_exif_gps_speedref, deserialize_exif_gps_speed);
|
||||
_gst_xmp_schema_add_mapping (schema, xmpinfo);
|
||||
|
||||
array = g_ptr_array_sized_new (2);
|
||||
xmpinfo = g_slice_new (XmpTag);
|
||||
xmpinfo->tag_name = "exif:GPSTrack";
|
||||
xmpinfo->serialize = serialize_exif_gps_direction;
|
||||
xmpinfo->deserialize = deserialize_exif_gps_track;
|
||||
xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
|
||||
g_ptr_array_add (array, xmpinfo);
|
||||
xmpinfo = g_slice_new (XmpTag);
|
||||
xmpinfo->tag_name = "exif:GPSTrackRef";
|
||||
xmpinfo->serialize = serialize_exif_gps_directionref;
|
||||
xmpinfo->deserialize = deserialize_exif_gps_track;
|
||||
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);
|
||||
xmpinfo =
|
||||
gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION,
|
||||
"exif:GPSTrack", "exif:GPSTrackRef", serialize_exif_gps_direction,
|
||||
serialize_exif_gps_directionref, deserialize_exif_gps_track);
|
||||
_gst_xmp_schema_add_mapping (schema, xmpinfo);
|
||||
|
||||
xmpinfo = gst_xmp_tag_create_compound (GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION,
|
||||
"exif:GPSImgDirection", "exif:GPSImgDirectionRef",
|
||||
serialize_exif_gps_direction, serialize_exif_gps_directionref,
|
||||
deserialize_exif_gps_img_direction);
|
||||
_gst_xmp_schema_add_mapping (schema, xmpinfo);
|
||||
|
||||
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);
|
||||
|
||||
/* photoshop schema */
|
||||
schema = gst_xmp_schema_new ();
|
||||
_gst_xmp_schema_add_simple_mapping (schema,
|
||||
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,
|
||||
"photoshop:City", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
|
||||
"photoshop:City", GstXmpTagTypeSimple, NULL, NULL);
|
||||
_gst_xmp_add_schema ("photoshop", schema);
|
||||
|
||||
/* iptc4xmpcore schema */
|
||||
schema = gst_xmp_schema_new ();
|
||||
_gst_xmp_schema_add_simple_mapping (schema,
|
||||
GST_TAG_GEO_LOCATION_SUBLOCATION, "Iptc4xmpCore:Location",
|
||||
GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
|
||||
GstXmpTagTypeSimple, NULL, NULL);
|
||||
_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;
|
||||
}
|
||||
|
||||
|
@ -1004,6 +1050,7 @@ static const GstXmpNamespaceMatch ns_match[] = {
|
|||
{"xap", "http://ns.adobe.com/xap/1.0/"},
|
||||
{"photoshop", "http://ns.adobe.com/photoshop/1.0/"},
|
||||
{"Iptc4xmpCore", "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"},
|
||||
{"Iptc4xmpExt", "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -1017,11 +1064,14 @@ struct _GstXmpNamespaceMap
|
|||
/* parsing */
|
||||
|
||||
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)
|
||||
{
|
||||
GType tag_type;
|
||||
GstTagMergeMode merge_mode;
|
||||
const gchar *tag = xmptag->gst_tag;
|
||||
|
||||
g_return_if_fail (tag != NULL);
|
||||
|
||||
if (xmptag && xmptag->deserialize) {
|
||||
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;
|
||||
gchar *part, *pp;
|
||||
guint i;
|
||||
const gchar *last_tag = NULL;
|
||||
XmpTag *last_xmp_tag = NULL;
|
||||
GSList *pending_tags = NULL;
|
||||
|
||||
/* Used for strucuture xmp tags */
|
||||
XmpTag *context_tag = NULL;
|
||||
|
||||
GstXmpNamespaceMap ns_map[] = {
|
||||
{"dc", NULL},
|
||||
{"exif", NULL},
|
||||
{"tiff", NULL},
|
||||
{"xap", NULL},
|
||||
{"photoshop", NULL},
|
||||
{"Iptc4xmpCore", NULL},
|
||||
{"dc", NULL}
|
||||
,
|
||||
{"exif", NULL}
|
||||
,
|
||||
{"tiff", NULL}
|
||||
,
|
||||
{"xap", NULL}
|
||||
,
|
||||
{"photoshop", NULL}
|
||||
,
|
||||
{"Iptc4xmpCore", NULL}
|
||||
,
|
||||
{"Iptc4xmpExt", 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);
|
||||
|
||||
GST_LOG ("Starting xmp parsing");
|
||||
|
||||
xps = gst_buffer_map (buffer, &len, NULL, GST_MAP_READ);
|
||||
g_return_val_if_fail (len > 0, NULL);
|
||||
|
||||
|
@ -1326,19 +1388,41 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
const gchar *gst_tag;
|
||||
XmpTag *xmp_tag = NULL;
|
||||
/* FIXME: eventualy rewrite ns
|
||||
* find ':'
|
||||
* check if ns before ':' is in ns_map and ns_map[i].gstreamer_ns!=NULL
|
||||
* do 2 stage filter in tag_matches
|
||||
*/
|
||||
gst_tag = _gst_xmp_tag_get_mapping_reverse (as, &xmp_tag);
|
||||
if (gst_tag) {
|
||||
if (context_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;
|
||||
|
||||
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->gst_tag = gst_tag;
|
||||
ptag->xmp_tag = xmp_tag;
|
||||
ptag->str = g_strdup (v);
|
||||
|
||||
|
@ -1365,15 +1449,42 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
|
|||
|
||||
/* skip rdf tags for now */
|
||||
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);
|
||||
if (parttag) {
|
||||
last_tag = parttag;
|
||||
/* check if this is the closing of the context */
|
||||
if (part[0] == '/'
|
||||
&& 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 */
|
||||
ne++;
|
||||
if (ne < xp2) {
|
||||
|
@ -1393,15 +1504,23 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
|
|||
if (ns[0] != '\n' && &ns[1] <= ne) {
|
||||
/* only log non-newline nodes, we still have to parse them */
|
||||
GST_INFO ("txt: %s", part);
|
||||
if (last_tag) {
|
||||
if (last_xmp_tag) {
|
||||
PendingXmpTag *ptag;
|
||||
|
||||
ptag = g_slice_new (PendingXmpTag);
|
||||
ptag->gst_tag = last_tag;
|
||||
ptag->xmp_tag = last_xmp_tag;
|
||||
ptag->str = g_strdup (part);
|
||||
GST_DEBUG ("Found tag %s -> %s", last_xmp_tag->tag_name,
|
||||
last_xmp_tag->gst_tag);
|
||||
|
||||
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 */
|
||||
|
@ -1416,7 +1535,7 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
|
|||
|
||||
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_slice_free (PendingXmpTag, ptag);
|
||||
|
@ -1522,70 +1641,93 @@ gst_value_serialize_xmp (const GValue * value)
|
|||
}
|
||||
|
||||
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;
|
||||
GString *data = serialization_data->data;
|
||||
GPtrArray *xmp_tag_array = NULL;
|
||||
char *s;
|
||||
|
||||
/* map gst-tag to xmp tag */
|
||||
xmp_tag_array = _xmp_tag_get_mapping (tag, serialization_data);
|
||||
/* struct type handled differently */
|
||||
if (xmp_tag->type == GstXmpTagTypeStruct ||
|
||||
xmp_tag->type == GstXmpTagTypeCompound) {
|
||||
GSList *iter;
|
||||
gboolean use_it = FALSE;
|
||||
|
||||
if (!xmp_tag_array) {
|
||||
GST_WARNING ("no mapping for %s to xmp", tag);
|
||||
/* check if any of the inner tags are present on the taglist */
|
||||
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;
|
||||
}
|
||||
|
||||
for (tag_index = 0; tag_index < xmp_tag_array->len; tag_index++) {
|
||||
XmpTag *xmp_tag;
|
||||
/* at this point we must have a gst_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);
|
||||
string_open_tag (data, xmp_tag->tag_name);
|
||||
ct = gst_tag_list_get_tag_size (list, xmp_tag->gst_tag);
|
||||
string_open_tag (data, xmp_tag->tag_name);
|
||||
|
||||
/* fast path for single valued tag */
|
||||
if (ct == 1 || xmp_tag->type == GST_XMP_TAG_TYPE_SIMPLE) {
|
||||
/* fast path for single valued tag */
|
||||
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) {
|
||||
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 {
|
||||
s = gst_value_serialize_xmp (gst_tag_list_get_value_index (list, tag,
|
||||
0));
|
||||
s = gst_value_serialize_xmp (gst_tag_list_get_value_index (list,
|
||||
xmp_tag->gst_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);
|
||||
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, xmp_tag->tag_name);
|
||||
string_close_tag (data, typename);
|
||||
}
|
||||
|
||||
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, "<rdf:Description rdf:about=\"\">\n");
|
||||
|
||||
/* iterate the taglist */
|
||||
gst_tag_list_foreach (list, write_one_tag, &serialization_data);
|
||||
/* iterate the schemas */
|
||||
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 */
|
||||
g_string_append (data, "</rdf:Description>\n");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* GStreamer XmpConfig
|
||||
/* GStreamer TagXmpWriter
|
||||
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* <refsect2>
|
||||
|
|
|
@ -34,6 +34,28 @@ A wide range of video and audio decoders, encoders, and filters are included.
|
|||
</GitRepository>
|
||||
</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>
|
||||
<Version>
|
||||
<revision>0.10.32</revision>
|
||||
|
|
|
@ -928,6 +928,7 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
|
|||
GList *tmp, *tosync = NULL;
|
||||
const GstCaps *format;
|
||||
const GstCaps *restriction;
|
||||
const gchar *missing_element_name;
|
||||
|
||||
format = gst_encoding_profile_get_format (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);
|
||||
scale = gst_element_factory_make ("videoscale", NULL);
|
||||
if (!scale) {
|
||||
missing_element_name = "videoscale";
|
||||
goto missing_element;
|
||||
}
|
||||
/* 4-tap scaling and black borders */
|
||||
g_object_set (scale, "method", 2, "add-borders", TRUE, 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);
|
||||
tosync = g_list_append (tosync, cspace);
|
||||
tosync = g_list_append (tosync, scale);
|
||||
|
@ -1197,6 +1207,11 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
|
|||
if (!gst_encoding_video_profile_get_variableframerate
|
||||
(GST_ENCODING_VIDEO_PROFILE (sprof))) {
|
||||
vrate = gst_element_factory_make ("videorate", NULL);
|
||||
if (!vrate) {
|
||||
missing_element_name = "videorate";
|
||||
goto missing_element;
|
||||
}
|
||||
|
||||
gst_bin_add ((GstBin *) ebin, vrate);
|
||||
tosync = g_list_prepend (tosync, 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);
|
||||
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);
|
||||
aconv2 = gst_element_factory_make ("audioconvert", 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);
|
||||
tosync = g_list_append (tosync, arate);
|
||||
|
@ -1288,6 +1315,15 @@ no_muxer_pad:
|
|||
"Couldn't find a compatible muxer pad to link encoder to");
|
||||
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:
|
||||
GST_ERROR_OBJECT (ebin, "Failed to link the encoder");
|
||||
goto cleanup;
|
||||
|
|
|
@ -18,6 +18,8 @@ libgstplaybin_la_SOURCES = \
|
|||
gststreaminfo.c \
|
||||
gststreamselector.c \
|
||||
gstsubtitleoverlay.c \
|
||||
gstplaysinkvideoconvert.c \
|
||||
gstplaysinkaudioconvert.c \
|
||||
gststreamsynchronizer.c
|
||||
|
||||
nodist_libgstplaybin_la_SOURCES = $(built_sources)
|
||||
|
@ -57,6 +59,8 @@ noinst_HEADERS = \
|
|||
gststreamselector.h \
|
||||
gstrawcaps.h \
|
||||
gstsubtitleoverlay.h \
|
||||
gstplaysinkvideoconvert.h \
|
||||
gstplaysinkaudioconvert.h \
|
||||
gststreamsynchronizer.h
|
||||
|
||||
BUILT_SOURCES = $(built_headers) $(built_sources)
|
||||
|
|
|
@ -170,6 +170,8 @@ struct _GstDecodeBin
|
|||
GList *blocked_pads; /* pads that have set to block */
|
||||
|
||||
gboolean expose_allstreams; /* Whether to expose unknow type streams or not */
|
||||
|
||||
gboolean upstream_seekable; /* if upstream is seekable */
|
||||
};
|
||||
|
||||
struct _GstDecodeBinClass
|
||||
|
@ -216,9 +218,10 @@ enum
|
|||
|
||||
/* automatic sizes, while prerolling we buffer up to 2MB, we ignore time
|
||||
* and buffers in this case. */
|
||||
#define AUTO_PREROLL_SIZE_BYTES 2 * 1024 * 1024
|
||||
#define AUTO_PREROLL_SIZE_BUFFERS 0
|
||||
#define AUTO_PREROLL_SIZE_TIME 0
|
||||
#define AUTO_PREROLL_SIZE_BYTES 2 * 1024 * 1024
|
||||
#define AUTO_PREROLL_SIZE_BUFFERS 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
|
||||
* as low as possible (try to aim for 5 buffers) */
|
||||
|
@ -2042,6 +2045,48 @@ beach:
|
|||
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
|
||||
type_found (GstElement * typefind, guint probability,
|
||||
GstCaps * caps, GstDecodeBin * decode_bin)
|
||||
|
@ -2070,6 +2115,10 @@ type_found (GstElement * typefind, guint probability,
|
|||
pad = gst_element_get_static_pad (typefind, "src");
|
||||
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
|
||||
* 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
|
||||
|
@ -2653,7 +2702,8 @@ decodebin_set_queue_size (GstDecodeBin * dbin, GstElement * multiqueue,
|
|||
if ((max_buffers = dbin->max_size_buffers) == 0)
|
||||
max_buffers = AUTO_PREROLL_SIZE_BUFFERS;
|
||||
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 {
|
||||
/* 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). */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* GStreamer
|
||||
* 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
|
||||
* 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_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
|
||||
*/
|
||||
struct _GstSourceGroup
|
||||
|
@ -319,7 +328,7 @@ struct _GstSourceGroup
|
|||
GList *stream_changed_pending;
|
||||
|
||||
/* 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)
|
||||
|
@ -1089,30 +1098,25 @@ init_group (GstPlayBin * playbin, GstSourceGroup * group)
|
|||
/* 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
|
||||
* NULL terminator (set when the memory is zeroed on allocation) */
|
||||
group->selector[0].media_list[0] = "audio/x-raw-";
|
||||
group->selector[0].type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
|
||||
group->selector[0].channels = group->audio_channels;
|
||||
group->selector[1].media_list[0] = "audio/";
|
||||
group->selector[1].type = GST_PLAY_SINK_TYPE_AUDIO;
|
||||
group->selector[1].channels = group->audio_channels;
|
||||
group->selector[2].media_list[0] = "text/";
|
||||
group->selector[2].media_list[1] = "application/x-subtitle";
|
||||
group->selector[2].media_list[2] = "application/x-ssa";
|
||||
group->selector[2].media_list[3] = "application/x-ass";
|
||||
group->selector[2].media_list[4] = "video/x-dvd-subpicture";
|
||||
group->selector[2].media_list[5] = "subpicture/";
|
||||
group->selector[2].media_list[6] = "subtitle/";
|
||||
group->selector[2].get_media_caps = gst_subtitle_overlay_create_factory_caps;
|
||||
group->selector[2].type = GST_PLAY_SINK_TYPE_TEXT;
|
||||
group->selector[2].channels = group->text_channels;
|
||||
group->selector[3].media_list[0] = "video/x-raw-";
|
||||
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;
|
||||
group->selector[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
|
||||
group->selector[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
|
||||
group->selector[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
|
||||
group->selector[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
|
||||
group->selector[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
|
||||
group->selector[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[4] = "video/x-dvd-subpicture";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
|
||||
group->selector[PLAYBIN_STREAM_TEXT].get_media_caps =
|
||||
gst_subtitle_overlay_create_factory_caps;
|
||||
group->selector[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
|
||||
group->selector[PLAYBIN_STREAM_TEXT].channels = group->text_channels;
|
||||
|
||||
for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) {
|
||||
for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
|
||||
GstSourceSelect *select = &group->selector[n];
|
||||
select->sinkpad_delayed_event = NULL;
|
||||
select->sinkpad_data_probe = 0;
|
||||
|
@ -1124,7 +1128,7 @@ free_group (GstPlayBin * playbin, GstSourceGroup * group)
|
|||
{
|
||||
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];
|
||||
if (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;
|
||||
|
||||
GST_PLAY_BIN_LOCK (playbin);
|
||||
|
||||
GST_DEBUG_OBJECT (playbin, "Changing current video stream %d -> %d",
|
||||
playbin->current_video, stream);
|
||||
|
||||
group = get_group (playbin);
|
||||
if (!(channels = group->video_channels))
|
||||
goto no_channels;
|
||||
|
@ -1536,6 +1544,10 @@ gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
|
|||
GstPad *sinkpad;
|
||||
|
||||
GST_PLAY_BIN_LOCK (playbin);
|
||||
|
||||
GST_DEBUG_OBJECT (playbin, "Changing current audio stream %d -> %d",
|
||||
playbin->current_audio, stream);
|
||||
|
||||
group = get_group (playbin);
|
||||
if (!(channels = group->audio_channels))
|
||||
goto no_channels;
|
||||
|
@ -1651,6 +1663,10 @@ gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
|
|||
GstPad *sinkpad;
|
||||
|
||||
GST_PLAY_BIN_LOCK (playbin);
|
||||
|
||||
GST_DEBUG_OBJECT (playbin, "Changing current text stream %d -> %d",
|
||||
playbin->current_text, stream);
|
||||
|
||||
group = get_group (playbin);
|
||||
if (!(channels = group->text_channels))
|
||||
goto no_channels;
|
||||
|
@ -2313,7 +2329,7 @@ selector_active_pad_changed (GObject * selector, GParamSpec * pspec,
|
|||
GST_PLAY_BIN_LOCK (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)) {
|
||||
select = &group->selector[i];
|
||||
}
|
||||
|
@ -2399,7 +2415,7 @@ array_has_value (const gchar * values[], const gchar * value)
|
|||
gint 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 FALSE;
|
||||
|
@ -2471,7 +2487,7 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
|
|||
GST_DEBUG_PAD_NAME (pad), caps, group);
|
||||
|
||||
/* 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)) {
|
||||
select = &group->selector[i];
|
||||
break;
|
||||
|
@ -2730,7 +2746,7 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
|
|||
GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
|
||||
|
||||
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];
|
||||
|
||||
/* 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_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. */
|
||||
GST_SOURCE_GROUP_LOCK (group);
|
||||
/* 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];
|
||||
|
||||
/* All streamsynchronizer streams should see stream-changed message,
|
||||
|
@ -2866,7 +2878,7 @@ shutdown:
|
|||
* instead of a NOT_LINKED error.
|
||||
*/
|
||||
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];
|
||||
|
||||
if (select->srcpad) {
|
||||
|
@ -3464,7 +3476,7 @@ deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
|
|||
|
||||
GST_SOURCE_GROUP_LOCK (group);
|
||||
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];
|
||||
|
||||
GST_DEBUG_OBJECT (playbin, "unlinking selector %s", select->media_list[0]);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* GStreamer
|
||||
* 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
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -30,6 +31,8 @@
|
|||
|
||||
#include "gstplaysink.h"
|
||||
#include "gststreamsynchronizer.h"
|
||||
#include "gstplaysinkvideoconvert.h"
|
||||
#include "gstplaysinkaudioconvert.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
|
||||
#define GST_CAT_DEFAULT gst_play_sink_debug
|
||||
|
@ -58,7 +61,6 @@ typedef struct
|
|||
GstPad *sinkpad;
|
||||
GstElement *queue;
|
||||
GstElement *conv;
|
||||
GstElement *resample;
|
||||
GstElement *volume; /* element with the volume property */
|
||||
gboolean sink_volume; /* if the volume was provided by the sink */
|
||||
GstElement *mute; /* element with the mute property */
|
||||
|
@ -80,7 +82,6 @@ typedef struct
|
|||
GstPad *sinkpad;
|
||||
GstElement *queue;
|
||||
GstElement *conv;
|
||||
GstElement *scale;
|
||||
GstElement *sink;
|
||||
gboolean async;
|
||||
GstElement *ts_offset;
|
||||
|
@ -149,6 +150,7 @@ struct _GstPlaySink
|
|||
/* audio */
|
||||
GstPad *audio_pad;
|
||||
gboolean audio_pad_raw;
|
||||
gboolean audio_pad_blocked;
|
||||
GstPad *audio_srcpad_stream_synchronizer;
|
||||
GstPad *audio_sinkpad_stream_synchronizer;
|
||||
/* audio tee */
|
||||
|
@ -159,10 +161,12 @@ struct _GstPlaySink
|
|||
/* video */
|
||||
GstPad *video_pad;
|
||||
gboolean video_pad_raw;
|
||||
gboolean video_pad_blocked;
|
||||
GstPad *video_srcpad_stream_synchronizer;
|
||||
GstPad *video_sinkpad_stream_synchronizer;
|
||||
/* text */
|
||||
GstPad *text_pad;
|
||||
gboolean text_pad_blocked;
|
||||
GstPad *text_srcpad_stream_synchronizer;
|
||||
GstPad *text_sinkpad_stream_synchronizer;
|
||||
|
||||
|
@ -191,21 +195,12 @@ struct _GstPlaySinkClass
|
|||
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 =
|
||||
GST_STATIC_PAD_TEMPLATE ("audio_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);
|
||||
static GstStaticPadTemplate videotemplate =
|
||||
GST_STATIC_PAD_TEMPLATE ("video_sink",
|
||||
GST_PAD_SINK,
|
||||
|
@ -216,6 +211,19 @@ static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
|
|||
GST_PAD_REQUEST,
|
||||
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 */
|
||||
enum
|
||||
{
|
||||
|
@ -613,6 +621,7 @@ gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
|
|||
GST_PLAY_SINK_LOCK (playsink);
|
||||
switch (type) {
|
||||
case GST_PLAY_SINK_TYPE_AUDIO:
|
||||
case GST_PLAY_SINK_TYPE_AUDIO_RAW:
|
||||
{
|
||||
GstPlayAudioChain *chain;
|
||||
if ((chain = (GstPlayAudioChain *) playsink->audiochain))
|
||||
|
@ -621,6 +630,7 @@ gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
|
|||
break;
|
||||
}
|
||||
case GST_PLAY_SINK_TYPE_VIDEO:
|
||||
case GST_PLAY_SINK_TYPE_VIDEO_RAW:
|
||||
{
|
||||
GstPlayVideoChain *chain;
|
||||
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
|
||||
* video display.
|
||||
* video display. Only used for *raw* video streams.
|
||||
*
|
||||
* +------------------------------------------------------------+
|
||||
* | vbin |
|
||||
|
@ -1275,46 +1285,19 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
|
|||
head = prev = chain->queue;
|
||||
}
|
||||
|
||||
if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
|
||||
GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
|
||||
chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
|
||||
if (chain->conv == NULL) {
|
||||
post_missing_element_message (playsink, "ffmpegcolorspace");
|
||||
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
|
||||
(_("Missing element '%s' - check your GStreamer installation."),
|
||||
"ffmpegcolorspace"), ("video rendering might fail"));
|
||||
if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
|
||||
GST_DEBUG_OBJECT (playsink, "creating videoconverter");
|
||||
chain->conv =
|
||||
g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
|
||||
gst_bin_add (bin, 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 {
|
||||
gst_bin_add (bin, 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;
|
||||
head = chain->conv;
|
||||
}
|
||||
prev = chain->conv;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
|
@ -1385,13 +1368,12 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
|
|||
|
||||
chain = playsink->videochain;
|
||||
|
||||
chain->chain.raw = raw;
|
||||
|
||||
/* if the chain was active we don't do anything */
|
||||
if (GST_PLAY_CHAIN (chain)->activated == TRUE)
|
||||
return TRUE;
|
||||
|
||||
if (chain->chain.raw != raw)
|
||||
return FALSE;
|
||||
|
||||
/* try to set the sink element to READY again */
|
||||
ret = gst_element_set_state (chain->sink, GST_STATE_READY);
|
||||
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.
|
||||
* Only used for *raw* video streams.
|
||||
*
|
||||
* +--------------------------------------------+
|
||||
* | tbin |
|
||||
|
@ -1764,54 +1747,32 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
|
|||
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");
|
||||
chain->conv = gst_element_factory_make ("audioconvert", "aconv");
|
||||
if (chain->conv == NULL) {
|
||||
post_missing_element_message (playsink, "audioconvert");
|
||||
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
|
||||
(_("Missing element '%s' - check your GStreamer installation."),
|
||||
"audioconvert"), ("possibly a liboil version mismatch?"));
|
||||
chain->conv =
|
||||
g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
|
||||
gst_bin_add (bin, 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 {
|
||||
gst_bin_add (bin, 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;
|
||||
head = chain->conv;
|
||||
}
|
||||
prev = chain->conv;
|
||||
|
||||
GST_DEBUG_OBJECT (playsink, "creating audioresample");
|
||||
chain->resample = gst_element_factory_make ("audioresample", "aresample");
|
||||
if (chain->resample == NULL) {
|
||||
post_missing_element_message (playsink, "audioresample");
|
||||
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;
|
||||
}
|
||||
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
|
||||
!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
|
||||
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!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");
|
||||
chain->volume = gst_element_factory_make ("volume", "volume");
|
||||
if (chain->volume == NULL) {
|
||||
post_missing_element_message (playsink, "volume");
|
||||
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
|
||||
(_("Missing element '%s' - check your GStreamer installation."),
|
||||
"volume"), ("possibly a liboil version mismatch?"));
|
||||
} else {
|
||||
GstPlaySinkAudioConvert *conv =
|
||||
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
|
||||
|
||||
if (conv->volume) {
|
||||
chain->volume = conv->volume;
|
||||
have_volume = TRUE;
|
||||
|
||||
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,
|
||||
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->chain.raw = raw;
|
||||
|
||||
/* if the chain was active we don't do anything */
|
||||
if (GST_PLAY_CHAIN (chain)->activated == TRUE)
|
||||
return TRUE;
|
||||
|
||||
if (chain->chain.raw != raw)
|
||||
return FALSE;
|
||||
|
||||
/* try to set the sink element to READY again */
|
||||
ret = gst_element_set_state (chain->sink, GST_STATE_READY);
|
||||
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_CALLBACK (notify_mute_cb), playsink);
|
||||
}
|
||||
|
||||
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
|
||||
} else {
|
||||
GstPlaySinkAudioConvert *conv =
|
||||
GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
|
||||
|
||||
/* 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");
|
||||
if (!raw) {
|
||||
GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
|
||||
|
||||
disconnect_chain (chain, playsink);
|
||||
chain->volume = NULL;
|
||||
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;
|
||||
}
|
||||
/* Disconnect signals */
|
||||
disconnect_chain (chain, playsink);
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -2864,6 +2820,196 @@ gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
|
|||
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
|
||||
* @playsink: a #GstPlaySink
|
||||
|
@ -2878,7 +3024,6 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
|
|||
{
|
||||
GstPad *res = NULL;
|
||||
gboolean created = FALSE;
|
||||
gboolean raw = FALSE;
|
||||
gboolean activate = TRUE;
|
||||
const gchar *pad_name = NULL;
|
||||
|
||||
|
@ -2887,11 +3032,8 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
|
|||
GST_PLAY_SINK_LOCK (playsink);
|
||||
switch (type) {
|
||||
case GST_PLAY_SINK_TYPE_AUDIO_RAW:
|
||||
pad_name = "audio_raw_sink";
|
||||
raw = TRUE;
|
||||
case GST_PLAY_SINK_TYPE_AUDIO:
|
||||
if (pad_name == NULL)
|
||||
pad_name = "audio_sink";
|
||||
pad_name = "audio_sink";
|
||||
if (!playsink->audio_tee) {
|
||||
GST_LOG_OBJECT (playsink, "creating tee");
|
||||
/* 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");
|
||||
playsink->audio_pad =
|
||||
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;
|
||||
}
|
||||
playsink->audio_pad_raw = raw;
|
||||
playsink->audio_pad_raw = FALSE;
|
||||
res = playsink->audio_pad;
|
||||
break;
|
||||
case GST_PLAY_SINK_TYPE_VIDEO_RAW:
|
||||
pad_name = "video_raw_sink";
|
||||
raw = TRUE;
|
||||
case GST_PLAY_SINK_TYPE_VIDEO:
|
||||
if (pad_name == NULL)
|
||||
pad_name = "video_sink";
|
||||
pad_name = "video_sink";
|
||||
if (!playsink->video_pad) {
|
||||
GST_LOG_OBJECT (playsink, "ghosting videosink");
|
||||
playsink->video_pad =
|
||||
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;
|
||||
}
|
||||
playsink->video_pad_raw = raw;
|
||||
playsink->video_pad_raw = FALSE;
|
||||
res = playsink->video_pad;
|
||||
break;
|
||||
case GST_PLAY_SINK_TYPE_TEXT:
|
||||
|
@ -2970,6 +3113,14 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
|
|||
* element is 'running' */
|
||||
gst_pad_set_active (res, TRUE);
|
||||
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)
|
||||
gst_pad_set_active (res, activate);
|
||||
}
|
||||
|
@ -3026,8 +3177,12 @@ gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
|
|||
GST_PLAY_SINK_LOCK (playsink);
|
||||
if (pad == 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) {
|
||||
res = &playsink->audio_pad;
|
||||
g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
|
||||
playsink);
|
||||
} else if (pad == playsink->text_pad) {
|
||||
res = &playsink->text_pad;
|
||||
} else {
|
||||
|
@ -3208,6 +3363,46 @@ gst_play_sink_change_state (GstElement * element, GstStateChange transition)
|
|||
ret = GST_STATE_CHANGE_ASYNC;
|
||||
break;
|
||||
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:
|
||||
if (playsink->audiochain && playsink->audiochain->sink_volume) {
|
||||
/* remove our links to the mute and volume elements when they were
|
||||
|
|
|
@ -41,11 +41,11 @@ G_BEGIN_DECLS
|
|||
|
||||
/**
|
||||
* GstPlaySinkType:
|
||||
* @GST_PLAY_SINK_TYPE_AUDIO: A non-raw audio pad
|
||||
* @GST_PLAY_SINK_TYPE_AUDIO_RAW: a raw audio pad
|
||||
* @GST_PLAY_SINK_TYPE_VIDEO: a non-raw video pad
|
||||
* @GST_PLAY_SINK_TYPE_VIDEO_RAW: a raw video pad
|
||||
* @GST_PLAY_SINK_TYPE_TEXT: a raw text pad
|
||||
* @GST_PLAY_SINK_TYPE_AUDIO: an audio pad
|
||||
* @GST_PLAY_SINK_TYPE_AUDIO_RAW: a raw audio pad. Deprecated.
|
||||
* @GST_PLAY_SINK_TYPE_VIDEO: a video pad
|
||||
* @GST_PLAY_SINK_TYPE_VIDEO_RAW: a raw video pad. Deprecated.
|
||||
* @GST_PLAY_SINK_TYPE_TEXT: a text pad
|
||||
* @GST_PLAY_SINK_TYPE_LAST: the last type
|
||||
* @GST_PLAY_SINK_TYPE_FLUSHING: a flushing pad, used when shutting down
|
||||
*
|
||||
|
|
516
gst/playback/gstplaysinkaudioconvert.c
Normal file
516
gst/playback/gstplaysinkaudioconvert.c
Normal 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);
|
||||
}
|
89
gst/playback/gstplaysinkaudioconvert.h
Normal file
89
gst/playback/gstplaysinkaudioconvert.h
Normal 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__ */
|
479
gst/playback/gstplaysinkvideoconvert.c
Normal file
479
gst/playback/gstplaysinkvideoconvert.c
Normal 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);
|
||||
}
|
84
gst/playback/gstplaysinkvideoconvert.h
Normal file
84
gst/playback/gstplaysinkvideoconvert.h
Normal 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__ */
|
|
@ -1544,7 +1544,7 @@ gst_subtitle_overlay_src_proxy_chain (GstPad * proxypad, GstBuffer * buffer)
|
|||
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)) {
|
||||
GST_ERROR_OBJECT (self, "Downstream chain error: %s",
|
||||
|
@ -1581,7 +1581,7 @@ gst_subtitle_overlay_src_proxy_event (GstPad * proxypad, GstEvent * event)
|
|||
event = NULL;
|
||||
ret = TRUE;
|
||||
} else {
|
||||
ret = self->src_proxy_event (proxypad, event);
|
||||
ret = gst_proxy_pad_event_default (proxypad, event);
|
||||
event = NULL;
|
||||
}
|
||||
|
||||
|
@ -1619,7 +1619,7 @@ gst_subtitle_overlay_video_sink_setcaps (GstPad * pad, GstCaps * caps)
|
|||
}
|
||||
GST_SUBTITLE_OVERLAY_UNLOCK (self);
|
||||
|
||||
ret = self->video_sink_setcaps (pad, caps);
|
||||
ret = gst_ghost_pad_setcaps_default (pad, caps);
|
||||
|
||||
out:
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
|
@ -1664,7 +1664,7 @@ static GstFlowReturn
|
|||
gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
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) {
|
||||
return ret;
|
||||
|
@ -1697,7 +1697,7 @@ gst_subtitle_overlay_subtitle_sink_chain (GstPad * pad, GstBuffer * buffer)
|
|||
gst_buffer_unref (buffer);
|
||||
return GST_FLOW_OK;
|
||||
} 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)) {
|
||||
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)) {
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1833,7 +1833,7 @@ gst_subtitle_overlay_subtitle_sink_link (GstPad * pad, GstPad * peer)
|
|||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
ret = self->subtitle_sink_link (pad, peer);
|
||||
ret = gst_ghost_pad_link_default (pad, peer);
|
||||
|
||||
gst_object_unref (self);
|
||||
return ret;
|
||||
|
@ -1852,7 +1852,7 @@ gst_subtitle_overlay_subtitle_sink_unlink (GstPad * pad)
|
|||
GST_DEBUG_OBJECT (pad, "Pad unlinking");
|
||||
gst_caps_replace (&self->subcaps, NULL);
|
||||
|
||||
self->subtitle_sink_unlink (pad);
|
||||
gst_ghost_pad_unlink_default (pad);
|
||||
|
||||
GST_SUBTITLE_OVERLAY_LOCK (self);
|
||||
self->subtitle_error = FALSE;
|
||||
|
@ -1924,7 +1924,7 @@ gst_subtitle_overlay_subtitle_sink_event (GstPad * pad, GstEvent * event)
|
|||
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) {
|
||||
GST_DEBUG_OBJECT (pad, "segment event: %" GST_PTR_FORMAT, event);
|
||||
|
@ -1943,8 +1943,6 @@ static void
|
|||
gst_subtitle_overlay_init (GstSubtitleOverlay * self)
|
||||
{
|
||||
GstPadTemplate *templ;
|
||||
GstIterator *it;
|
||||
GValue item = { 0, };
|
||||
GstPad *proxypad = NULL;
|
||||
|
||||
self->lock = g_mutex_new ();
|
||||
|
@ -1952,68 +1950,45 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self)
|
|||
|
||||
templ = gst_static_pad_template_get (&srctemplate);
|
||||
self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
|
||||
it = gst_pad_iterate_internal_links (self->srcpad);
|
||||
if (G_UNLIKELY (!it
|
||||
|| gst_iterator_next (it, &item) != GST_ITERATOR_OK
|
||||
|| ((proxypad = g_value_get_object (&item)) == NULL))) {
|
||||
GST_ERROR_OBJECT (self, "Failed to get proxypad of srcpad");
|
||||
} else {
|
||||
self->src_proxy_event = GST_PAD_EVENTFUNC (proxypad);
|
||||
gst_pad_set_event_function (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);
|
||||
|
||||
proxypad =
|
||||
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->srcpad)));
|
||||
gst_pad_set_event_function (proxypad,
|
||||
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
|
||||
gst_pad_set_chain_function (proxypad,
|
||||
GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
|
||||
gst_object_unref (proxypad);
|
||||
|
||||
gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
|
||||
|
||||
templ = gst_static_pad_template_get (&video_sinktemplate);
|
||||
self->video_sinkpad =
|
||||
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_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_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_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
|
||||
|
||||
proxypad = NULL;
|
||||
it = gst_pad_iterate_internal_links (self->video_sinkpad);
|
||||
if (G_UNLIKELY (!it
|
||||
|| 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);
|
||||
proxypad =
|
||||
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
|
||||
(self->video_sinkpad)));
|
||||
self->video_block_pad = proxypad;
|
||||
g_value_unset (&item);
|
||||
|
||||
gst_element_add_pad (GST_ELEMENT_CAST (self), self->video_sinkpad);
|
||||
|
||||
templ = gst_static_pad_template_get (&subtitle_sinktemplate);
|
||||
self->subtitle_sinkpad =
|
||||
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_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_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_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_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_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_chain));
|
||||
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_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_acceptcaps));
|
||||
|
||||
proxypad = NULL;
|
||||
it = gst_pad_iterate_internal_links (self->subtitle_sinkpad);
|
||||
if (G_UNLIKELY (!it
|
||||
|| 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);
|
||||
proxypad =
|
||||
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
|
||||
(self->subtitle_sinkpad)));
|
||||
self->subtitle_block_pad = proxypad;
|
||||
|
||||
gst_element_add_pad (GST_ELEMENT_CAST (self), self->subtitle_sinkpad);
|
||||
|
|
|
@ -68,26 +68,16 @@ struct _GstSubtitleOverlay
|
|||
gboolean do_async;
|
||||
|
||||
GstPad *srcpad;
|
||||
GstPadEventFunction src_proxy_event;
|
||||
GstPadChainFunction src_proxy_chain;
|
||||
gboolean downstream_chain_error;
|
||||
|
||||
GstPad *video_sinkpad;
|
||||
GstPad *video_block_pad;
|
||||
GstPadSetCapsFunction video_sink_setcaps;
|
||||
GstPadEventFunction video_sink_event;
|
||||
GstPadChainFunction video_sink_chain;
|
||||
gboolean video_sink_blocked;
|
||||
GstSegment video_segment;
|
||||
gint fps_n, fps_d;
|
||||
|
||||
GstPad *subtitle_sinkpad;
|
||||
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;
|
||||
GstSegment subtitle_segment;
|
||||
gboolean subtitle_flush;
|
||||
|
|
|
@ -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_UNLOCK(dec) (g_mutex_unlock(GST_URI_DECODE_BIN_GET_LOCK(dec)))
|
||||
|
||||
typedef struct _GstURIDecodeBinStream
|
||||
{
|
||||
gulong probe_id;
|
||||
guint bitrate;
|
||||
} GstURIDecodeBinStream;
|
||||
|
||||
/**
|
||||
* GstURIDecodeBin
|
||||
*
|
||||
|
@ -91,7 +97,7 @@ struct _GstURIDecodeBin
|
|||
guint have_type_id; /* have-type signal id from typefind */
|
||||
GSList *decodebins;
|
||||
GSList *pending_decodebins;
|
||||
GSList *srcpads;
|
||||
GHashTable *streams;
|
||||
gint numpads;
|
||||
|
||||
/* for dynamic sources */
|
||||
|
@ -930,7 +936,99 @@ source_no_more_pads (GstElement * element, GstURIDecodeBin * 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.
|
||||
*/
|
||||
static void
|
||||
|
@ -940,6 +1038,7 @@ new_decoded_pad_cb (GstElement * element, GstPad * pad, gboolean last,
|
|||
GstPad *newpad;
|
||||
GstPadTemplate *pad_tmpl;
|
||||
gchar *padname;
|
||||
GstURIDecodeBinStream *stream;
|
||||
|
||||
GST_DEBUG_OBJECT (element, "new decoded pad, name: <%s>. Last: %d",
|
||||
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 */
|
||||
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_element_add_pad (GST_ELEMENT_CAST (decoder), newpad);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
source_pad_event_probe (GstPad * pad, GstEvent * event,
|
||||
GstURIDecodeBin * decoder)
|
||||
|
@ -1422,22 +1529,6 @@ remove_decoders (GstURIDecodeBin * bin, gboolean force)
|
|||
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
|
||||
proxy_unknown_type_signal (GstElement * element, GstPad * pad, GstCaps * caps,
|
||||
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 */
|
||||
static void
|
||||
remove_source (GstURIDecodeBin * bin)
|
||||
|
@ -1822,6 +1919,10 @@ remove_source (GstURIDecodeBin * bin)
|
|||
gst_bin_remove (GST_BIN_CAST (bin), bin->typefind);
|
||||
bin->typefind = NULL;
|
||||
}
|
||||
if (bin->streams) {
|
||||
g_hash_table_destroy (bin->streams);
|
||||
bin->streams = NULL;
|
||||
}
|
||||
/* Don't loose the SOURCE flag */
|
||||
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_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,
|
||||
* 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
|
||||
|
@ -2408,14 +2512,12 @@ gst_uri_decode_bin_change_state (GstElement * element,
|
|||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
GST_DEBUG ("paused to ready");
|
||||
remove_decoders (decoder, FALSE);
|
||||
remove_pads (decoder);
|
||||
remove_source (decoder);
|
||||
do_async_done (decoder);
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
GST_DEBUG ("ready to null");
|
||||
remove_decoders (decoder, TRUE);
|
||||
remove_pads (decoder);
|
||||
remove_source (decoder);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -86,6 +86,8 @@ enum
|
|||
#define DEFAULT_SILENT TRUE
|
||||
#define DEFAULT_NEW_PREF 1.0
|
||||
#define DEFAULT_SKIP_TO_FIRST FALSE
|
||||
#define DEFAULT_DROP_ONLY FALSE
|
||||
#define DEFAULT_AVERAGE_PERIOD 0
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -96,7 +98,9 @@ enum
|
|||
ARG_DROP,
|
||||
ARG_SILENT,
|
||||
ARG_NEW_PREF,
|
||||
ARG_SKIP_TO_FIRST
|
||||
ARG_SKIP_TO_FIRST,
|
||||
ARG_DROP_ONLY,
|
||||
ARG_AVERAGE_PERIOD
|
||||
/* FILL ME */
|
||||
};
|
||||
|
||||
|
@ -194,6 +198,33 @@ gst_video_rate_class_init (GstVideoRateClass * klass)
|
|||
gst_element_class_add_pad_template (element_class,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -321,6 +352,8 @@ gst_video_rate_setcaps (GstPad * pad, GstCaps * caps)
|
|||
videorate->out_frame_count = 0;
|
||||
videorate->to_rate_numerator = rate_numerator;
|
||||
videorate->to_rate_denominator = rate_denominator;
|
||||
videorate->wanted_diff = gst_util_uint64_scale_int (GST_SECOND,
|
||||
rate_denominator, rate_numerator);
|
||||
otherpad = videorate->sinkpad;
|
||||
} else {
|
||||
videorate->from_rate_numerator = rate_numerator;
|
||||
|
@ -430,6 +463,7 @@ gst_video_rate_reset (GstVideoRate * videorate)
|
|||
videorate->next_ts = GST_CLOCK_TIME_NONE;
|
||||
videorate->last_ts = GST_CLOCK_TIME_NONE;
|
||||
videorate->discont = TRUE;
|
||||
videorate->average = 0;
|
||||
gst_video_rate_swap_prev (videorate, NULL, 0);
|
||||
|
||||
gst_segment_init (&videorate->segment, GST_FORMAT_TIME);
|
||||
|
@ -463,6 +497,8 @@ gst_video_rate_init (GstVideoRate * videorate)
|
|||
gst_video_rate_reset (videorate);
|
||||
videorate->silent = DEFAULT_SILENT;
|
||||
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_denominator = 0;
|
||||
|
@ -513,8 +549,11 @@ gst_video_rate_flush_prev (GstVideoRate * videorate, gboolean duplicate)
|
|||
GST_BUFFER_DURATION (outbuf) = videorate->next_ts - push_ts;
|
||||
}
|
||||
|
||||
/* adapt for looping, bring back to time in current segment. */
|
||||
GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.base;
|
||||
/* We do not need to update time in VFR (variable frame rate) mode */
|
||||
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,
|
||||
"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;
|
||||
}
|
||||
|
||||
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
|
||||
gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstVideoRate *videorate;
|
||||
GstFlowReturn res = GST_FLOW_OK;
|
||||
GstClockTime intime, in_ts, in_dur;
|
||||
GstClockTime avg_period;
|
||||
gboolean skip = FALSE;
|
||||
|
||||
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)
|
||||
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_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)) {
|
||||
/* new buffer, we expect to output a buffer that matches the first
|
||||
* timestamp in the segment */
|
||||
if (videorate->skip_to_first) {
|
||||
if (videorate->skip_to_first || skip) {
|
||||
videorate->next_ts = intime;
|
||||
videorate->base_ts = in_ts - videorate->segment.start;
|
||||
videorate->out_frame_count = 0;
|
||||
|
@ -871,6 +990,10 @@ gst_video_rate_chain (GstPad * pad, GstBuffer * buffer)
|
|||
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
|
||||
* going into an infinite loop */
|
||||
}
|
||||
|
@ -931,6 +1054,7 @@ gst_video_rate_set_property (GObject * object,
|
|||
{
|
||||
GstVideoRate *videorate = GST_VIDEO_RATE (object);
|
||||
|
||||
GST_OBJECT_LOCK (videorate);
|
||||
switch (prop_id) {
|
||||
case ARG_SILENT:
|
||||
videorate->silent = g_value_get_boolean (value);
|
||||
|
@ -941,10 +1065,17 @@ gst_video_rate_set_property (GObject * object,
|
|||
case ARG_SKIP_TO_FIRST:
|
||||
videorate->skip_to_first = g_value_get_boolean (value);
|
||||
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:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (videorate);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -953,6 +1084,7 @@ gst_video_rate_get_property (GObject * object,
|
|||
{
|
||||
GstVideoRate *videorate = GST_VIDEO_RATE (object);
|
||||
|
||||
GST_OBJECT_LOCK (videorate);
|
||||
switch (prop_id) {
|
||||
case ARG_IN:
|
||||
g_value_set_uint64 (value, videorate->in);
|
||||
|
@ -975,10 +1107,17 @@ gst_video_rate_get_property (GObject * object,
|
|||
case ARG_SKIP_TO_FIRST:
|
||||
g_value_set_boolean (value, videorate->skip_to_first);
|
||||
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:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (videorate);
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
|
|
|
@ -63,6 +63,10 @@ struct _GstVideoRate
|
|||
gboolean discont;
|
||||
guint64 last_ts; /* Timestamp of last input buffer */
|
||||
|
||||
guint64 average_period;
|
||||
GstClockTimeDiff wanted_diff; /* target average diff */
|
||||
GstClockTimeDiff average; /* moving average period */
|
||||
|
||||
/* segment handling */
|
||||
GstSegment segment;
|
||||
|
||||
|
@ -71,6 +75,8 @@ struct _GstVideoRate
|
|||
gboolean silent;
|
||||
gdouble new_pref;
|
||||
gboolean skip_to_first;
|
||||
gboolean drop_only;
|
||||
guint64 average_period_set;
|
||||
};
|
||||
|
||||
struct _GstVideoRateClass
|
||||
|
|
|
@ -129,6 +129,7 @@ check_PROGRAMS = \
|
|||
elements/multifdsink \
|
||||
elements/playbin \
|
||||
elements/playbin2 \
|
||||
elements/playbin2-compressed \
|
||||
$(check_subparse) \
|
||||
elements/videorate \
|
||||
elements/videoscale \
|
||||
|
@ -376,6 +377,9 @@ elements_playbin_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
|
|||
elements_playbin2_LDADD = $(GST_BASE_LIBS) $(LDADD)
|
||||
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_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
|
||||
|
||||
|
|
2531
tests/check/elements/playbin2-compressed.c
Normal file
2531
tests/check/elements/playbin2-compressed.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -925,12 +925,13 @@ tag_list_equals (GstTagList * taglist, GstTagList * taglist2)
|
|||
}
|
||||
|
||||
static void
|
||||
do_xmp_tag_serialization_deserialization (GstTagList * taglist)
|
||||
do_xmp_tag_serialization_deserialization (GstTagList * taglist,
|
||||
const gchar ** schemas)
|
||||
{
|
||||
GstTagList *taglist2;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
do_xmp_tag_serialization_deserialization (taglist);
|
||||
do_xmp_tag_serialization_deserialization (taglist, NULL);
|
||||
gst_tag_list_free (taglist);
|
||||
}
|
||||
|
||||
|
@ -1134,13 +1135,19 @@ GST_END_TEST;
|
|||
|
||||
GST_START_TEST (test_xmp_compound_tags)
|
||||
{
|
||||
const gchar *schemas[] = { "Iptc4xmpExt", NULL };
|
||||
GstTagList *taglist = gst_tag_list_new ();
|
||||
|
||||
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",
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,9 @@ EXTRA_DIST = gstcapslist.h
|
|||
noinst_PROGRAMS = $(examples)
|
||||
|
||||
LDADD = $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
|
||||
$(GST_BASE_LIBS) \
|
||||
$(GST_LIBS)
|
||||
AM_CFLAGS = -I$(top_builddir)/gst-libs \
|
||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||
$(GST_BASE_CFLAGS) \
|
||||
$(GST_CFLAGS)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _GST_PLUGINS_BASE__STDINT_H
|
||||
#define _GST_PLUGINS_BASE__STDINT_H 1
|
||||
#ifndef _GENERATED_STDINT_H
|
||||
#define _GENERATED_STDINT_H "gst-plugins-base 0.10.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 */
|
||||
#define _STDINT_HAVE_STDINT_H 1
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -75,13 +75,13 @@
|
|||
#define GST_MAJORMINOR "0.10"
|
||||
|
||||
/* package name in plugins */
|
||||
#define GST_PACKAGE_NAME "GStreamer Base Plug-ins prerelease"
|
||||
#define GST_PACKAGE_NAME "GStreamer Base Plug-ins git"
|
||||
|
||||
/* package origin */
|
||||
#define GST_PACKAGE_ORIGIN "Unknown package origin"
|
||||
|
||||
/* 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. */
|
||||
#undef G_UDEV_API_IS_SUBJECT_TO_CHANGE
|
||||
|
@ -337,7 +337,7 @@
|
|||
#define PACKAGE_NAME "GStreamer Base Plug-ins"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "GStreamer Base Plug-ins 0.10.32.4"
|
||||
#define PACKAGE_STRING "GStreamer Base Plug-ins 0.10.34.1"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "gst-plugins-base"
|
||||
|
@ -346,7 +346,7 @@
|
|||
#undef PACKAGE_URL
|
||||
|
||||
/* 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 */
|
||||
#ifdef _DEBUG
|
||||
|
@ -377,7 +377,7 @@
|
|||
#undef USE_TREMOLO
|
||||
|
||||
/* 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
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
|
|
|
@ -17,6 +17,8 @@ EXPORTS
|
|||
gst_audio_frame_byte_size
|
||||
gst_audio_frame_length
|
||||
gst_audio_get_channel_positions
|
||||
gst_audio_iec61937_frame_size
|
||||
gst_audio_iec61937_payload
|
||||
gst_audio_is_buffer_framed
|
||||
gst_audio_set_caps_channel_positions_list
|
||||
gst_audio_set_channel_positions
|
||||
|
|
Loading…
Reference in a new issue