From 8da902bb78cb5653f5e4b9b1371b34171f377d8f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 2 Nov 2011 15:31:11 -0400 Subject: [PATCH 01/32] configure: Allow setting GLIB_EXTRA_CFLAGS Similar to gstreamer commit bb2020b1e794210cf7d44c6626122f611016a620 --- common | 2 +- configure.ac | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common b/common index 11f0cd5a3f..e4a9407a51 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 11f0cd5a3fba36f85cf3e434150bfe66b1bf08d4 +Subproject commit e4a9407a51803016bab9918b03e2034981886bdb diff --git a/configure.ac b/configure.ac index 3247887a01..8284534db9 100644 --- a/configure.ac +++ b/configure.ac @@ -902,8 +902,8 @@ AC_SUBST(GST_PLUGINS_BASE_CFLAGS) dnl FIXME: do we want to rename to GST_ALL_* ? dnl add GST_OPTION_CFLAGS, but overridable -GST_CXXFLAGS="$GLIB_CFLAGS $GST_CFLAGS $GLIB_EXTRA_CFLAGS \$(GST_OPTION_CXXFLAGS)" -GST_CFLAGS="$GLIB_CFLAGS $GST_CFLAGS $GLIB_EXTRA_CFLAGS \$(GST_OPTION_CFLAGS)" +GST_CXXFLAGS="$GLIB_CFLAGS $GST_CFLAGS \$(GLIB_EXTRA_CFLAGS) \$(GST_OPTION_CXXFLAGS)" +GST_CFLAGS="$GLIB_CFLAGS $GST_CFLAGS \$(GLIB_EXTRA_CFLAGS) \$(GST_OPTION_CFLAGS)" AC_SUBST(GST_CFLAGS) AC_SUBST(GST_CXXFLAGS) dnl add GCOV libs because libtool strips -fprofile-arcs -ftest-coverage From 308d4b99eac63f781831f85cb804cc0bdfe1033c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 2 Nov 2011 23:33:18 +0000 Subject: [PATCH 02/32] docs: add draft for subtitle overlays to design docs Main purpose is to provide a generic way to make subtitles work on top of non-raw video (vaapi, vdpau, etc.). --- docs/design/draft-subtitle-overlays.txt | 548 ++++++++++++++++++++++++ 1 file changed, 548 insertions(+) create mode 100644 docs/design/draft-subtitle-overlays.txt diff --git a/docs/design/draft-subtitle-overlays.txt b/docs/design/draft-subtitle-overlays.txt new file mode 100644 index 0000000000..ceff5e5e5e --- /dev/null +++ b/docs/design/draft-subtitle-overlays.txt @@ -0,0 +1,548 @@ +=============================================================== + Subtitle overlays, hardware-accelerated decoding and playbin2 +=============================================================== + +Status: EARLY DRAFT / BRAINSTORMING + +The following text will use "playbin" synonymous with "playbin2". + + === 1. Background === + +Subtitles can be muxed in containers or come from an external source. + +Subtitles come in many shapes and colours. Usually they are either +text-based (incl. 'pango markup'), or bitmap-based (e.g. DVD subtitles +and the most common form of DVB subs). Bitmap based subtitles are +usually compressed in some way, like some form of run-length encoding. + +Subtitles are currently decoded and rendered in subtitle-format-specific +overlay elements. These elements have two sink pads (one for raw video +and one for the subtitle format in question) and one raw video source pad. + +They will take care of synchronising the two input streams, and of +decoding and rendering the subtitles on top of the raw video stream. + +Digression: one could theoretically have dedicated decoder/render elements +that output an AYUV or ARGB image, and then let a videomixer element do +the actual overlaying, but this is not very efficient, because it requires +us to allocate and blend whole pictures (1920x1080 AYUV = 8MB, +1280x720 AYUV = 3.6MB, 720x576 AYUV = 1.6MB) even if the overlay region +is only a small rectangle at the bottom. This wastes memory and CPU. +We could do something better by introducing a new format that only +encodes the region(s) of interest, but we don't have such a format yet, and +are not necessarily keen to rewrite this part of the logic in playbin2 +at this point - and we can't change existing elements' behaviour, so would +need to introduce new elements for this. + +Playbin2 supports outputting compressed formats, i.e. it does not +force decoding to a raw format, but is happy to output to a non-raw +format as long as the sink supports that as well. + +In case of certain hardware-accelerated decoding APIs, we will make use +of that functionality. However, the decoder will not output a raw video +format then, but some kind of hardware/API-specific format (in the caps) +and the buffers will reference hardware/API-specific objects that +the hardware/API-specific sink will know how to handle. + + + === 2. The Problem === + +In the case of such hardware-accelerated decoding, the decoder will not +output raw pixels that can easily be manipulated. Instead, it will +output hardware/API-specific objects that can later be used to render +a frame using the same API. + +Even if we could transform such a buffer into raw pixels, we most +likely would want to avoid that, in order to avoid the need to +map the data back into system memory (and then later back to the GPU). +It's much better to upload the much smaller encoded data to the GPU/DSP +and then leave it there until rendered. + +Currently playbin2 only supports subtitles on top of raw decoded video. +It will try to find a suitable overlay element from the plugin registry +based on the input subtitle caps and the rank. (It is assumed that we +will be able to convert any raw video format into any format required +by the overlay using a converter such as ffmpegcolorspace.) + +It will not render subtitles if the video sent to the sink is not +raw YUV or RGB or if conversions have been disabled by setting the +native-video flag on playbin2. + +Subtitle rendering is considered an important feature. Enabling +hardware-accelerated decoding by default should not lead to a major +feature regression in this area. + +This means that we need to support subtitle rendering on top of +non-raw video. + + + === 3. Possible Solutions === + +The goal is to keep knowledge of the subtitle format within the +format-specific GStreamer plugins, and knowledge of any specific +video acceleration API to the GStreamer plugins implementing +that API. We do not want to make the pango/dvbsuboverlay/dvdspu/kate +plugins link to libva/libvdpau/etc. and we do not want to make +the vaapi/vdpau plugins link to all of libpango/libkate/libass etc. + + +Multiple possible solutions come to mind: + + (a) backend-specific overlay elements + + e.g. vaapitextoverlay, vdpautextoverlay, vaapidvdspu, vdpaudvdspu, + vaapidvbsuboverlay, vdpaudvbsuboverlay, etc. + + This assumes the overlay can be done directly on the backend-specific + object passed around. + + The main drawback with this solution is that it leads to a lot of + code duplication and may also lead to uncertainty about distributing + certain duplicated pieces of code. The code duplication is pretty + much unavoidable, since making textoverlay, dvbsuboverlay, dvdspu, + kate, assrender, etc. available in form of base classes to derive + from is not really an option. Similarly, one would not really want + the vaapi/vdpau plugin to depend on a bunch of other libraries + such as libpango, libkate, libtiger, libass, etc. + + One could add some new kind of overlay plugin feature though in + combination with a generic base class of some sort, but in order + to accommodate all the different cases and formats one would end + up with quite convoluted/tricky API. + + (Of course there could also be a GstFancyVideoBuffer that provides + an abstraction for such video accelerated objects and that could + provide an API to add overlays to it in a generic way, but in the + end this is just a less generic variant of (c), and it is not clear + that there are real benefits to a specialised solution vs. a more + generic one). + + + (b) convert backend-specific object to raw pixels and then overlay + + Even where possible technically, this is most likely very + inefficient. + + + (c) attach the overlay data to the backend-specific video frame buffers + in a generic way and do the actual overlaying/blitting later in + backend-specific code such as the video sink (or an accelerated + encoder/transcoder) + + In this case, the actual overlay rendering (i.e. the actual text + rendering or decoding DVD/DVB data into pixels) is done in the + subtitle-format-specific GStreamer plugin. All knowledge about + the subtitle format is contained in the overlay plugin then, + and all knowledge about the video backend in the video backend + specific plugin. + + The main question then is how to get the overlay pixels (and + we will only deal with pixels here) from the overlay element + to the video sink. + + This could be done in multiple ways: One could send custom + events downstream with the overlay data, or one could attach + the overlay data directly to the video buffers in some way. + + Sending inline events has the advantage that is is fairly + transparent to any elements between the overlay element and + the video sink: if an effects plugin creates a new video + buffer for the output, nothing special needs to be done to + maintain the subtitle overlay information, since the overlay + data is not attached to the buffer. However, it slightly + complicates things at the sink, since it would also need to + look for the new event in question instead of just processing + everything in its buffer render function. + + If one attaches the overlay data to the buffer directly, any + element between overlay and video sink that creates a new + video buffer would need to be aware of the overlay data + attached to it and copy it over to the newly-created buffer. + + One would have to do implement a special kind of new query + (e.g. FEATURE query) that is not passed on automatically by + gst_pad_query_default() in order to make sure that all elements + downstream will handle the attached overlay data. (This is only + a problem if we want to also attach overlay data to raw video + pixel buffers; for new non-raw types we can just make it + mandatory and assume support and be done with it; for existing + non-raw types nothing changes anyway if subtitles don't work) + (we need to maintain backwards compatibility for existing raw + video pipelines like e.g.: ..decoder ! suboverlay ! encoder..) + + Even though slightly more work, attaching the overlay information + to buffers seems more intuitive than sending it interleaved as + events. And buffers stored or passed around (e.g. via the + "last-buffer" property in the sink when doing screenshots via + playbin2) always contain all the information needed. + + + (d) create a video/x-raw-*-delta format and use a backend-specific videomixer + + This possibility was hinted at already in the digression in + section 1. It would satisfy the goal of keeping subtitle format + knowledge in the subtitle plugins and video backend knowledge + in the video backend plugin. It would also add a concept that + might be generally useful (think ximagesrc capture with xdamage). + However, it would require adding foorender variants of all the + existing overlay elements, and changing playbin2 to that new + design, which is somewhat intrusive. And given the general + nature of such a new format/API, we would need to take a lot + of care to be able to accommodate all possible use cases when + designing the API, which makes it considerably more ambitious. + Lastly, we would need to write videomixer variants for the + various accelerated video backends as well. + + +Overall (c) appears to be the most promising solution. It is the least +intrusive and should be fairly straight-forward to implement with +reasonable effort, requiring only small changes to existing elements +and requiring no new elements. + +Doing the final overlaying in the sink as opposed to a videomixer +or overlay in the middle of the pipeline has other advantages: + + - if video frames need to be dropped, e.g. for QoS reasons, + we could also skip the actual subtitle overlaying and + possibly the decoding/rendering as well, if the + implementation and API allows for that to be delayed. + + - the sink often knows the actual size of the window/surface/screen + the output video is rendered to. This *may* make it possible to + render the overlay image in a higher resolution than the input + video, solving a long standing issue with pixelated subtitles on + top of low-resolution videos that are then scaled up in the sink. + This would require for the rendering to be delayed of course instead + of just attaching an AYUV/ARGB/RGBA blog of pixels to the video buffer + in the overlay, but that could all be supported. + + - if the video backend / sink has support for high-quality text + rendering (clutter?) we could just pass the text or pango markup + to the sink and let it do the rest (this is unlikely to be + supported in the general case - text and glyph rendering is + hard; also, we don't really want to make up our own text markup + system, and pango markup is probably too limited for complex + karaoke stuff). + + + === 4. API needed === + + (a) Representation of subtitle overlays to be rendered + + We need to pass the overlay pixels from the overlay element to the + sink somehow. Whatever the exact mechanism, let's assume we pass + a refcounted GstVideoOverlayComposition struct or object. + + A composition is made up of one or more overlays/rectangles. + + In the simplest case an overlay rectangle is just a blob of + RGBA/ABGR [FIXME?] or AYUV pixels with positioning info and other + metadata, and there is only one rectangle to render. + + We're keeping the naming generic ("OverlayFoo" rather than + "SubtitleFoo") here, since this might also be handy for + other use cases such as e.g. logo overlays or so. It is not + designed for full-fledged video stream mixing though. + + // Note: don't mind the exact implementation details, they'll be hidden + + // FIXME: might be confusing in 0.11 though since GstXOverlay was + // renamed to GstVideoOverlay in 0.11, but not much we can do, + // maybe we can rename GstVideoOverlay to something better + + struct GstVideoOverlayComposition + { + guint num_rectangles; + GstVideoOverlayRectangle ** rectangles; + + /* lowest rectangle sequence number still used by the upstream + * overlay element. This way a renderer maintaining some kind of + * rectangles <-> surface cache can know when to free cached + * surfaces/rectangles. */ + guint min_seq_num_used; + + /* sequence number for the composition (same series as rectangles) */ + guint seq_num; + } + + struct GstVideoOverlayRectangle + { + /* Position on video frame and dimension of output rectangle in + * output frame terms (already adjusted for the PAR of the output + * frame). x/y can be negative (overlay will be clipped then) */ + gint x, y; + guint render_width, render_height; + + /* Dimensions of overlay pixels */ + guint width, height, stride; + + /* This is the PAR of the overlay pixels */ + guint par_n, par_d; + + /* Format of pixels, GST_VIDEO_FORMAT_ARGB on big-endian systems, + * and BGRA on little-endian systems (i.e. pixels are treated as + * 32-bit values and alpha is always in the most-significant byte, + * and blue is in the least-significant byte). + * + * FIXME: does anyone actually use AYUV in practice? (we do + * in our utility function to blend on top of raw video) + * What about AYUV and endianness? Do we always have [A][Y][U][V] + * in memory? */ + /* FIXME: maybe use our own enum? */ + GstVideoFormat format; + + /* Refcounted blob of memory, no caps or timestamps */ + GstBuffer *pixels; + + // FIXME: how to express source like text or pango markup? + // (just add source type enum + source buffer with data) + // + // FOR 0.10: always send pixel blobs, but attach source data in + // addition (reason: if downstream changes, we can't renegotiate + // that properly, if we just do a query of supported formats from + // the start). Sink will just ignore pixels and use pango markup + // from source data if it supports that. + // + // FOR 0.11: overlay should query formats (pango markup, pixels) + // supported by downstream and then only send that. We can + // renegotiate via the reconfigure event. + // + + /* sequence number: useful for backends/renderers/sinks that want + * to maintain a cache of rectangles <-> surfaces. The value of + * the min_seq_num_used in the composition tells the renderer which + * rectangles have expired. */ + guint seq_num; + + /* FIXME: we also need a (private) way to cache converted/scaled + * pixel blobs */ + } + + (a1) Overlay consumer API: + + How would this work in a video sink that supports scaling of textures: + + gst_foo_sink_render () { + /* assume only one for now */ + if video_buffer has composition: + composition = video_buffer.get_composition() + + for each rectangle in composition: + if rectangle.source_data_type == PANGO_MARKUP + actor = text_from_pango_markup (rectangle.get_source_data()) + else + pixels = rectangle.get_pixels_unscaled (FORMAT_RGBA, ...) + actor = texture_from_rgba (pixels, ...) + + .. position + scale on top of video surface ... + } + + (a2) Overlay producer API: + + e.g. logo or subpicture overlay: got pixels, stuff into rectangle: + + if (logoverlay->cached_composition == NULL) { + comp = composition_new (); + + rect = rectangle_new (format, pixels_buf, + width, height, stride, par_n, par_d, + x, y, render_width, render_height); + + /* composition adds its own ref for the rectangle */ + composition_add_rectangle (comp, rect); + rectangle_unref (rect); + + /* buffer adds its own ref for the composition */ + video_buffer_attach_composition (comp); + + /* we take ownership of the composition and save it for later */ + logoverlay->cached_composition = comp; + } else { + video_buffer_attach_composition (logoverlay->cached_composition); + } + + FIXME: also add some API to modify render position/dimensions of + a rectangle (probably requires creation of new rectangle, unless + we handle writability like with other mini objects). + + (b) Fallback overlay rendering/blitting on top of raw video + + Eventually we want to use this overlay mechanism not only for + hardware-accelerated video, but also for plain old raw video, + either at the sink or in the overlay element directly. + + Apart from the advantages listed earlier in section 3, this + allows us to consolidate a lot of overlaying/blitting code that + is currently repeated in every single overlay element in one + location. This makes it considerably easier to support a whole + range of raw video formats out of the box, add SIMD-optimised + rendering using ORC, or handle corner cases correctly. + + (Note: side-effect of overlaying raw video at the video sink is + that if e.g. a screnshotter gets the last buffer via the last-buffer + property of basesink, it would get an image without the subtitles + on top. This could probably be fixed by re-implementing the + property in GstVideoSink though. Playbin2 could handle this + internally as well). + + void + gst_video_overlay_composition_blend (GstVideoOverlayComposition * comp + GstBuffer * video_buf) + { + guint n; + + g_return_if_fail (gst_buffer_is_writable (video_buf)); + g_return_if_fail (GST_BUFFER_CAPS (video_buf) != NULL); + + ... parse video_buffer caps into BlendVideoFormatInfo ... + + for each rectangle in the composition: { + + if (gst_video_format_is_yuv (video_buf_format)) { + overlay_format = FORMAT_AYUV; + } else if (gst_video_format_is_rgb (video_buf_format)) { + overlay_format = FORMAT_ARGB; + } else { + /* FIXME: grayscale? */ + return; + } + + /* this will scale and convert AYUV<->ARGB if needed */ + pixels = rectangle_get_pixels_scaled (rectangle, overlay_format); + + ... clip output rectangle ... + + __do_blend (video_buf_format, video_buf->data, + overlay_format, pixels->data, + x, y, width, height, stride); + + gst_buffer_unref (pixels); + } + } + + + (c) Flatten all rectangles in a composition + + We cannot assume that the video backend API can handle any + number of rectangle overlays, it's possible that it only + supports one single overlay, in which case we need to squash + all rectangles into one. + + However, we'll just declare this a corner case for now, and + implement it only if someone actually needs it. It's easy + to add later API-wise. Might be a bit tricky if we have + rectangles with different PARs/formats (e.g. subs and a logo), + though we could probably always just use the code from (b) + with a fully transparent video buffer to create a flattened + overlay buffer. + + (d) core API: new FEATURE query + + For 0.10 we need to add a FEATURE query, so the overlay element + can query whether the sink downstream and all elements between + the overlay element and the sink support the new overlay API. + Elements in between need to support it because the render + positions and dimensions need to be updated if the video is + cropped or rescaled, for example. + + In order to ensure that all elements support the new API, + we need to drop the query in the pad default query handler + (so it only succeeds if all elements handle it explicitly). + + Might want two variants of the feature query - one where + all elements in the chain need to support it explicitly + and one where it's enough if some element downstream + supports it. + + In 0.11 this could probably be handled via GstMeta and + ALLOCATION queries (and/or we could simply require + elements to be aware of this API from the start). + + There appears to be no issue with downstream possibly + not being linked yet at the time when an overlay would + want to do such a query. + + +Other considerations: + + - renderers (overlays or sinks) may be able to handle only ARGB or only AYUV + (for most graphics/hw-API it's likely ARGB of some sort, while our + blending utility functions will likely want the same colour space as + the underlying raw video format, which is usually YUV of some sort). + We need to convert where required, and should cache the conversion. + + - renderers may or may not be able to scale the overlay. We need to + do the scaling internally if not (simple case: just horizontal scaling + to adjust for PAR differences; complex case: both horizontal and vertical + scaling, e.g. if subs come from a different source than the video or the + video has been rescaled or cropped between overlay element and sink). + + - renderers may be able to generate (possibly scaled) pixels on demand + from the original data (e.g. a string or RLE-encoded data). We will + ignore this for now, since this functionality can still be added later + via API additions. The most interesting case would be to pass a pango + markup string, since e.g. clutter can handle that natively. + + - renderers may be able to write data directly on top of the video pixels + (instead of creating an intermediary buffer with the overlay which is + then blended on top of the actual video frame), e.g. dvdspu, dvbsuboverlay + + However, in the interest of simplicity, we should probably ignore the + fact that some elements can blend their overlays directly on top of the + video (decoding/uncompressing them on the fly), even more so as it's + not obvious that it's actually faster to decode the same overlay + 70-90 times (say) (ie. ca. 3 seconds of video frames) and then blend + it 70-90 times instead of decoding it once into a temporary buffer + and then blending it directly from there, possibly SIMD-accelerated. + Also, this is only relevant if the video is raw video and not some + hardware-acceleration backend object. + + And ultimately it is the overlay element that decides whether to do + the overlay right there and then or have the sink do it (if supported). + It could decide to keep doing the overlay itself for raw video and + only use our new API for non-raw video. + + - renderers may want to make sure they only upload the overlay pixels once + per rectangle if that rectangle recurs in subsequent frames (as part of + the same composition or a different composition), as is likely. This caching + of e.g. surfaces needs to be done renderer-side and can be accomplished + based on the sequence numbers. The composition contains the lowest + sequence number still in use upstream (an overlay element may want to + cache created compositions+rectangles as well after all to re-use them + for multiple frames), based on that the renderer can expire cached + objects. The caching needs to be done renderer-side because attaching + renderer-specific objects to the rectangles won't work well given the + refcounted nature of rectangles and compositions, making it unpredictable + when a rectangle or composition will be freed or from which thread + context it will be freed. The renderer-specific objects are likely bound + to other types of renderer-specific contexts, and need to be managed + in connection with those. + + - composition/rectangles should internally provide a certain degree of + thread-safety. Multiple elements (sinks, overlay element) might access + or use the same objects from multiple threads at the same time, and it + is expected that elements will keep a ref to compositions and rectangles + they push downstream for a while, e.g. until the current subtitle + composition expires. + + === 5. Future considerations === + + - alternatives: there may be multiple versions/variants of the same subtitle + stream. On DVDs, there may be a 4:3 version and a 16:9 version of the same + subtitles. We could attach both variants and let the renderer pick the best + one for the situation (currently we just use the 16:9 version). With totem, + it's ultimately totem that adds the 'black bars' at the top/bottom, so totem + also knows if it's got a 4:3 display and can/wants to fit 4:3 subs (which + may render on top of the bars) or not, for example. + + === 6. Misc. FIXMEs === + +TEST: should these look (roughly) alike (note text distortion) - needs fixing in textoverlay + +gst-launch-0.10 \ + videotestsrc ! video/x-raw-yuv,width=640,height=480,pixel-aspect-ratio=1/1 ! textoverlay text=Hello font-desc=72 ! xvimagesink \ + videotestsrc ! video/x-raw-yuv,width=320,height=480,pixel-aspect-ratio=2/1 ! textoverlay text=Hello font-desc=72 ! xvimagesink \ + videotestsrc ! video/x-raw-yuv,width=640,height=240,pixel-aspect-ratio=1/2 ! textoverlay text=Hello font-desc=72 ! xvimagesink + + ~~~ THE END ~~~ + From 41dc3033d42026478b3756de59f62e12f2070de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 28 Oct 2011 10:07:42 +0200 Subject: [PATCH 03/32] oggmux: Remove obsolete #include --- ext/ogg/gstoggmux.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 58dd85f807..186e87d55f 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -40,7 +40,6 @@ #endif #include -#include #include #include From a583b63722d2397914ad2e774df0ea6ba2b9d8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 3 Nov 2011 09:56:14 +0100 Subject: [PATCH 04/32] Revert "playsinkaudioconvert: Fix warning when there is no target pad yet" This reverts commit f35c51c14915729f0fdf2b348f351ea7e81027cc. Better patch coming soon. --- gst/playback/gstplaysinkaudioconvert.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 2441bfe261..2c28c6eca0 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -348,18 +348,13 @@ 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 = NULL; + GstPad *otherpad, *peer; GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - if (otherpad) { - peer = gst_pad_get_peer (otherpad); - gst_object_unref (otherpad); - otherpad = NULL; - } - + peer = gst_pad_get_peer (otherpad); if (peer) { ret = gst_pad_get_caps_reffed (peer); gst_object_unref (peer); @@ -367,6 +362,7 @@ gst_play_sink_audio_convert_getcaps (GstPad * pad) ret = gst_caps_new_any (); } + gst_object_unref (otherpad); gst_object_unref (self); return ret; From 2b84b328b1cac2c083fb322f4abb91bf439fb944 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 7 Oct 2011 11:16:44 +0000 Subject: [PATCH 05/32] playsink: handle NULL ghost pad target For the src pad anyway. https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkaudioconvert.c | 22 ++++++++++++++++------ gst/playback/gstplaysinkvideoconvert.c | 21 ++++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 2c28c6eca0..d31898d723 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -352,17 +352,27 @@ gst_play_sink_audio_convert_getcaps (GstPad * pad) GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); + if (!otherpad) { + if (pad == self->srcpad) { + otherpad = self->sink_proxypad; + } + /* no equivalent for the sink pad */ + } 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); + if (otherpad) { + 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); } else { + GST_WARNING_OBJECT (self, "Could not traverse bin"); ret = gst_caps_new_any (); } - - gst_object_unref (otherpad); gst_object_unref (self); return ret; diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index 8b22a644e6..d003545e7b 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -332,17 +332,28 @@ gst_play_sink_video_convert_getcaps (GstPad * pad) GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); + if (!otherpad) { + if (pad == self->srcpad) { + otherpad = self->sink_proxypad; + } + /* no equivalent for the sink pad */ + } 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); + if (otherpad) { + 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); } else { + GST_WARNING_OBJECT (self, "Could not traverse bin"); ret = gst_caps_new_any (); } - gst_object_unref (otherpad); gst_object_unref (self); return ret; From c8e0d215cbaf9ffc0713fe8c060b52af7ffff958 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 13:00:05 +0000 Subject: [PATCH 06/32] playsink: fix passthrough mode (hopefully) The code was doing counterintuitive rewiring of pads when the bin did not contain any elements. We now add an identity element in that case, which makes it simpler, and should fix the AC3 passthrough mode when using pulseaudio (but I don't see the bug here so can't test). https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkaudioconvert.c | 102 +++++++++++++++++-------- gst/playback/gstplaysinkaudioconvert.h | 1 + gst/playback/gstplaysinkvideoconvert.c | 98 +++++++++++++++++------- gst/playback/gstplaysinkvideoconvert.h | 1 + 4 files changed, 140 insertions(+), 62 deletions(-) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index d31898d723..9169c81da8 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -94,12 +94,45 @@ distribute_running_time (GstElement * element, const GstSegment * segment) gst_object_unref (pad); } +static void +gst_play_sink_audio_convert_add_identity (GstPlaySinkAudioConvert * self) +{ + self->identity = gst_element_factory_make ("identity", "identity"); + if (self->identity == NULL) { + post_missing_element_message (self, "identity"); + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + "identity"), ("audio rendering might fail")); + } else { + gst_bin_add (GST_BIN_CAST (self), self->identity); + gst_element_sync_state_with_parent (self->identity); + distribute_running_time (self->identity, &self->segment); + } +} + +static void +gst_play_sink_audio_convert_set_targets (GstPlaySinkAudioConvert * self, + GstElement * head, GstElement * tail) +{ + GstPad *pad; + + pad = gst_element_get_static_pad (head, "sink"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); + gst_object_unref (pad); + + pad = gst_element_get_static_pad (tail, "src"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); + gst_object_unref (pad); +} + static void pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self) { GstPad *peer; GstCaps *caps; gboolean raw; + GstElement *head = NULL, *prev = NULL; + GstBin *bin = GST_BIN_CAST (self); GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); self->sink_proxypad_blocked = blocked; @@ -123,10 +156,6 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self) 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); @@ -181,26 +210,8 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self) 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"); @@ -224,12 +235,20 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self) } } - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), - self->sink_proxypad); - GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); } + g_assert ((head != NULL) == (prev != NULL)); + + /* to make things simple and avoid counterintuitive pad juggling, + ensure there is at least one element in the list */ + if (!head) { + gst_play_sink_audio_convert_add_identity (self); + prev = head = self->identity; + } + + gst_play_sink_audio_convert_set_targets (self, head, prev); + unblock: gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), @@ -243,8 +262,12 @@ 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); + + /* use a simple identity, better than nothing */ + gst_play_sink_audio_convert_add_identity (self); + gst_play_sink_audio_convert_set_targets (self, self->identity, + self->identity); + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), (GDestroyNotify) gst_object_unref); @@ -355,8 +378,13 @@ gst_play_sink_audio_convert_getcaps (GstPad * pad) if (!otherpad) { if (pad == self->srcpad) { otherpad = self->sink_proxypad; + } else if (pad == self->sinkpad) { + otherpad = + GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD + (self->sinkpad))); + } else { + GST_ERROR_OBJECT (pad, "Not one of our pads"); } - /* no equivalent for the sink pad */ } GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); @@ -436,8 +464,11 @@ gst_play_sink_audio_convert_change_state (GstElement * element, gst_bin_remove (GST_BIN_CAST (self), self->volume); } } - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), - self->sink_proxypad); + if (!self->identity) { + gst_play_sink_audio_convert_add_identity (self); + } + gst_play_sink_audio_convert_set_targets (self, self->identity, + self->identity); self->raw = FALSE; GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); break; @@ -448,6 +479,14 @@ gst_play_sink_audio_convert_change_state (GstElement * element, (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), (GDestroyNotify) gst_object_unref); GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (self->identity) { + gst_element_set_state (self->identity, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), self->identity); + self->identity = NULL; + } + break; default: break; } @@ -512,9 +551,6 @@ gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self) 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. diff --git a/gst/playback/gstplaysinkaudioconvert.h b/gst/playback/gstplaysinkaudioconvert.h index e7d48bc711..e9c0ab39ac 100644 --- a/gst/playback/gstplaysinkaudioconvert.h +++ b/gst/playback/gstplaysinkaudioconvert.h @@ -71,6 +71,7 @@ struct _GstPlaySinkAudioConvert gboolean raw; GstElement *conv, *resample; + GstElement *identity; /* < pseudo public > */ GstElement *volume; diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index d003545e7b..96f9e620f3 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -94,12 +94,45 @@ distribute_running_time (GstElement * element, const GstSegment * segment) gst_object_unref (pad); } +static void +gst_play_sink_video_convert_add_identity (GstPlaySinkVideoConvert * self) +{ + self->identity = gst_element_factory_make ("identity", "identity"); + if (self->identity == NULL) { + post_missing_element_message (self, "identity"); + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + "identity"), ("video rendering might fail")); + } else { + gst_bin_add (GST_BIN_CAST (self), self->identity); + gst_element_sync_state_with_parent (self->identity); + distribute_running_time (self->identity, &self->segment); + } +} + +static void +gst_play_sink_video_convert_set_targets (GstPlaySinkVideoConvert * self, + GstElement * head, GstElement * tail) +{ + GstPad *pad; + + pad = gst_element_get_static_pad (head, "sink"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); + gst_object_unref (pad); + + pad = gst_element_get_static_pad (tail, "src"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); + gst_object_unref (pad); +} + static void pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self) { GstPad *peer; GstCaps *caps; gboolean raw; + GstBin *bin = GST_BIN_CAST (self); + GstElement *head = NULL, *prev = NULL; GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); self->sink_proxypad_blocked = blocked; @@ -123,9 +156,6 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self) 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"); @@ -167,26 +197,8 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self) 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"); @@ -204,12 +216,20 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self) 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"); } + g_assert ((head != NULL) == (prev != NULL)); + + /* to make things simple and avoid counterintuitive pad juggling, + ensure there is at least one element in the list */ + if (!head) { + gst_play_sink_video_convert_add_identity (self); + prev = head = self->identity; + } + + gst_play_sink_video_convert_set_targets (self, head, prev); + unblock: gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), @@ -223,8 +243,12 @@ 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); + + /* use a simple identity, better than nothing */ + gst_play_sink_video_convert_add_identity (self); + gst_play_sink_video_convert_set_targets (self, self->identity, + self->identity); + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), (GDestroyNotify) gst_object_unref); @@ -335,8 +359,13 @@ gst_play_sink_video_convert_getcaps (GstPad * pad) if (!otherpad) { if (pad == self->srcpad) { otherpad = self->sink_proxypad; + } else if (pad == self->sinkpad) { + otherpad = + GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD + (self->sinkpad))); + } else { + GST_ERROR_OBJECT (pad, "Not one of our pads"); } - /* no equivalent for the sink pad */ } GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); @@ -408,8 +437,11 @@ gst_play_sink_video_convert_change_state (GstElement * element, 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); + if (!self->identity) { + gst_play_sink_video_convert_add_identity (self); + } + gst_play_sink_video_convert_set_targets (self, self->identity, + self->identity); self->raw = FALSE; GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); break; @@ -420,6 +452,14 @@ gst_play_sink_video_convert_change_state (GstElement * element, (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), (GDestroyNotify) gst_object_unref); GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (self->identity) { + gst_element_set_state (self->identity, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), self->identity); + self->identity = NULL; + } + break; default: break; } diff --git a/gst/playback/gstplaysinkvideoconvert.h b/gst/playback/gstplaysinkvideoconvert.h index 83b3b1edca..10027ee420 100644 --- a/gst/playback/gstplaysinkvideoconvert.h +++ b/gst/playback/gstplaysinkvideoconvert.h @@ -71,6 +71,7 @@ struct _GstPlaySinkVideoConvert gboolean raw; GstElement *conv, *scale; + GstElement *identity; }; struct _GstPlaySinkVideoConvertClass From fd27e34582fb8785e741785e265380497bf15e14 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 16:55:30 +0000 Subject: [PATCH 07/32] playsink: refactor the converter bins since they are almost identical https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/Makefile.am | 2 + gst/playback/gstplaysinkaudioconvert.c | 496 ++-------------------- gst/playback/gstplaysinkaudioconvert.h | 35 +- gst/playback/gstplaysinkconvertbin.c | 544 +++++++++++++++++++++++++ gst/playback/gstplaysinkconvertbin.h | 96 +++++ gst/playback/gstplaysinkvideoconvert.c | 487 ++-------------------- gst/playback/gstplaysinkvideoconvert.h | 34 +- 7 files changed, 705 insertions(+), 989 deletions(-) create mode 100644 gst/playback/gstplaysinkconvertbin.c create mode 100644 gst/playback/gstplaysinkconvertbin.h diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index 9c8273f15c..3ee27e4183 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -22,6 +22,7 @@ libgstplaybin_la_SOURCES = \ gstsubtitleoverlay.c \ gstplaysinkvideoconvert.c \ gstplaysinkaudioconvert.c \ + gstplaysinkconvertbin.c \ gststreamsynchronizer.c nodist_libgstplaybin_la_SOURCES = $(built_sources) @@ -63,6 +64,7 @@ noinst_HEADERS = \ gstsubtitleoverlay.h \ gstplaysinkvideoconvert.h \ gstplaysinkaudioconvert.h \ + gstplaysinkconvertbin.h \ gststreamsynchronizer.h BUILT_SOURCES = $(built_headers) $(built_sources) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 9169c81da8..b4709397f0 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -32,378 +32,51 @@ GST_DEBUG_CATEGORY_STATIC (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); + GST_TYPE_PLAY_SINK_CONVERT_BIN); static gboolean -is_raw_caps (GstCaps * caps) +gst_play_sink_audio_convert_add_conversion_elements (GstPlaySinkConvertBin * + cbin) { - gint i, n; - GstStructure *s; - const gchar *name; + GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT (cbin); + GstElement *el, *prev = NULL; - 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; - } + g_assert (cbin->conversion_elements == NULL); - 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_send_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_send_event (pad, event); - - gst_object_unref (pad); -} - -static void -gst_play_sink_audio_convert_add_identity (GstPlaySinkAudioConvert * self) -{ - self->identity = gst_element_factory_make ("identity", "identity"); - if (self->identity == NULL) { - post_missing_element_message (self, "identity"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "identity"), ("audio rendering might fail")); - } else { - gst_bin_add (GST_BIN_CAST (self), self->identity); - gst_element_sync_state_with_parent (self->identity); - distribute_running_time (self->identity, &self->segment); - } -} - -static void -gst_play_sink_audio_convert_set_targets (GstPlaySinkAudioConvert * self, - GstElement * head, GstElement * tail) -{ - GstPad *pad; - - pad = gst_element_get_static_pad (head, "sink"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (tail, "src"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); - gst_object_unref (pad); -} - -static void -pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self) -{ - GstPad *peer; - GstCaps *caps; - gboolean raw; - GstElement *head = NULL, *prev = NULL; - GstBin *bin = GST_BIN_CAST (self); - - 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) { - 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_converters) { + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + "audioconvert", "conv"); + if (el) { + prev = el; } - 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); + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + "audioresample", "resample"); + if (el) { + if (prev) { - if (!gst_element_link_pads_full (prev, "src", self->volume, "sink", + if (!gst_element_link_pads_full (prev, "src", el, "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) goto link_failed; - } else { - head = self->volume; } - prev = self->volume; + prev = el; } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); - } else { - - 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_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); } - g_assert ((head != NULL) == (prev != NULL)); - - /* to make things simple and avoid counterintuitive pad juggling, - ensure there is at least one element in the list */ - if (!head) { - gst_play_sink_audio_convert_add_identity (self); - prev = head = self->identity; + if (self->use_volume && self->volume) { + el = self->volume; + gst_play_sink_convert_bin_add_conversion_element (cbin, el); + if (prev) { + if (!gst_element_link_pads_full (prev, "src", el, "sink", + GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) + goto link_failed; + } + prev = el; } - - gst_play_sink_audio_convert_set_targets (self, head, prev); - -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; + return TRUE; link_failed: - { - GST_ELEMENT_ERROR (self, CORE, PAD, - (NULL), ("Failed to configure the audio converter.")); - - /* use a simple identity, better than nothing */ - gst_play_sink_audio_convert_add_identity (self); - gst_play_sink_audio_convert_set_targets (self, self->identity, - self->identity); - - 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); - 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); - otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); - if (!otherpad) { - if (pad == self->srcpad) { - otherpad = self->sink_proxypad; - } else if (pad == self->sinkpad) { - otherpad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD - (self->sinkpad))); - } else { - GST_ERROR_OBJECT (pad, "Not one of our pads"); - } - } - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - - if (otherpad) { - 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); - } else { - GST_WARNING_OBJECT (self, "Could not traverse bin"); - ret = gst_caps_new_any (); - } - gst_object_unref (self); - - return ret; + return FALSE; } static void @@ -414,86 +87,9 @@ gst_play_sink_audio_convert_finalize (GObject * 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); - } - } - if (!self->identity) { - gst_play_sink_audio_convert_add_identity (self); - } - gst_play_sink_audio_convert_set_targets (self, self->identity, - self->identity); - 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); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (self->identity) { - gst_element_set_state (self->identity, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->identity); - self->identity = NULL; - } - break; - default: - break; - } - - return ret; -} - static void gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass) { @@ -508,48 +104,20 @@ gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * 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 "); - - 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; + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); - 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); + cbin->audio = TRUE; + cbin->add_conversion_elements = + gst_play_sink_audio_convert_add_conversion_elements; /* FIXME: Only create this on demand but for now we need * it to always exist because of playsink's volume proxying diff --git a/gst/playback/gstplaysinkaudioconvert.h b/gst/playback/gstplaysinkaudioconvert.h index e9c0ab39ac..5f80af67b4 100644 --- a/gst/playback/gstplaysinkaudioconvert.h +++ b/gst/playback/gstplaysinkaudioconvert.h @@ -18,6 +18,7 @@ */ #include +#include "gstplaysinkconvertbin.h" #ifndef __GST_PLAY_SINK_AUDIO_CONVERT_H__ #define __GST_PLAY_SINK_AUDIO_CONVERT_H__ @@ -36,42 +37,12 @@ G_BEGIN_DECLS #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; - GstElement *identity; + GstPlaySinkConvertBin parent; /* < pseudo public > */ GstElement *volume; @@ -81,7 +52,7 @@ struct _GstPlaySinkAudioConvert struct _GstPlaySinkAudioConvertClass { - GstBinClass parent; + GstPlaySinkConvertBinClass parent; }; GType gst_play_sink_audio_convert_get_type (void); diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c new file mode 100644 index 0000000000..cfd8dc8b79 --- /dev/null +++ b/gst/playback/gstplaysinkconvertbin.c @@ -0,0 +1,544 @@ +/* GStreamer + * Copyright (C) <2011> Sebastian Dröge + * Copyright (C) <2011> Vincent Penquerch + * + * 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 "gstplaysinkconvertbin.h" + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (gst_play_sink_convert_bin_debug); +#define GST_CAT_DEFAULT gst_play_sink_convert_bin_debug + +#define parent_class gst_play_sink_convert_bin_parent_class + +G_DEFINE_TYPE (GstPlaySinkConvertBin, gst_play_sink_convert_bin, 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") + && !g_str_has_prefix (name, "video/x-raw")) + return FALSE; + } + + return TRUE; +} + +static void +gst_play_sink_convert_bin_post_missing_element_message (GstPlaySinkConvertBin * + 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_send_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_send_event (pad, event); + + gst_object_unref (pad); +} + +void +gst_play_sink_convert_bin_add_conversion_element (GstPlaySinkConvertBin * self, + GstElement * el) +{ + self->conversion_elements = g_list_append (self->conversion_elements, el); + gst_bin_add (GST_BIN (self), gst_object_ref (el)); +} + +GstElement * +gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin + * self, const char *factory, const char *name) +{ + GstElement *el; + + el = gst_element_factory_make (factory, name); + if (el == NULL) { + gst_play_sink_convert_bin_post_missing_element_message (self, factory); + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + factory), + (self->audio ? "audio rendering might fail" : + "video rendering might fail")); + } else { + gst_play_sink_convert_bin_add_conversion_element (self, el); + } + return el; +} + +static void +gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self) +{ + self->identity = gst_element_factory_make ("identity", "identity"); + if (self->identity == NULL) { + gst_play_sink_convert_bin_post_missing_element_message (self, "identity"); + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + "identity"), (self->audio ? + "audio rendering might fail" : "video rendering might fail") + + ); + } else { + gst_bin_add (GST_BIN_CAST (self), self->identity); + gst_element_sync_state_with_parent (self->identity); + distribute_running_time (self->identity, &self->segment); + } +} + +static void +gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self) +{ + GstPad *pad; + GstElement *head, *tail; + + if (self->conversion_elements == NULL) { + head = tail = self->identity; + } else { + head = GST_ELEMENT (g_list_first (self->conversion_elements)->data); + tail = GST_ELEMENT (g_list_last (self->conversion_elements)->data); + } + + pad = gst_element_get_static_pad (head, "sink"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); + gst_object_unref (pad); + + pad = gst_element_get_static_pad (tail, "src"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); + gst_object_unref (pad); +} + +static void +gst_play_sink_convert_bin_remove_element (GstElement * element, + GstPlaySinkConvertBin * self) +{ + gst_element_set_state (element, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), element); +} + +static void +gst_play_sink_convert_bin_on_element_added (GstElement * element, + GstPlaySinkConvertBin * self) +{ + gst_element_sync_state_with_parent (element); + distribute_running_time (element, &self->segment); +} + +static void +pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkConvertBin * self) +{ + GstPad *peer; + GstCaps *caps; + gboolean raw; + + GST_PLAY_SINK_CONVERT_BIN_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) { + 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); + + g_assert (self->add_conversion_elements); + if (!(*self->add_conversion_elements) (self)) { + goto link_failed; + } + if (self->conversion_elements) + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_on_element_added, self); + + GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); + } else { + + 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->conversion_elements) { + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_remove_element, self); + g_list_free (self->conversion_elements); + self->conversion_elements = NULL; + } + + GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); + } + + /* to make things simple and avoid counterintuitive pad juggling, + ensure there is at least one element in the list */ + if (!self->conversion_elements) { + gst_play_sink_convert_bin_add_identity (self); + } + + gst_play_sink_convert_bin_set_targets (self); + +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_CONVERT_BIN_UNLOCK (self); + return; + +link_failed: + { + GST_ELEMENT_ERROR (self, CORE, PAD, + (NULL), ("Failed to configure the converter bin.")); + + /* use a simple identity, better than nothing */ + gst_play_sink_convert_bin_add_identity (self); + gst_play_sink_convert_bin_set_targets (self); + + 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_CONVERT_BIN_UNLOCK (self); + return; + } +} + +static gboolean +gst_play_sink_convert_bin_sink_event (GstPad * pad, GstEvent * event) +{ + GstPlaySinkConvertBin *self = + GST_PLAY_SINK_CONVERT_BIN (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_CONVERT_BIN_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_CONVERT_BIN_UNLOCK (self); + } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + GST_DEBUG_OBJECT (self, "Resetting segment"); + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + } + + gst_event_unref (event); + gst_object_unref (self); + + return ret; +} + +static gboolean +gst_play_sink_convert_bin_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstPlaySinkConvertBin *self = + GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); + gboolean ret; + GstStructure *s; + const gchar *name; + gboolean reconfigure = FALSE; + gboolean raw; + + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + s = gst_caps_get_structure (caps, 0); + name = gst_structure_get_name (s); + + if (self->audio) { + raw = g_str_has_prefix (name, "audio/x-raw-"); + } else { + raw = g_str_has_prefix (name, "video/x-raw-"); + } + + if (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_CONVERT_BIN_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_convert_bin_getcaps (GstPad * pad) +{ + GstPlaySinkConvertBin *self = + GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); + GstCaps *ret; + GstPad *otherpad, *peer; + + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); + if (!otherpad) { + if (pad == self->srcpad) { + otherpad = self->sink_proxypad; + } else if (pad == self->sinkpad) { + otherpad = + GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD + (self->sinkpad))); + } else { + GST_ERROR_OBJECT (pad, "Not one of our pads"); + } + } + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + + if (otherpad) { + 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); + } else { + GST_WARNING_OBJECT (self, "Could not traverse bin"); + ret = gst_caps_new_any (); + } + gst_object_unref (self); + + return ret; +} + +static void +gst_play_sink_convert_bin_finalize (GObject * object) +{ + GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_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_convert_bin_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret; + GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (element); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_PLAY_SINK_CONVERT_BIN_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_CONVERT_BIN_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_CONVERT_BIN_LOCK (self); + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + if (self->conversion_elements) { + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_remove_element, self); + g_list_free (self->conversion_elements); + self->conversion_elements = NULL; + } + if (!self->identity) { + gst_play_sink_convert_bin_add_identity (self); + } + gst_play_sink_convert_bin_set_targets (self); + self->raw = FALSE; + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_PLAY_SINK_CONVERT_BIN_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_CONVERT_BIN_UNLOCK (self); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (self->identity) { + gst_element_set_state (self->identity, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), self->identity); + self->identity = NULL; + } + break; + default: + break; + } + + return ret; +} + +static void +gst_play_sink_convert_bin_class_init (GstPlaySinkConvertBinClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + GST_DEBUG_CATEGORY_INIT (gst_play_sink_convert_bin_debug, + "playsinkconvertbin", 0, "play bin"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_play_sink_convert_bin_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 Converter Bin", "Bin/Converter", + "Convenience bin for audio/video conversion", + "Sebastian Dröge "); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_change_state); +} + +static void +gst_play_sink_convert_bin_init (GstPlaySinkConvertBin * 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_convert_bin_sink_event)); + gst_pad_set_setcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_sink_setcaps)); + gst_pad_set_getcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_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_convert_bin_getcaps)); + gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad); + gst_object_unref (templ); +} diff --git a/gst/playback/gstplaysinkconvertbin.h b/gst/playback/gstplaysinkconvertbin.h new file mode 100644 index 0000000000..2665651973 --- /dev/null +++ b/gst/playback/gstplaysinkconvertbin.h @@ -0,0 +1,96 @@ +/* GStreamer + * Copyright (C) <2011> Sebastian Dröge + * Copyright (C) <2011> Vincent Penquerc'h + * + * 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 + +#ifndef __GST_PLAY_SINK_CONVERT_BIN_H__ +#define __GST_PLAY_SINK_CONVERT_BIN_H__ + +G_BEGIN_DECLS +#define GST_TYPE_PLAY_SINK_CONVERT_BIN \ + (gst_play_sink_convert_bin_get_type()) +#define GST_PLAY_SINK_CONVERT_BIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY_SINK_CONVERT_BIN, GstPlaySinkConvertBin)) +#define GST_PLAY_SINK_CONVERT_BIN_CAST(obj) \ + ((GstPlaySinkConvertBin *) obj) +#define GST_PLAY_SINK_CONVERT_BIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY_SINK_CONVERT_BIN, GstPlaySinkConvertBinClass)) +#define GST_IS_PLAY_SINK_CONVERT_BIN(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY_SINK_CONVERT_BIN)) +#define GST_IS_PLAY_SINK_CONVERT_BIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_CONVERT_BIN)) + +#define GST_PLAY_SINK_CONVERT_BIN_LOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "locking from thread %p", \ + g_thread_self ()); \ + g_mutex_lock (GST_PLAY_SINK_CONVERT_BIN_CAST(obj)->lock); \ + GST_LOG_OBJECT (obj, \ + "locked from thread %p", \ + g_thread_self ()); \ +} G_STMT_END + +#define GST_PLAY_SINK_CONVERT_BIN_UNLOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "unlocking from thread %p", \ + g_thread_self ()); \ + g_mutex_unlock (GST_PLAY_SINK_CONVERT_BIN_CAST(obj)->lock); \ +} G_STMT_END + +typedef struct _GstPlaySinkConvertBin GstPlaySinkConvertBin; +typedef struct _GstPlaySinkConvertBinClass GstPlaySinkConvertBinClass; + +struct _GstPlaySinkConvertBin +{ + GstBin parent; + + /* < private > */ + GMutex *lock; + + GstPad *sinkpad, *sink_proxypad; + gboolean sink_proxypad_blocked; + GstSegment segment; + + GstPad *srcpad; + + gboolean raw; + GList *conversion_elements; + GstElement *identity; + + /* configuration for derived classes */ + gboolean audio; + gboolean (*add_conversion_elements)(GstPlaySinkConvertBin *); +}; + +struct _GstPlaySinkConvertBinClass +{ + GstBinClass parent; +}; + +GType gst_play_sink_convert_bin_get_type (void); +GstElement * +gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin *self, + const char *factory, const char *name); +void +gst_play_sink_convert_bin_add_conversion_element (GstPlaySinkConvertBin *self, + GstElement *el); + +G_END_DECLS +#endif /* __GST_PLAY_SINK_CONVERT_BIN_H__ */ diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index 96f9e620f3..0159f7cdd8 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -32,439 +32,36 @@ GST_DEBUG_CATEGORY_STATIC (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); + GST_TYPE_PLAY_SINK_CONVERT_BIN); static gboolean -is_raw_caps (GstCaps * caps) +gst_play_sink_video_convert_add_conversion_elements (GstPlaySinkConvertBin * + cbin) { - gint i, n; - GstStructure *s; - const gchar *name; + GstElement *el, *prev = NULL; - 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; + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + COLORSPACE, "conv"); + if (el) + prev = el; + + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + "videoscale", "scale"); + if (el) { + /* Add black borders if necessary to keep the DAR */ + g_object_set (el, "add-borders", TRUE, NULL); + if (prev) { + if (!gst_element_link_pads_full (prev, "src", el, "sink", + GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) + goto link_failed; + } + prev = el; } 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_send_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_send_event (pad, event); - - gst_object_unref (pad); -} - -static void -gst_play_sink_video_convert_add_identity (GstPlaySinkVideoConvert * self) -{ - self->identity = gst_element_factory_make ("identity", "identity"); - if (self->identity == NULL) { - post_missing_element_message (self, "identity"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "identity"), ("video rendering might fail")); - } else { - gst_bin_add (GST_BIN_CAST (self), self->identity); - gst_element_sync_state_with_parent (self->identity); - distribute_running_time (self->identity, &self->segment); - } -} - -static void -gst_play_sink_video_convert_set_targets (GstPlaySinkVideoConvert * self, - GstElement * head, GstElement * tail) -{ - GstPad *pad; - - pad = gst_element_get_static_pad (head, "sink"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (tail, "src"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); - gst_object_unref (pad); -} - -static void -pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self) -{ - GstPad *peer; - GstCaps *caps; - gboolean raw; - GstBin *bin = GST_BIN_CAST (self); - GstElement *head = NULL, *prev = NULL; - - 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) { - - 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 (COLORSPACE, "conv"); - if (self->conv == NULL) { - post_missing_element_message (self, COLORSPACE); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - COLORSPACE), ("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; - } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); - } else { - - 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_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); - } - - g_assert ((head != NULL) == (prev != NULL)); - - /* to make things simple and avoid counterintuitive pad juggling, - ensure there is at least one element in the list */ - if (!head) { - gst_play_sink_video_convert_add_identity (self); - prev = head = self->identity; - } - - gst_play_sink_video_convert_set_targets (self, head, prev); - -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.")); - - /* use a simple identity, better than nothing */ - gst_play_sink_video_convert_add_identity (self); - gst_play_sink_video_convert_set_targets (self, self->identity, - self->identity); - - 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); - 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); - otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); - if (!otherpad) { - if (pad == self->srcpad) { - otherpad = self->sink_proxypad; - } else if (pad == self->sinkpad) { - otherpad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD - (self->sinkpad))); - } else { - GST_ERROR_OBJECT (pad, "Not one of our pads"); - } - } - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - - if (otherpad) { - 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); - } else { - GST_WARNING_OBJECT (self, "Could not traverse bin"); - ret = gst_caps_new_any (); - } - - 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; - } - if (!self->identity) { - gst_play_sink_video_convert_add_identity (self); - } - gst_play_sink_video_convert_set_targets (self, self->identity, - self->identity); - 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); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (self->identity) { - gst_element_set_state (self->identity, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->identity); - self->identity = NULL; - } - break; - default: - break; - } - - return ret; + return FALSE; } static void @@ -479,51 +76,17 @@ gst_play_sink_video_convert_class_init (GstPlaySinkVideoConvertClass * klass) 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 "); - - 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); + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); + cbin->audio = FALSE; + cbin->add_conversion_elements = + gst_play_sink_video_convert_add_conversion_elements; } diff --git a/gst/playback/gstplaysinkvideoconvert.h b/gst/playback/gstplaysinkvideoconvert.h index 10027ee420..33030adb4b 100644 --- a/gst/playback/gstplaysinkvideoconvert.h +++ b/gst/playback/gstplaysinkvideoconvert.h @@ -18,6 +18,7 @@ */ #include +#include "gstplaysinkconvertbin.h" #ifndef __GST_PLAY_SINK_VIDEO_CONVERT_H__ #define __GST_PLAY_SINK_VIDEO_CONVERT_H__ @@ -36,47 +37,18 @@ G_BEGIN_DECLS #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; + GstPlaySinkConvertBin parent; - /* < private > */ - GMutex *lock; - - GstPad *sinkpad, *sink_proxypad; - gboolean sink_proxypad_blocked; - GstSegment segment; - - GstPad *srcpad; - - gboolean raw; - GstElement *conv, *scale; - GstElement *identity; }; struct _GstPlaySinkVideoConvertClass { - GstBinClass parent; + GstPlaySinkConvertBinClass parent; }; GType gst_play_sink_video_convert_get_type (void); From 69d98d08c193c5ee21775b0cc8ef6b0a6c6d0089 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 17:29:50 +0000 Subject: [PATCH 08/32] playsink: only compare against the media type we expect ie, audio/x-raw- for audio, video/x-raw- for video. Add a trailing - to be more specific. I doubt there's anything like audio/x-rawhide or something, but you never know. https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkconvertbin.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index cfd8dc8b79..6a78ef4118 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -45,18 +45,18 @@ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS_ANY); static gboolean -is_raw_caps (GstCaps * caps) +is_raw_caps (GstCaps * caps, gboolean audio) { gint i, n; GstStructure *s; const gchar *name; + const gchar *prefix = audio ? "audio/x-raw-" : "video/x-raw-"; 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") - && !g_str_has_prefix (name, "video/x-raw")) + if (!g_str_has_prefix (name, prefix)) return FALSE; } @@ -200,7 +200,7 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkConvertBin * self) caps = gst_pad_get_caps_reffed (peer); gst_object_unref (peer); - raw = is_raw_caps (caps); + raw = is_raw_caps (caps, self->audio); GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw); gst_caps_unref (caps); From 6925c02bc23091a250bfbf2aba68e469b9cf2e4e Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 17:26:48 +0000 Subject: [PATCH 09/32] playsink: keep both raw and non raw pipelines at all times and switch between them as needed. https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkconvertbin.c | 93 ++++++++++------------------ 1 file changed, 33 insertions(+), 60 deletions(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 6a78ef4118..c3949dc9bc 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -137,18 +137,21 @@ gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self) ); } else { gst_bin_add (GST_BIN_CAST (self), self->identity); - gst_element_sync_state_with_parent (self->identity); - distribute_running_time (self->identity, &self->segment); } } static void -gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self) +gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self, + gboolean passthrough) { GstPad *pad; GstElement *head, *tail; - if (self->conversion_elements == NULL) { + if (self->conversion_elements == NULL || passthrough) { + if (!passthrough) { + GST_WARNING_OBJECT (self, + "Doing passthrough as no converter elements were added"); + } head = tail = self->identity; } else { head = GST_ELEMENT (g_list_first (self->conversion_elements)->data); @@ -208,45 +211,23 @@ pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkConvertBin * self) goto unblock; self->raw = raw; + 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 (raw) { - GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline"); + GST_DEBUG_OBJECT (self, "Switching to 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); - - g_assert (self->add_conversion_elements); - if (!(*self->add_conversion_elements) (self)) { - goto link_failed; - } if (self->conversion_elements) g_list_foreach (self->conversion_elements, (GFunc) gst_play_sink_convert_bin_on_element_added, self); - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); } else { - GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline"); + GST_DEBUG_OBJECT (self, "Switch to passthrough 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->conversion_elements) { - g_list_foreach (self->conversion_elements, - (GFunc) gst_play_sink_convert_bin_remove_element, self); - g_list_free (self->conversion_elements); - self->conversion_elements = NULL; - } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); + gst_play_sink_convert_bin_on_element_added (self->identity, self); } - /* to make things simple and avoid counterintuitive pad juggling, - ensure there is at least one element in the list */ - if (!self->conversion_elements) { - gst_play_sink_convert_bin_add_identity (self); - } - - gst_play_sink_convert_bin_set_targets (self); + gst_play_sink_convert_bin_set_targets (self, !raw); unblock: gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, @@ -256,23 +237,6 @@ unblock: done: GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); return; - -link_failed: - { - GST_ELEMENT_ERROR (self, CORE, PAD, - (NULL), ("Failed to configure the converter bin.")); - - /* use a simple identity, better than nothing */ - gst_play_sink_convert_bin_add_identity (self); - gst_play_sink_convert_bin_set_targets (self); - - 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_CONVERT_BIN_UNLOCK (self); - return; - } } static gboolean @@ -430,6 +394,16 @@ gst_play_sink_convert_bin_change_state (GstElement * element, GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (element); switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + g_assert (self->add_conversion_elements); + if (!(*self->add_conversion_elements) (self)) { + GST_ELEMENT_ERROR (self, CORE, PAD, + (NULL), ("Failed to configure the converter bin.")); + } + gst_play_sink_convert_bin_add_identity (self); + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); if (gst_pad_is_blocked (self->sink_proxypad)) @@ -450,16 +424,7 @@ gst_play_sink_convert_bin_change_state (GstElement * element, case GST_STATE_CHANGE_PAUSED_TO_READY: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - if (self->conversion_elements) { - g_list_foreach (self->conversion_elements, - (GFunc) gst_play_sink_convert_bin_remove_element, self); - g_list_free (self->conversion_elements); - self->conversion_elements = NULL; - } - if (!self->identity) { - gst_play_sink_convert_bin_add_identity (self); - } - gst_play_sink_convert_bin_set_targets (self); + gst_play_sink_convert_bin_set_targets (self, TRUE); self->raw = FALSE; GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; @@ -472,11 +437,19 @@ gst_play_sink_convert_bin_change_state (GstElement * element, GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; case GST_STATE_CHANGE_READY_TO_NULL: + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + if (self->conversion_elements) { + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_remove_element, self); + g_list_free (self->conversion_elements); + self->conversion_elements = NULL; + } if (self->identity) { gst_element_set_state (self->identity, GST_STATE_NULL); gst_bin_remove (GST_BIN_CAST (self), self->identity); self->identity = NULL; } + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; default: break; From b34dac9a8737eb5b9e16e86ba2553c9758990e3a Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 17:54:27 +0000 Subject: [PATCH 10/32] playsink: cache inner converter bin caps https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkconvertbin.c | 28 ++++++++++++++++++++++++++++ gst/playback/gstplaysinkconvertbin.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index c3949dc9bc..8754449544 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -386,6 +386,29 @@ gst_play_sink_convert_bin_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gst_play_sink_convert_bin_cache_converter_caps (GstPlaySinkConvertBin * self) +{ + GstElement *head; + GstPad *pad; + + self->converter_caps = NULL; + + if (!self->conversion_elements) + return; + + head = GST_ELEMENT (g_list_first (self->conversion_elements)->data); + pad = gst_element_get_static_pad (head, "sink"); + if (!pad) + return; + + self->converter_caps = gst_pad_get_caps_reffed (pad); + GST_INFO_OBJECT (self, "Converter caps: %" GST_PTR_FORMAT, + self->converter_caps); + + gst_object_unref (pad); +} + static GstStateChangeReturn gst_play_sink_convert_bin_change_state (GstElement * element, GstStateChange transition) @@ -401,6 +424,7 @@ gst_play_sink_convert_bin_change_state (GstElement * element, GST_ELEMENT_ERROR (self, CORE, PAD, (NULL), ("Failed to configure the converter bin.")); } + gst_play_sink_convert_bin_cache_converter_caps (self); gst_play_sink_convert_bin_add_identity (self); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; @@ -449,6 +473,10 @@ gst_play_sink_convert_bin_change_state (GstElement * element, gst_bin_remove (GST_BIN_CAST (self), self->identity); self->identity = NULL; } + if (self->converter_caps) { + gst_caps_unref (self->converter_caps); + self->converter_caps = NULL; + } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; default: diff --git a/gst/playback/gstplaysinkconvertbin.h b/gst/playback/gstplaysinkconvertbin.h index 2665651973..7fac18d4fa 100644 --- a/gst/playback/gstplaysinkconvertbin.h +++ b/gst/playback/gstplaysinkconvertbin.h @@ -74,6 +74,8 @@ struct _GstPlaySinkConvertBin GList *conversion_elements; GstElement *identity; + GstCaps *converter_caps; + /* configuration for derived classes */ gboolean audio; gboolean (*add_conversion_elements)(GstPlaySinkConvertBin *); From 3939457b00f3ae81833c41b8fbab2bed5099aee9 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 18:06:00 +0000 Subject: [PATCH 11/32] playsink: consider both passthrough and converter caps in getcaps Since we can switch between both modes. https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkconvertbin.c | 31 ++++++++++++---------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 8754449544..349c0a5761 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -343,33 +343,28 @@ gst_play_sink_convert_bin_getcaps (GstPad * pad) GstPad *otherpad, *peer; GST_PLAY_SINK_CONVERT_BIN_LOCK (self); - otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); - if (!otherpad) { - if (pad == self->srcpad) { - otherpad = self->sink_proxypad; - } else if (pad == self->sinkpad) { - otherpad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD - (self->sinkpad))); - } else { - GST_ERROR_OBJECT (pad, "Not one of our pads"); - } + if (pad == self->srcpad) { + otherpad = self->sinkpad; + } else if (pad == self->sinkpad) { + otherpad = self->srcpad; + } else { + GST_ERROR_OBJECT (pad, "Not one of our pads"); + otherpad = NULL; } - GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); if (otherpad) { peer = gst_pad_get_peer (otherpad); if (peer) { - ret = gst_pad_get_caps_reffed (peer); + GstCaps *peer_caps = gst_pad_get_caps_reffed (peer); gst_object_unref (peer); + ret = gst_caps_union (peer_caps, self->converter_caps); + gst_caps_unref (peer_caps); } else { - ret = gst_caps_new_any (); + ret = gst_caps_ref (self->converter_caps); } - gst_object_unref (otherpad); - } else { - GST_WARNING_OBJECT (self, "Could not traverse bin"); - ret = gst_caps_new_any (); } + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + gst_object_unref (self); return ret; From c08a23169d85b47c28ce8ded06a80b90c02ef8cc Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 18:43:06 +0000 Subject: [PATCH 12/32] playsink: handle NULL cached caps in getcaps https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkconvertbin.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 349c0a5761..35faf4a5f2 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -357,11 +357,17 @@ gst_play_sink_convert_bin_getcaps (GstPad * pad) if (peer) { GstCaps *peer_caps = gst_pad_get_caps_reffed (peer); gst_object_unref (peer); - ret = gst_caps_union (peer_caps, self->converter_caps); - gst_caps_unref (peer_caps); + if (self->converter_caps) { + ret = gst_caps_union (peer_caps, self->converter_caps); + gst_caps_unref (peer_caps); + } else { + ret = peer_caps; + } } else { ret = gst_caps_ref (self->converter_caps); } + } else { + ret = gst_caps_new_any (); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); @@ -389,13 +395,17 @@ gst_play_sink_convert_bin_cache_converter_caps (GstPlaySinkConvertBin * self) self->converter_caps = NULL; - if (!self->conversion_elements) + if (!self->conversion_elements) { + GST_WARNING_OBJECT (self, "No conversion elements"); return; + } head = GST_ELEMENT (g_list_first (self->conversion_elements)->data); pad = gst_element_get_static_pad (head, "sink"); - if (!pad) + if (!pad) { + GST_WARNING_OBJECT (self, "No sink pad found"); return; + } self->converter_caps = gst_pad_get_caps_reffed (pad); GST_INFO_OBJECT (self, "Converter caps: %" GST_PTR_FORMAT, From ae3ba533914d7961248190bc56734d7b6fe2b716 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 21:05:30 +0000 Subject: [PATCH 13/32] playsink: handle after-the-fact changes in converters/volume booleans The playsink was nastily poking a boolean in the structure. Make those booleans properties, so we are told when they change, and rebuild the conversion bin when they do. Some cleanup to go with it too. https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysink.c | 23 +++---- gst/playback/gstplaysinkaudioconvert.c | 90 ++++++++++++++++++++++++-- gst/playback/gstplaysinkaudioconvert.h | 3 +- gst/playback/gstplaysinkconvertbin.c | 68 ++++++++++--------- gst/playback/gstplaysinkconvertbin.h | 5 +- gst/playback/gstplaysinkvideoconvert.c | 10 +-- gst/playback/gstplaysinkvideoconvert.h | 1 - 7 files changed, 143 insertions(+), 57 deletions(-) diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index d642fa0a4b..3639f6c7c1 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -1799,9 +1799,15 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw) if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) { - GST_DEBUG_OBJECT (playsink, "creating audioconvert"); + gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO); + gboolean use_volume = + !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME; + GST_DEBUG_OBJECT (playsink, + "creating audioconvert with use-converters %d, use-volume %d", + use_converters, use_volume); chain->conv = - g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL); + g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", + "use-converters", use_converters, "use-volume", use_volume, NULL); gst_bin_add (bin, chain->conv); if (prev) { if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink", @@ -1812,11 +1818,6 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw) } prev = chain->conv; - 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) { GstPlaySinkAudioConvert *conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv); @@ -1964,13 +1965,13 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw) G_CALLBACK (notify_mute_cb), playsink); } - GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE; + g_object_set (chain->conv, "use-volume", FALSE, NULL); } 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; + g_object_set (chain->conv, "use-volume", TRUE, NULL); GST_DEBUG_OBJECT (playsink, "the sink has no volume property"); /* Disconnect signals */ @@ -3028,14 +3029,14 @@ caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink) if (pad == playsink->audio_pad) { raw = is_raw_pad (pad); - reconfigure = (! !playsink->audio_pad_raw != ! !raw) + 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) + reconfigure = (!!playsink->video_pad_raw != !!raw) && playsink->videochain; GST_DEBUG_OBJECT (pad, "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw, diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index b4709397f0..acfd6eff61 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -34,15 +34,26 @@ GST_DEBUG_CATEGORY_STATIC (gst_play_sink_audio_convert_debug); G_DEFINE_TYPE (GstPlaySinkAudioConvert, gst_play_sink_audio_convert, GST_TYPE_PLAY_SINK_CONVERT_BIN); -static gboolean -gst_play_sink_audio_convert_add_conversion_elements (GstPlaySinkConvertBin * - cbin) +enum { - GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT (cbin); + PROP_0, + PROP_USE_CONVERTERS, + PROP_USE_VOLUME, +}; + +static gboolean +gst_play_sink_audio_convert_add_conversion_elements (GstPlaySinkAudioConvert * + self) +{ + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); GstElement *el, *prev = NULL; g_assert (cbin->conversion_elements == NULL); + GST_DEBUG_OBJECT (self, + "Building audio conversion with use-converters %d, use-volume %d", + self->use_converters, self->use_volume); + if (self->use_converters) { el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, "audioconvert", "conv"); @@ -73,6 +84,7 @@ gst_play_sink_audio_convert_add_conversion_elements (GstPlaySinkConvertBin * } prev = el; } + return TRUE; link_failed: @@ -90,6 +102,59 @@ gst_play_sink_audio_convert_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gst_play_sink_audio_convert_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object); + gboolean v, changed = FALSE; + + switch (prop_id) { + case PROP_USE_CONVERTERS: + v = g_value_get_boolean (value); + if (v != self->use_converters) { + self->use_converters = v; + changed = TRUE; + } + break; + case PROP_USE_VOLUME: + v = g_value_get_boolean (value); + if (v != self->use_volume) { + self->use_volume = v; + changed = TRUE; + } + break; + default: + break; + } + + if (changed) { + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); + GST_DEBUG_OBJECT (self, "Rebuilding converter bin"); + gst_play_sink_convert_bin_remove_elements (cbin); + gst_play_sink_audio_convert_add_conversion_elements (self); + gst_play_sink_convert_bin_cache_converter_caps (cbin); + } +} + +static void +gst_play_sink_audio_convert_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object); + + switch (prop_id) { + case PROP_USE_CONVERTERS: + g_value_set_boolean (value, self->use_converters); + break; + case PROP_USE_VOLUME: + g_value_set_boolean (value, self->use_volume); + break; + default: + break; + } +} + static void gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass) { @@ -103,6 +168,18 @@ gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass) gstelement_class = (GstElementClass *) klass; gobject_class->finalize = gst_play_sink_audio_convert_finalize; + gobject_class->set_property = gst_play_sink_audio_convert_set_property; + gobject_class->get_property = gst_play_sink_audio_convert_get_property; + + g_object_class_install_property (gobject_class, PROP_USE_CONVERTERS, + g_param_spec_boolean ("use-converters", "Use converters", + "Whether to use conversion elements", FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_USE_VOLUME, + g_param_spec_boolean ("use-volume", "Use volume", + "Whether to use a volume element", FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); gst_element_class_set_details_simple (gstelement_class, "Player Sink Audio Converter", "Audio/Bin/Converter", @@ -116,8 +193,6 @@ gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self) GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); cbin->audio = TRUE; - cbin->add_conversion_elements = - gst_play_sink_audio_convert_add_conversion_elements; /* FIXME: Only create this on demand but for now we need * it to always exist because of playsink's volume proxying @@ -126,4 +201,7 @@ gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self) self->volume = gst_element_factory_make ("volume", "volume"); if (self->volume) gst_object_ref_sink (self->volume); + + gst_play_sink_audio_convert_add_conversion_elements (self); + gst_play_sink_convert_bin_cache_converter_caps (cbin); } diff --git a/gst/playback/gstplaysinkaudioconvert.h b/gst/playback/gstplaysinkaudioconvert.h index 5f80af67b4..ad532e2e60 100644 --- a/gst/playback/gstplaysinkaudioconvert.h +++ b/gst/playback/gstplaysinkaudioconvert.h @@ -36,7 +36,6 @@ G_BEGIN_DECLS (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)) - typedef struct _GstPlaySinkAudioConvert GstPlaySinkAudioConvert; typedef struct _GstPlaySinkAudioConvertClass GstPlaySinkAudioConvertClass; @@ -46,8 +45,8 @@ struct _GstPlaySinkAudioConvert /* < pseudo public > */ GstElement *volume; - gboolean use_volume; gboolean use_converters; + gboolean use_volume; }; struct _GstPlaySinkAudioConvertClass diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 35faf4a5f2..afd4b75a02 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -147,6 +147,8 @@ gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self, GstPad *pad; GstElement *head, *tail; + GST_DEBUG_OBJECT (self, "Setting pad targets with passthrough %d", + passthrough); if (self->conversion_elements == NULL || passthrough) { if (!passthrough) { GST_WARNING_OBJECT (self, @@ -159,10 +161,12 @@ gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self, } pad = gst_element_get_static_pad (head, "sink"); + GST_DEBUG_OBJECT (self, "Ghosting bin sink pad to %" GST_PTR_FORMAT, pad); gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); gst_object_unref (pad); pad = gst_element_get_static_pad (tail, "src"); + GST_DEBUG_OBJECT (self, "Ghosting bin src pad to %" GST_PTR_FORMAT, pad); gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); gst_object_unref (pad); } @@ -289,6 +293,7 @@ gst_play_sink_convert_bin_sink_setcaps (GstPad * pad, GstCaps * caps) gboolean reconfigure = FALSE; gboolean raw; + GST_DEBUG_OBJECT (pad, "setcaps: %" GST_PTR_FORMAT, caps); GST_PLAY_SINK_CONVERT_BIN_LOCK (self); s = gst_caps_get_structure (caps, 0); name = gst_structure_get_name (s); @@ -299,6 +304,8 @@ gst_play_sink_convert_bin_sink_setcaps (GstPad * pad, GstCaps * caps) raw = g_str_has_prefix (name, "video/x-raw-"); } + GST_DEBUG_OBJECT (self, "raw %d, self->raw %d, blocked %d", + raw, self->raw, gst_pad_is_blocked (self->sink_proxypad)); if (raw) { if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); @@ -376,24 +383,49 @@ gst_play_sink_convert_bin_getcaps (GstPad * pad) return ret; } +void +gst_play_sink_convert_bin_remove_elements (GstPlaySinkConvertBin * self) +{ + if (self->conversion_elements) { + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_remove_element, self); + g_list_free (self->conversion_elements); + self->conversion_elements = NULL; + } + if (self->identity) { + gst_element_set_state (self->identity, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), self->identity); + self->identity = NULL; + } + if (self->converter_caps) { + gst_caps_unref (self->converter_caps); + self->converter_caps = NULL; + } +} + static void gst_play_sink_convert_bin_finalize (GObject * object) { GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (object); + gst_play_sink_convert_bin_remove_elements (self); + gst_object_unref (self->sink_proxypad); g_mutex_free (self->lock); G_OBJECT_CLASS (parent_class)->finalize (object); } -static void +void gst_play_sink_convert_bin_cache_converter_caps (GstPlaySinkConvertBin * self) { GstElement *head; GstPad *pad; - self->converter_caps = NULL; + if (self->converter_caps) { + gst_caps_unref (self->converter_caps); + self->converter_caps = NULL; + } if (!self->conversion_elements) { GST_WARNING_OBJECT (self, "No conversion elements"); @@ -422,17 +454,6 @@ gst_play_sink_convert_bin_change_state (GstElement * element, GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (element); switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - GST_PLAY_SINK_CONVERT_BIN_LOCK (self); - g_assert (self->add_conversion_elements); - if (!(*self->add_conversion_elements) (self)) { - GST_ELEMENT_ERROR (self, CORE, PAD, - (NULL), ("Failed to configure the converter bin.")); - } - gst_play_sink_convert_bin_cache_converter_caps (self); - gst_play_sink_convert_bin_add_identity (self); - GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); - break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); if (gst_pad_is_blocked (self->sink_proxypad)) @@ -465,25 +486,6 @@ gst_play_sink_convert_bin_change_state (GstElement * element, (GDestroyNotify) gst_object_unref); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; - case GST_STATE_CHANGE_READY_TO_NULL: - GST_PLAY_SINK_CONVERT_BIN_LOCK (self); - if (self->conversion_elements) { - g_list_foreach (self->conversion_elements, - (GFunc) gst_play_sink_convert_bin_remove_element, self); - g_list_free (self->conversion_elements); - self->conversion_elements = NULL; - } - if (self->identity) { - gst_element_set_state (self->identity, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->identity); - self->identity = NULL; - } - if (self->converter_caps) { - gst_caps_unref (self->converter_caps); - self->converter_caps = NULL; - } - GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); - break; default: break; } @@ -547,4 +549,6 @@ gst_play_sink_convert_bin_init (GstPlaySinkConvertBin * self) GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_getcaps)); gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad); gst_object_unref (templ); + + gst_play_sink_convert_bin_add_identity (self); } diff --git a/gst/playback/gstplaysinkconvertbin.h b/gst/playback/gstplaysinkconvertbin.h index 7fac18d4fa..d8379ef7a8 100644 --- a/gst/playback/gstplaysinkconvertbin.h +++ b/gst/playback/gstplaysinkconvertbin.h @@ -78,7 +78,6 @@ struct _GstPlaySinkConvertBin /* configuration for derived classes */ gboolean audio; - gboolean (*add_conversion_elements)(GstPlaySinkConvertBin *); }; struct _GstPlaySinkConvertBinClass @@ -93,6 +92,10 @@ gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin void gst_play_sink_convert_bin_add_conversion_element (GstPlaySinkConvertBin *self, GstElement *el); +void +gst_play_sink_convert_bin_cache_converter_caps (GstPlaySinkConvertBin * self); +void +gst_play_sink_convert_bin_remove_elements (GstPlaySinkConvertBin * self); G_END_DECLS #endif /* __GST_PLAY_SINK_CONVERT_BIN_H__ */ diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index 0159f7cdd8..a8f3d81189 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -35,9 +35,10 @@ G_DEFINE_TYPE (GstPlaySinkVideoConvert, gst_play_sink_video_convert, GST_TYPE_PLAY_SINK_CONVERT_BIN); static gboolean -gst_play_sink_video_convert_add_conversion_elements (GstPlaySinkConvertBin * - cbin) +gst_play_sink_video_convert_add_conversion_elements (GstPlaySinkVideoConvert * + self) { + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); GstElement *el, *prev = NULL; el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, @@ -87,6 +88,7 @@ gst_play_sink_video_convert_init (GstPlaySinkVideoConvert * self) { GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); cbin->audio = FALSE; - cbin->add_conversion_elements = - gst_play_sink_video_convert_add_conversion_elements; + + gst_play_sink_video_convert_add_conversion_elements (self); + gst_play_sink_convert_bin_cache_converter_caps (cbin); } diff --git a/gst/playback/gstplaysinkvideoconvert.h b/gst/playback/gstplaysinkvideoconvert.h index 33030adb4b..df56d97ab8 100644 --- a/gst/playback/gstplaysinkvideoconvert.h +++ b/gst/playback/gstplaysinkvideoconvert.h @@ -36,7 +36,6 @@ G_BEGIN_DECLS (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)) - typedef struct _GstPlaySinkVideoConvert GstPlaySinkVideoConvert; typedef struct _GstPlaySinkVideoConvertClass GstPlaySinkVideoConvertClass; From 80971a3b337f8083afd06aa63cb01eb5f3f67585 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 17 Oct 2011 22:02:03 +0000 Subject: [PATCH 14/32] playsink-videoconvert: Fix warning on build Remove unused variable --- gst/playback/gstplaysinkvideoconvert.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index a8f3d81189..72a9aff7fc 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -68,13 +68,11 @@ link_failed: 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; gst_element_class_set_details_simple (gstelement_class, From 34f72da9cc41e4b1e94dc175efdc386751b266ce Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 17 Oct 2011 22:41:11 +0000 Subject: [PATCH 15/32] playsinkconvertbin: Adding some debug messages Adds a couple debug messages and some g_assert to make debugging easier --- gst/playback/gstplaysinkconvertbin.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index afd4b75a02..4648d670f6 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -150,6 +150,8 @@ gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self, GST_DEBUG_OBJECT (self, "Setting pad targets with passthrough %d", passthrough); if (self->conversion_elements == NULL || passthrough) { + GST_DEBUG_OBJECT (self, "no conversion elements, using identity (%p) as " + "head/tail", self->identity); if (!passthrough) { GST_WARNING_OBJECT (self, "Doing passthrough as no converter elements were added"); @@ -158,8 +160,13 @@ gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self, } else { head = GST_ELEMENT (g_list_first (self->conversion_elements)->data); tail = GST_ELEMENT (g_list_last (self->conversion_elements)->data); + GST_DEBUG_OBJECT (self, "conversion elements in use, picking " + "head:%s and tail:%s", GST_OBJECT_NAME (head), GST_OBJECT_NAME (tail)); } + g_return_if_fail (head != NULL); + g_return_if_fail (tail != NULL); + pad = gst_element_get_static_pad (head, "sink"); GST_DEBUG_OBJECT (self, "Ghosting bin sink pad to %" GST_PTR_FORMAT, pad); gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); From f9ea3fdda8c391afc17cb7b110a998d333d12059 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 17 Oct 2011 22:41:49 +0000 Subject: [PATCH 16/32] playsinkconvertbin: No need to remove the identity The identity element should be handled by the GstBin's cleanup, removing it on the remove_elements function might remove it too soon, as this function can be called directly from playsink --- gst/playback/gstplaysinkconvertbin.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 4648d670f6..7c1bb68f68 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -399,11 +399,6 @@ gst_play_sink_convert_bin_remove_elements (GstPlaySinkConvertBin * self) g_list_free (self->conversion_elements); self->conversion_elements = NULL; } - if (self->identity) { - gst_element_set_state (self->identity, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->identity); - self->identity = NULL; - } if (self->converter_caps) { gst_caps_unref (self->converter_caps); self->converter_caps = NULL; From 0a07701164a35f736ba8ba881bbcc12e9f9140ce Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 17 Oct 2011 23:14:54 +0000 Subject: [PATCH 17/32] playsinkconvertbin: Be more consistent with ghostpad targets Set up targets on READY->PAUSED state change to passthrough by default. This prevents the targets from being unset on the first run, while the 'raw' variable would mean that some target is set. --- gst/playback/gstplaysinkconvertbin.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 7c1bb68f68..6123d11f9c 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -464,6 +464,13 @@ gst_play_sink_convert_bin_change_state (GstElement * element, (GDestroyNotify) gst_object_unref); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + gst_play_sink_convert_bin_set_targets (self, TRUE); + self->raw = FALSE; + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + break; default: break; } From c3e94d1c0890f5dcc139d29692bb588ae6f5d48e Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 19 Oct 2011 14:12:01 +0100 Subject: [PATCH 18/32] playsink: lock the new {set,get}_property functions https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkaudioconvert.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index acfd6eff61..44572b4869 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -109,6 +109,7 @@ gst_play_sink_audio_convert_set_property (GObject * object, guint prop_id, GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object); gboolean v, changed = FALSE; + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); switch (prop_id) { case PROP_USE_CONVERTERS: v = g_value_get_boolean (value); @@ -135,6 +136,7 @@ gst_play_sink_audio_convert_set_property (GObject * object, guint prop_id, gst_play_sink_audio_convert_add_conversion_elements (self); gst_play_sink_convert_bin_cache_converter_caps (cbin); } + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); } static void @@ -143,6 +145,7 @@ gst_play_sink_audio_convert_get_property (GObject * object, guint prop_id, { GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object); + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); switch (prop_id) { case PROP_USE_CONVERTERS: g_value_set_boolean (value, self->use_converters); @@ -153,6 +156,7 @@ gst_play_sink_audio_convert_get_property (GObject * object, guint prop_id, default: break; } + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); } static void From 0cac680faccd6d2c1396339f1994a63d971697d2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 19 Oct 2011 14:13:30 +0100 Subject: [PATCH 19/32] playsink: re-add identity where appropriate https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkaudioconvert.c | 1 + gst/playback/gstplaysinkconvertbin.c | 2 +- gst/playback/gstplaysinkconvertbin.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 44572b4869..f8783d067b 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -134,6 +134,7 @@ gst_play_sink_audio_convert_set_property (GObject * object, guint prop_id, GST_DEBUG_OBJECT (self, "Rebuilding converter bin"); gst_play_sink_convert_bin_remove_elements (cbin); gst_play_sink_audio_convert_add_conversion_elements (self); + gst_play_sink_convert_bin_add_identity (cbin); gst_play_sink_convert_bin_cache_converter_caps (cbin); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 6123d11f9c..545b9e55e0 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -123,7 +123,7 @@ gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin return el; } -static void +void gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self) { self->identity = gst_element_factory_make ("identity", "identity"); diff --git a/gst/playback/gstplaysinkconvertbin.h b/gst/playback/gstplaysinkconvertbin.h index d8379ef7a8..0e71866c7a 100644 --- a/gst/playback/gstplaysinkconvertbin.h +++ b/gst/playback/gstplaysinkconvertbin.h @@ -96,6 +96,8 @@ void gst_play_sink_convert_bin_cache_converter_caps (GstPlaySinkConvertBin * self); void gst_play_sink_convert_bin_remove_elements (GstPlaySinkConvertBin * self); +void +gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self); G_END_DECLS #endif /* __GST_PLAY_SINK_CONVERT_BIN_H__ */ From 7eb8a9aaf6c0bf92a35e2c3f35ba7398eb8f6988 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 19 Oct 2011 14:13:39 +0100 Subject: [PATCH 20/32] playsink: send flush start/stop event when we switch elements https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/gstplaysinkconvertbin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 545b9e55e0..75e0668242 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -81,6 +81,9 @@ distribute_running_time (GstElement * element, const GstSegment * segment) pad = gst_element_get_static_pad (element, "sink"); + gst_pad_send_event (pad, gst_event_new_flush_start ()); + gst_pad_send_event (pad, gst_event_new_flush_stop ()); + if (segment->accum) { event = gst_event_new_new_segment_full (FALSE, segment->rate, segment->applied_rate, segment->format, 0, segment->accum, 0); From b29a3d3cfffa418f8cdfd4c83a1750dc96039695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 3 Nov 2011 10:07:27 +0100 Subject: [PATCH 21/32] playsinkconvertbin: Don't add identity multiple times --- gst/playback/gstplaysinkconvertbin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 75e0668242..7039653dd5 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -129,6 +129,9 @@ gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin void gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self) { + if (self->identity) + return; + self->identity = gst_element_factory_make ("identity", "identity"); if (self->identity == NULL) { gst_play_sink_convert_bin_post_missing_element_message (self, "identity"); From cee87108176fd0b9b1c6d772d93a0ea190d8265f Mon Sep 17 00:00:00 2001 From: Mart Raudsepp Date: Thu, 20 Oct 2011 13:04:52 +0300 Subject: [PATCH 22/32] decodebin2: Use existing "caps" quark for one of the structure sets --- gst/playback/gstdecodebin2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c index d1eb15413f..22f6cc54f8 100644 --- a/gst/playback/gstdecodebin2.c +++ b/gst/playback/gstdecodebin2.c @@ -3483,7 +3483,7 @@ gst_decode_chain_get_topology (GstDecodeChain * chain) caps = NULL; } } - gst_structure_set (u, "caps", GST_TYPE_CAPS, caps, NULL); + gst_structure_id_set (u, topology_caps, GST_TYPE_CAPS, caps, NULL); gst_caps_unref (caps); return u; From 5c58bcfd155b7aa6d101b32a77da35d4284cbcad Mon Sep 17 00:00:00 2001 From: Mart Raudsepp Date: Thu, 3 Nov 2011 14:10:31 +0200 Subject: [PATCH 23/32] decodebin2: Post all source pads in stream-topology messages as "element-srcpad" values This allows us to easily get ahold of all pads on a stream-topology message, including pre-decoder ones, while "pad" only gives us access to the raw pads (as used by discoverer). --- gst/playback/gstdecodebin2.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c index 22f6cc54f8..520416b8c4 100644 --- a/gst/playback/gstdecodebin2.c +++ b/gst/playback/gstdecodebin2.c @@ -3352,7 +3352,8 @@ sort_end_pads (GstDecodePad * da, GstDecodePad * db) } static GstCaps * -_gst_element_get_linked_caps (GstElement * src, GstElement * sink) +_gst_element_get_linked_caps (GstElement * src, GstElement * sink, + GstPad ** srcpad) { GstIterator *it; GstElement *parent; @@ -3369,6 +3370,10 @@ _gst_element_get_linked_caps (GstElement * src, GstElement * sink) parent = gst_pad_get_parent_element (peer); if (parent == sink) { caps = gst_pad_get_negotiated_caps (pad); + if (srcpad) { + gst_object_ref (pad); + *srcpad = pad; + } done = TRUE; } @@ -3397,6 +3402,7 @@ static GQuark topology_structure_name = 0; static GQuark topology_caps = 0; static GQuark topology_next = 0; static GQuark topology_pad = 0; +static GQuark topology_element_srcpad = 0; /* FIXME: Invent gst_structure_take_structure() to prevent all the * structure copying for nothing @@ -3422,8 +3428,11 @@ gst_decode_chain_get_topology (GstDecodeChain * chain) gst_structure_id_set (u, topology_caps, GST_TYPE_CAPS, chain->endcaps, NULL); - if (chain->endpad) + if (chain->endpad) { gst_structure_id_set (u, topology_pad, GST_TYPE_PAD, chain->endpad, NULL); + gst_structure_id_set (u, topology_element_srcpad, GST_TYPE_PAD, + chain->endpad, NULL); + } gst_structure_id_set (s, topology_next, GST_TYPE_STRUCTURE, u, NULL); gst_structure_free (u); u = s; @@ -3453,13 +3462,15 @@ gst_decode_chain_get_topology (GstDecodeChain * chain) GstDecodeElement *delem, *delem_next; GstElement *elem, *elem_next; GstCaps *caps; + GstPad *srcpad; delem = l->data; elem = delem->element; delem_next = l->next->data; elem_next = delem_next->element; + srcpad = NULL; - caps = _gst_element_get_linked_caps (elem_next, elem); + caps = _gst_element_get_linked_caps (elem_next, elem, &srcpad); if (caps) { s = gst_structure_id_empty_new (topology_structure_name); @@ -3470,6 +3481,12 @@ gst_decode_chain_get_topology (GstDecodeChain * chain) gst_structure_free (u); u = s; } + + if (srcpad) { + gst_structure_id_set (u, topology_element_srcpad, GST_TYPE_PAD, srcpad, + NULL); + gst_object_unref (srcpad); + } } /* Caps that resulted in this chain */ @@ -3484,6 +3501,8 @@ gst_decode_chain_get_topology (GstDecodeChain * chain) } } gst_structure_id_set (u, topology_caps, GST_TYPE_CAPS, caps, NULL); + gst_structure_id_set (u, topology_element_srcpad, GST_TYPE_PAD, chain->pad, + NULL); gst_caps_unref (caps); return u; @@ -4059,6 +4078,7 @@ gst_decode_bin_plugin_init (GstPlugin * plugin) topology_caps = g_quark_from_static_string ("caps"); topology_next = g_quark_from_static_string ("next"); topology_pad = g_quark_from_static_string ("pad"); + topology_element_srcpad = g_quark_from_static_string ("element-srcpad"); return gst_element_register (plugin, "decodebin2", GST_RANK_NONE, GST_TYPE_DECODE_BIN); From 7559fb29a458c9f0caf50dbc2ecca534ece0569d Mon Sep 17 00:00:00 2001 From: "Reynaldo H. Verdejo Pinochet" Date: Thu, 3 Nov 2011 21:35:38 -0300 Subject: [PATCH 24/32] Add missing default include paths to androgenizer call Fixes building tag/ with Android's NDK --- gst-libs/gst/tag/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/tag/Makefile.am b/gst-libs/gst/tag/Makefile.am index 50d69aa252..858d1452f2 100644 --- a/gst-libs/gst/tag/Makefile.am +++ b/gst-libs/gst/tag/Makefile.am @@ -109,7 +109,7 @@ Android.mk: Makefile.am -:TAGS eng debug \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(libgsttag_@GST_MAJORMINOR@_la_SOURCES) \ - -:CFLAGS $(DEFS) $(libgsttag_@GST_MAJORMINOR@_la_CFLAGS) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgsttag_@GST_MAJORMINOR@_la_CFLAGS) \ -:LDFLAGS $(libgsttag_@GST_MAJORMINOR@_la_LDFLAGS) \ $(libgsttag_@GST_MAJORMINOR@_la_LIBADD) \ -ldl \ From 6e9a302eca9aeaff4debedce2e61fa934e4a8109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 4 Nov 2011 10:36:51 +0100 Subject: [PATCH 25/32] playsinkconvertbin: Use gst_caps_merge() instead of gst_caps_union() This keeps the caps order and is more efficient. --- gst/playback/gstplaysinkconvertbin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index 7039653dd5..1daa7f1fca 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -378,8 +378,8 @@ gst_play_sink_convert_bin_getcaps (GstPad * pad) GstCaps *peer_caps = gst_pad_get_caps_reffed (peer); gst_object_unref (peer); if (self->converter_caps) { - ret = gst_caps_union (peer_caps, self->converter_caps); - gst_caps_unref (peer_caps); + gst_caps_merge (peer_caps, gst_caps_ref (self->converter_caps)); + ret = peer_caps; } else { ret = peer_caps; } From 7875ee11a570b16f502579485e21964c69ddba0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 4 Nov 2011 10:37:12 +0100 Subject: [PATCH 26/32] subtitleoverlay: Use gst_caps_merge() instead of gst_caps_union() This keeps the caps order and is more efficient. --- gst/playback/gstsubtitleoverlay.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/gst/playback/gstsubtitleoverlay.c b/gst/playback/gstsubtitleoverlay.c index b3b53c7e1e..49d790bf4d 100644 --- a/gst/playback/gstsubtitleoverlay.c +++ b/gst/playback/gstsubtitleoverlay.c @@ -298,26 +298,16 @@ _factory_filter (GstPluginFeature * feature, GstCaps ** subcaps) templ_caps = _get_sub_caps (factory); if (is_renderer && have_video_sink && templ_caps) { - GstCaps *tmp; - GST_DEBUG ("Found renderer element %s (%s) with caps %" GST_PTR_FORMAT, gst_element_factory_get_longname (factory), gst_plugin_feature_get_name (feature), templ_caps); - tmp = gst_caps_union (*subcaps, templ_caps); - gst_caps_unref (templ_caps); - gst_caps_replace (subcaps, tmp); - gst_caps_unref (tmp); + gst_caps_merge (*subcaps, templ_caps); return TRUE; } else if (!is_renderer && !have_video_sink && templ_caps) { - GstCaps *tmp; - GST_DEBUG ("Found parser element %s (%s) with caps %" GST_PTR_FORMAT, gst_element_factory_get_longname (factory), gst_plugin_feature_get_name (feature), templ_caps); - tmp = gst_caps_union (*subcaps, templ_caps); - gst_caps_unref (templ_caps); - gst_caps_replace (subcaps, tmp); - gst_caps_unref (tmp); + gst_caps_merge (*subcaps, templ_caps); return TRUE; } else { if (templ_caps) From 58b9818853417cf85ffb716c9f6b9c3489c293bd Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 May 2011 16:02:36 +0300 Subject: [PATCH 27/32] baseaudiosink: trivial comment fixes Some found by Havard Graff. Signed-off-by: Felipe Contreras --- gst-libs/gst/audio/gstbaseaudiosink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index a7477508e4..0d44e2d815 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -58,7 +58,7 @@ struct _GstBaseAudioSinkPrivate GstClockTime eos_time; gboolean do_time_offset; - /* number of microseconds we alow timestamps or clock slaving to drift + /* number of microseconds we allow timestamps or clock slaving to drift * before resyncing */ guint64 drift_tolerance; }; @@ -216,7 +216,7 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass) /** * GstBaseAudioSink:drift-tolerance * - * Controls the amount of time in milliseconds that timestamps or clocks are allowed + * Controls the amount of time in microseconds that timestamps or clocks are allowed * to drift before resynchronisation happens. * * Since: 0.10.26 @@ -1352,7 +1352,7 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, diff = sink->next_sample - sample_offset; /* calculate the max allowed drift in units of samples. By default this is - * 20ms and should be anough to compensate for timestamp rounding errors. */ + * 40ms and should be anough to compensate for timestamp rounding errors. */ maxdrift = (ringbuf->spec.rate * sink->priv->drift_tolerance) / GST_MSECOND; /* calc align with previous sample */ From 369cf3f14a8d969923c4a595c82ec0d4df2b3148 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 May 2011 15:49:20 +0300 Subject: [PATCH 28/32] baseaudiosink: split drift-tolerance into alignment-threshold So that drift-tolerance is used for clock slaving resync, and alignment-threshold is for timestamp drift. --- gst-libs/gst/audio/gstbaseaudiosink.c | 88 ++++++++++++++++++++++++--- gst-libs/gst/audio/gstbaseaudiosink.h | 5 ++ 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 0d44e2d815..837b11e600 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -58,9 +58,13 @@ struct _GstBaseAudioSinkPrivate GstClockTime eos_time; gboolean do_time_offset; - /* number of microseconds we allow timestamps or clock slaving to drift + /* number of microseconds we allow clock slaving to drift * before resyncing */ guint64 drift_tolerance; + + /* number of nanoseconds we allow timestamps to drift + * before resyncing */ + GstClockTime alignment_threshold; }; /* BaseAudioSink signals and args */ @@ -79,10 +83,14 @@ enum /* FIXME, enable pull mode when clock slaving and trick modes are figured out */ #define DEFAULT_CAN_ACTIVATE_PULL FALSE -/* when timestamps or clock slaving drift for more than 40ms we resync. This is +/* when clock slaving drift for more than 40ms we resync. This is * a reasonable default */ #define DEFAULT_DRIFT_TOLERANCE ((40 * GST_MSECOND) / GST_USECOND) +/* when timestamps drift for more than 40ms we resync. This should + * be anough to compensate for timestamp rounding errors. */ +#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) + enum { PROP_0, @@ -93,6 +101,7 @@ enum PROP_SLAVE_METHOD, PROP_CAN_ACTIVATE_PULL, PROP_DRIFT_TOLERANCE, + PROP_ALIGNMENT_THRESHOLD, PROP_LAST }; @@ -216,16 +225,29 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass) /** * GstBaseAudioSink:drift-tolerance * - * Controls the amount of time in microseconds that timestamps or clocks are allowed + * Controls the amount of time in microseconds that clocks are allowed * to drift before resynchronisation happens. * * Since: 0.10.26 */ g_object_class_install_property (gobject_class, PROP_DRIFT_TOLERANCE, g_param_spec_int64 ("drift-tolerance", "Drift Tolerance", - "Tolerance for timestamp and clock drift in microseconds", 1, + "Tolerance for clock drift in microseconds", 1, G_MAXINT64, DEFAULT_DRIFT_TOLERANCE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstBaseAudioSink:alignment_threshold + * + * Controls the amount of time in nanoseconds that timestamps are allowed + * to drift from their ideal time before choosing not to align them. + * + * Since: 0.10.26 + */ + g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, + g_param_spec_int64 ("alignment-threshold", "Alignment Threshold", + "Timestamp alignment threshold in nanoseconds", 1, + G_MAXINT64, DEFAULT_ALIGNMENT_THRESHOLD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state); @@ -266,6 +288,7 @@ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK; baseaudiosink->priv->slave_method = DEFAULT_SLAVE_METHOD; baseaudiosink->priv->drift_tolerance = DEFAULT_DRIFT_TOLERANCE; + baseaudiosink->priv->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock", (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink); @@ -659,6 +682,50 @@ gst_base_audio_sink_get_drift_tolerance (GstBaseAudioSink * sink) return result; } +/** + * gst_base_audio_sink_set_alignment_threshold: + * @sink: a #GstBaseAudioSink + * @alignment_threshold: the new alignment threshold in nanoseconds + * + * Controls the sink's alignment threshold. + * + * Since: 0.10.31 + */ +void +gst_base_audio_sink_set_alignment_threshold (GstBaseAudioSink * sink, + GstClockTime alignment_threshold) +{ + g_return_if_fail (GST_IS_BASE_AUDIO_SINK (sink)); + + GST_OBJECT_LOCK (sink); + sink->priv->alignment_threshold = alignment_threshold; + GST_OBJECT_UNLOCK (sink); +} + +/** + * gst_base_audio_sink_get_alignment_threshold + * @sink: a #GstBaseAudioSink + * + * Get the current alignment threshold, in nanoseconds, used by @sink. + * + * Returns: The current alignment threshold used by @sink. + * + * Since: 0.10.31 + */ +GstClockTime +gst_base_audio_sink_get_alignment_threshold (GstBaseAudioSink * sink) +{ + gint64 result; + + g_return_val_if_fail (GST_IS_BASE_AUDIO_SINK (sink), -1); + + GST_OBJECT_LOCK (sink); + result = sink->priv->alignment_threshold; + GST_OBJECT_UNLOCK (sink); + + return result; +} + static void gst_base_audio_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -686,6 +753,10 @@ gst_base_audio_sink_set_property (GObject * object, guint prop_id, case PROP_DRIFT_TOLERANCE: gst_base_audio_sink_set_drift_tolerance (sink, g_value_get_int64 (value)); break; + case PROP_ALIGNMENT_THRESHOLD: + gst_base_audio_sink_set_alignment_threshold (sink, + g_value_get_uint64 (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -719,6 +790,10 @@ gst_base_audio_sink_get_property (GObject * object, guint prop_id, case PROP_DRIFT_TOLERANCE: g_value_set_int64 (value, gst_base_audio_sink_get_drift_tolerance (sink)); break; + case PROP_ALIGNMENT_THRESHOLD: + g_value_set_uint64 (value, + gst_base_audio_sink_get_alignment_threshold (sink)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1351,9 +1426,8 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, else diff = sink->next_sample - sample_offset; - /* calculate the max allowed drift in units of samples. By default this is - * 40ms and should be anough to compensate for timestamp rounding errors. */ - maxdrift = (ringbuf->spec.rate * sink->priv->drift_tolerance) / GST_MSECOND; + /* calculate the max allowed drift in units of samples. */ + maxdrift = (ringbuf->spec.rate * sink->priv->alignment_threshold) / GST_MSECOND; /* calc align with previous sample */ align = sink->next_sample - sample_offset; diff --git a/gst-libs/gst/audio/gstbaseaudiosink.h b/gst-libs/gst/audio/gstbaseaudiosink.h index 95c0774259..0da0a15c1f 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.h +++ b/gst-libs/gst/audio/gstbaseaudiosink.h @@ -180,6 +180,11 @@ void gst_base_audio_sink_set_drift_tolerance (GstBaseAudioSink *sink, gint64 drift_tolerance); gint64 gst_base_audio_sink_get_drift_tolerance (GstBaseAudioSink *sink); +void gst_base_audio_sink_set_alignment_threshold (GstBaseAudioSink * sink, + GstClockTime alignment_threshold); +GstClockTime + gst_base_audio_sink_get_alignment_threshold (GstBaseAudioSink * sink); + G_END_DECLS #endif /* __GST_BASE_AUDIO_SINK_H__ */ From fbde258be6b3c3b02e051cc5c8ca649570a65b3a Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 21 May 2011 16:16:42 +0300 Subject: [PATCH 29/32] baseaudiosink: use gst_util_uint64_scale_int when appropriate It's probably safer this way. --- gst-libs/gst/audio/gstbaseaudiosink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 837b11e600..c3553da522 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -1427,7 +1427,8 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, diff = sink->next_sample - sample_offset; /* calculate the max allowed drift in units of samples. */ - maxdrift = (ringbuf->spec.rate * sink->priv->alignment_threshold) / GST_MSECOND; + max_sample_diff = gst_util_uint64_scale_int (sink->priv->alignment_threshold, + ringbuf->spec.rate, GST_SECOND); /* calc align with previous sample */ align = sink->next_sample - sample_offset; From 3f1395afaeaee0eead62d72178823b9ee3fd09a1 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Mon, 7 Nov 2011 11:18:34 +0100 Subject: [PATCH 30/32] baseaudiosink: rename some variables --- gst-libs/gst/audio/gstbaseaudiosink.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index c3553da522..a88f7e65af 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -1412,8 +1412,8 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, { GstRingBuffer *ringbuf = sink->ringbuffer; gint64 align; - gint64 diff; - gint64 maxdrift; + gint64 sample_diff; + gint64 max_sample_diff; gint segdone = g_atomic_int_get (&ringbuf->segdone) - ringbuf->segbase; gint64 samples_done = segdone * ringbuf->samples_per_seg; gint64 headroom = sample_offset - samples_done; @@ -1422,9 +1422,9 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, /* now try to align the sample to the previous one, first see how big the * difference is. */ if (sample_offset >= sink->next_sample) - diff = sample_offset - sink->next_sample; + sample_diff = sample_offset - sink->next_sample; else - diff = sink->next_sample - sample_offset; + sample_diff = sink->next_sample - sample_offset; /* calculate the max allowed drift in units of samples. */ max_sample_diff = gst_util_uint64_scale_int (sink->priv->alignment_threshold, @@ -1434,18 +1434,19 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, align = sink->next_sample - sample_offset; /* don't align if it means writing behind the read-segment */ - if (diff > headroom && align < 0) + if (sample_diff > headroom && align < 0) allow_align = FALSE; - if (G_LIKELY (diff < maxdrift && allow_align)) { + if (G_LIKELY (sample_diff < max_sample_diff && allow_align)) { GST_DEBUG_OBJECT (sink, "align with prev sample, ABS (%" G_GINT64_FORMAT ") < %" - G_GINT64_FORMAT, align, maxdrift); + G_GINT64_FORMAT, align, max_sample_diff); } else { gint64 diff_s G_GNUC_UNUSED; /* calculate sample diff in seconds for error message */ - diff_s = gst_util_uint64_scale_int (diff, GST_SECOND, ringbuf->spec.rate); + diff_s = + gst_util_uint64_scale_int (sample_diff, GST_SECOND, ringbuf->spec.rate); /* timestamps drifted apart from previous samples too much, we need to * resync. We log this as an element warning. */ From 0a111bf26e02e19901969db3b3e83082d594ae55 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Mon, 7 Nov 2011 11:31:47 +0100 Subject: [PATCH 31/32] baseaudiosink: delay the resyncing of timestamp vs ringbuffertime A common problem for audio-playback is that the timestamps might not be completely linear. This is specially common when doing streaming over a network, where you can have jittery and/or bursty packettransmission, which again will often be reflected on the buffertimestamps. Now, the current implementation have a threshold that says how far the buffertimestamp is allowed o drift from the ideal aligned time in the ringbuffer. This was an instant reaction, and ment that if one buffer arrived with a timestamp that would breach the drift-tolerance, a resync would take place, and the result would be an audible gap for the listener. The annoying thing would be that in the case of a "timestamp-outlier", you would first resync one way, say +100ms, and then, if the next timestamp was "back on track", you would end up resyncing the other way (-100ms) So in fact, when you had only one buffer with slightly off timestamping, you would end up with *two* audible gaps. This is the problem this patch addresses. The way to "fix" this problem with the previous implementation, would have been to increase the "drift-tolerance" to a value that was greater than the largest timestamp-outlier one would normally expect. The big problem with this approach, however, is that it will allow normal operations with a huge offset timestamp vs running-time, which is detrimental to lip-sync. If the drift-tolerance is set to 200ms, it basically means that lip-sync can easily end up being off by that much. This patch will basically start a timer when the first breach of drift-tolerance is detected. If any following timestamp for the next n nanoseconds gets "back on track" within the threshold, it has basically eliminated the effect of an outlier, and the timer is stopped. If, however, all timestamps within this time-limit are breaching the threshold, we are probably facing a more permanent offset in the timestamps, and a resync is allowed to happen. So basically this patch offers something as rare as both higher accuracy, it terms of allowing smaller drift-tolerances, as well as much smoother, less glitchy playback! Commit message and improvments by Havard Graff. Fixes bug #640859. --- gst-libs/gst/audio/gstbaseaudiosink.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index a88f7e65af..dd531c9190 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -62,6 +62,9 @@ struct _GstBaseAudioSinkPrivate * before resyncing */ guint64 drift_tolerance; + /* time of the previous detected discont candidate */ + GstClockTime discont_time; + /* number of nanoseconds we allow timestamps to drift * before resyncing */ GstClockTime alignment_threshold; @@ -963,6 +966,7 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event) sink->priv->avg_skew = -1; sink->next_sample = -1; sink->priv->eos_time = -1; + sink->priv->discont_time = -1; if (sink->ringbuffer) gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE); break; @@ -1384,6 +1388,7 @@ gst_base_audio_sink_sync_latency (GstBaseSink * bsink, GstMiniObject * obj) sink->priv->avg_skew = -1; sink->next_sample = -1; sink->priv->eos_time = -1; + sink->priv->discont_time = -1; return GST_FLOW_OK; @@ -1418,6 +1423,7 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, gint64 samples_done = segdone * ringbuf->samples_per_seg; gint64 headroom = sample_offset - samples_done; gboolean allow_align = TRUE; + gboolean discont = FALSE; /* now try to align the sample to the previous one, first see how big the * difference is. */ @@ -1437,7 +1443,24 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, if (sample_diff > headroom && align < 0) allow_align = FALSE; - if (G_LIKELY (sample_diff < max_sample_diff && allow_align)) { + /* wait before deciding to make a discontinuity */ + if (G_UNLIKELY (sample_diff >= max_sample_diff)) { + GstClockTime time = gst_util_uint64_scale_int (sample_offset, + GST_SECOND, ringbuf->spec.rate); + if (sink->priv->discont_time == -1) { + /* discont candidate */ + sink->priv->discont_time = time; + } else if (time - sink->priv->discont_time >= GST_SECOND) { + /* one second passed, discontinuity detected */ + discont = TRUE; + sink->priv->discont_time = -1; + } + } else if (G_UNLIKELY (sink->priv->discont_time != -1)) { + /* we have had a discont, but are now back on track! */ + sink->priv->discont_time = -1; + } + + if (G_LIKELY (!discont && allow_align)) { GST_DEBUG_OBJECT (sink, "align with prev sample, ABS (%" G_GINT64_FORMAT ") < %" G_GINT64_FORMAT, align, max_sample_diff); @@ -2045,6 +2068,7 @@ gst_base_audio_sink_change_state (GstElement * element, sink->next_sample = -1; sink->priv->last_align = -1; sink->priv->eos_time = -1; + sink->priv->discont_time = -1; gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE); gst_ring_buffer_may_start (sink->ringbuffer, FALSE); From 3df415d4c7d142cf07f805464ab9f41d098b505f Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Mon, 2 May 2011 13:05:28 +0300 Subject: [PATCH 32/32] baseaudiosink: make discont-wait configurable Now we can configure how much time to wait before deciding that a discont has happened. Also, adds getter and setter to allow derived implementations to set this value upon construction. Suggestions and several improvements by Havard Graff. Signed-off-by: Felipe Contreras --- gst-libs/gst/audio/gstbaseaudiosink.c | 108 ++++++++++++++++++++++---- gst-libs/gst/audio/gstbaseaudiosink.h | 5 ++ 2 files changed, 97 insertions(+), 16 deletions(-) diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index dd531c9190..eb4a0a3a4c 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -62,12 +62,15 @@ struct _GstBaseAudioSinkPrivate * before resyncing */ guint64 drift_tolerance; - /* time of the previous detected discont candidate */ - GstClockTime discont_time; - /* number of nanoseconds we allow timestamps to drift * before resyncing */ GstClockTime alignment_threshold; + + /* time of the previous detected discont candidate */ + GstClockTime discont_time; + + /* number of nanoseconds to wait until creating a discontinuity */ + GstClockTime discont_wait; }; /* BaseAudioSink signals and args */ @@ -86,13 +89,17 @@ enum /* FIXME, enable pull mode when clock slaving and trick modes are figured out */ #define DEFAULT_CAN_ACTIVATE_PULL FALSE +/* when timestamps drift for more than 40ms we resync. This should + * be anough to compensate for timestamp rounding errors. */ +#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) + /* when clock slaving drift for more than 40ms we resync. This is * a reasonable default */ #define DEFAULT_DRIFT_TOLERANCE ((40 * GST_MSECOND) / GST_USECOND) -/* when timestamps drift for more than 40ms we resync. This should - * be anough to compensate for timestamp rounding errors. */ -#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) +/* allow for one second before resyncing to see if the timestamps drift will + * fix itself, or is a permanent offset */ +#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) enum { @@ -103,8 +110,9 @@ enum PROP_PROVIDE_CLOCK, PROP_SLAVE_METHOD, PROP_CAN_ACTIVATE_PULL, - PROP_DRIFT_TOLERANCE, PROP_ALIGNMENT_THRESHOLD, + PROP_DRIFT_TOLERANCE, + PROP_DISCONT_WAIT, PROP_LAST }; @@ -252,6 +260,19 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass) G_MAXINT64, DEFAULT_ALIGNMENT_THRESHOLD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstBaseAudioSink:discont-wait + * + * A window of time in nanoseconds to wait before creating a discontinuity as + * a result of breaching the drift-tolerance. + */ + g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT, + g_param_spec_int64 ("discont-wait", "Discont Wait", + "Window of time in nanoseconds to wait before " + "creating a discontinuity", 0, + G_MAXINT64, DEFAULT_DISCONT_WAIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state); gstelement_class->provide_clock = @@ -292,6 +313,7 @@ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, baseaudiosink->priv->slave_method = DEFAULT_SLAVE_METHOD; baseaudiosink->priv->drift_tolerance = DEFAULT_DRIFT_TOLERANCE; baseaudiosink->priv->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; + baseaudiosink->priv->discont_wait = DEFAULT_DISCONT_WAIT; baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock", (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink); @@ -729,6 +751,50 @@ gst_base_audio_sink_get_alignment_threshold (GstBaseAudioSink * sink) return result; } +/** + * gst_base_audio_sink_set_discont_wait: + * @sink: a #GstBaseAudioSink + * @discont_wait: the new discont wait in nanoseconds + * + * Controls how long the sink will wait before creating a discontinuity. + * + * Since: 0.10.31 + */ +void +gst_base_audio_sink_set_discont_wait (GstBaseAudioSink * sink, + GstClockTime discont_wait) +{ + g_return_if_fail (GST_IS_BASE_AUDIO_SINK (sink)); + + GST_OBJECT_LOCK (sink); + sink->priv->discont_wait = discont_wait; + GST_OBJECT_UNLOCK (sink); +} + +/** + * gst_base_audio_sink_get_discont_wait + * @sink: a #GstBaseAudioSink + * + * Get the current discont wait, in nanoseconds, used by @sink. + * + * Returns: The current discont wait used by @sink. + * + * Since: 0.10.31 + */ +GstClockTime +gst_base_audio_sink_get_discont_wait (GstBaseAudioSink * sink) +{ + GstClockTime result; + + g_return_val_if_fail (GST_IS_BASE_AUDIO_SINK (sink), -1); + + GST_OBJECT_LOCK (sink); + result = sink->priv->discont_wait; + GST_OBJECT_UNLOCK (sink); + + return result; +} + static void gst_base_audio_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -760,6 +826,9 @@ gst_base_audio_sink_set_property (GObject * object, guint prop_id, gst_base_audio_sink_set_alignment_threshold (sink, g_value_get_uint64 (value)); break; + case PROP_DISCONT_WAIT: + gst_base_audio_sink_set_discont_wait (sink, g_value_get_uint64 (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -797,6 +866,9 @@ gst_base_audio_sink_get_property (GObject * object, guint prop_id, g_value_set_uint64 (value, gst_base_audio_sink_get_alignment_threshold (sink)); break; + case PROP_DISCONT_WAIT: + g_value_set_uint64 (value, gst_base_audio_sink_get_discont_wait (sink)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1443,17 +1515,21 @@ gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, if (sample_diff > headroom && align < 0) allow_align = FALSE; - /* wait before deciding to make a discontinuity */ if (G_UNLIKELY (sample_diff >= max_sample_diff)) { - GstClockTime time = gst_util_uint64_scale_int (sample_offset, - GST_SECOND, ringbuf->spec.rate); - if (sink->priv->discont_time == -1) { - /* discont candidate */ - sink->priv->discont_time = time; - } else if (time - sink->priv->discont_time >= GST_SECOND) { - /* one second passed, discontinuity detected */ + /* wait before deciding to make a discontinuity */ + if (sink->priv->discont_wait > 0) { + GstClockTime time = gst_util_uint64_scale_int (sample_offset, + GST_SECOND, ringbuf->spec.rate); + if (sink->priv->discont_time == -1) { + /* discont candidate */ + sink->priv->discont_time = time; + } else if (time - sink->priv->discont_time >= sink->priv->discont_wait) { + /* discont_wait expired, discontinuity detected */ + discont = TRUE; + sink->priv->discont_time = -1; + } + } else { discont = TRUE; - sink->priv->discont_time = -1; } } else if (G_UNLIKELY (sink->priv->discont_time != -1)) { /* we have had a discont, but are now back on track! */ diff --git a/gst-libs/gst/audio/gstbaseaudiosink.h b/gst-libs/gst/audio/gstbaseaudiosink.h index 0da0a15c1f..e41a46a500 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.h +++ b/gst-libs/gst/audio/gstbaseaudiosink.h @@ -185,6 +185,11 @@ void gst_base_audio_sink_set_alignment_threshold (GstBaseAudioSink * sink, GstClockTime gst_base_audio_sink_get_alignment_threshold (GstBaseAudioSink * sink); +void gst_base_audio_sink_set_discont_wait (GstBaseAudioSink * sink, + GstClockTime discont_wait); +GstClockTime + gst_base_audio_sink_get_discont_wait (GstBaseAudioSink * sink); + G_END_DECLS #endif /* __GST_BASE_AUDIO_SINK_H__ */