diff --git a/ChangeLog b/ChangeLog
index 0339e63992d24c043c499bcf7bed6495cc259c71..22bd0f5c0c328aca3eded64e6c6f36d3400f3594 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,108 +1,668 @@
-=== release 1.22.0 ===
+=== release 1.24.12 ===
 
-2023-01-23 19:29:34 +0000  Tim-Philipp Müller <tim@centricular.com>
+2025-01-29 20:12:29 +0000  Tim-Philipp Müller <tim@centricular.com>
 
 	* NEWS:
 	* RELEASE:
-	* docs/gst_plugins_cache.json:
 	* gst-plugins-good.doap:
 	* meson.build:
-	  Release 1.22.0
+	  Release 1.24.12
 
-2023-01-23 16:27:56 +0000  Tim-Philipp Müller <tim@centricular.com>
+2025-01-28 15:08:03 +0100  Alexander Slobodeniuk <aslobodeniuk@fluendo.com>
 
-	* po/hr.po:
-	* po/ro.po:
-	* po/zh_CN.po:
-	  gst-plugins-good: update translations
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3773>
+	* gst/isomp4/gstqtmux.c:
+	  qtmux: fix critical warnings on negotiation error
+	  This pipeline fails to negotiate on my PC:
+	  gst-launch-1.0 v4l2src ! h264parse ! qtmux ! filesink location=t.mp4
+	  When it happens some critical glib warnings are emitted:
+	  -------------------------------
+	  GStreamer-CRITICAL **: 15:09:03.485: gst_mini_object_copy: assertion 'mini_object != NULL' failed
+	  GStreamer-CRITICAL **: 15:09:03.485: gst_mini_object_unref: assertion 'mini_object != NULL' failed
+	  GStreamer-CRITICAL **: 15:09:03.485: gst_caps_get_structure: assertion 'GST_IS_CAPS (caps)' failed
+	  GStreamer-CRITICAL **: 15:09:03.485: gst_structure_set_value: assertion 'structure != NULL' failed
+	  GStreamer-CRITICAL **: 15:09:03.485: gst_mini_object_unref: assertion 'mini_object != NULL' failed
+	  --------------------------------
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8377>
+
+2025-01-04 10:03:42 +0100  Edward Hervey <edward@centricular.com>
+
+	* gst/multifile/gstsplitmuxsink.c:
+	  splitmuxsink: Fix wrong usage of GstClockTime vs GstClockTimeDiff
+	  This could potentially have caused issues (because of the rest of the code using
+	  checks for signed invalid values on a unsigned value)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8293>
 
-2023-01-17 17:51:16 +0200  Sebastian Dröge <sebastian@centricular.com>
+2025-01-04 10:03:12 +0100  Edward Hervey <edward@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/matroska/matroska-demux.c:
-	* gst/matroska/matroska-mux.c:
-	* gst/matroska/webm-mux.c:
-	  matroska: Add `stream-format = (string) obu-stream` to AV1 caps
-	  Anything else is not allowed in Matroska/WebM.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3740>
+	* gst/multifile/gstsplitmuxsrc.c:
+	  splitmuxsrc: Add missing break
+	  This would cause the reconfigure path to be called
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8293>
 
-2023-01-17 17:50:27 +0200  Sebastian Dröge <sebastian@centricular.com>
+2025-01-04 09:59:55 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Add missing break
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8293>
+
+2025-01-04 09:59:34 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  adaptivedemux2: Fix usage of GstClockTime vs GstClockTimeDiff
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8293>
+
+2025-01-02 12:24:03 +0100  Jochen Henneberg <jochen@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/isomp4/gstqtmuxmap.c:
 	* gst/isomp4/qtdemux.c:
-	  isomp4: Add `stream-format = (string) obu-stream` to AV1 caps
-	  Anything else is not allowed in MP4.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3740>
+	  qtdemux: Undef helper macros after use
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8276>
 
-2023-01-17 15:04:10 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+2024-12-18 08:44:30 +0100  Jochen Henneberg <jochen@centricular.com>
 
-	* gst/isomp4/fourcc.h:
 	* gst/isomp4/qtdemux.c:
-	* gst/isomp4/qtdemux_types.c:
-	  qtdemux: Add basic support for AVC-Intra video
-	  AVC-Intra is a range of H.264-compliant intra-only codecs from
-	  Panasonic. The codes and descriptions have been taken from VLC.
-	  The (encumbered) sample I have here produces byte-stream H.264,
-	  including SPS and PPS and no `avcC` box.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3739>
+	  qtdemux: Validate matrix before doing simplified multiply
+	  The matrix multiplication makes some assumption about the element
+	  values to simplify the math with fixpoint values. If this is allowed
+	  for the given matrices is now checked first.
+	  Then the debug output for matrix and a comment have been fixed.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8276>
+
+2024-12-17 10:48:45 +0100  Jochen Henneberg <jochen@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fixup for orientation matrix parsing
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8276>
+
+2024-12-10 21:34:48 +0100  Jochen Henneberg <jochen@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Use mvhd transform matrix and support for flipping
+	  The mvhd matrix is now combined with the tkhd matrix. The combined
+	  matrix is then checked if it matches one of the standard values for
+	  GST_TAG_IMAGE_ORIENTATION.
+	  This check now includes matrices with flipping.
+	  Fixes #4064
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8276>
 
-=== release 1.21.90 ===
+2025-01-07 09:31:26 +0100  Edward Hervey <edward@centricular.com>
 
-2023-01-13 19:08:48 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* gst/multifile/gstsplitmuxsrc.c:
+	  splitmuxsrc: Ensure only a single stream-start event is pushed
+	  Since we are simulating a single output, we want to ensure only a single
+	  stream-start is pushed downstream. We do *not* want to send a (potentially) new
+	  stream start event after flushing (like after seeks).
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4146
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8284>
+
+2025-01-09 17:12:54 +0000  Will Miller <will.miller@pexip.com>
+
+	* gst/rtp/gstrtpvp9pay.c:
+	* tests/check/elements/rtpvp9.c:
+	  rtpvp9pay: fix profile parsing
+	  Incorrect parsing of these bits meant that we were incorrectly parsing
+	  the VP9 uncompressed bitstream header for some profiles, as the header
+	  is of variable length and format depending on the profile. Amongst
+	  various unintended effects, this caused the width and height from the SS
+	  to be incorrectly parsed and set in the caps.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8283>
+
+2024-11-08 12:06:28 +0100  Piotr Brzeziński <piotr@centricular.com>
+
+	* sys/osxaudio/gstosxaudiosrc.c:
+	* sys/osxaudio/gstosxcoreaudio.h:
+	  osxaudiosrc: Work around timestamps on iOS not starting from 0
+	  On macOS, you always get your own 'timeline' for the AudioUnit session, so timestamps start from 0.
+	  On iOS however, AudioUnit seems to give you a 'shared' timeline so timestamps start at a later, non-0 point in time.
+	  Simply offsetting seems to do the trick.
+	  This was causing osxaudiosrc to not output any sound on iOS.
+	  Regressed in 2df9283d3f2ea06af5ebd6db03a6d545cac52f19
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8263>
+
+2024-11-08 12:02:23 +0100  Piotr Brzeziński <piotr@centricular.com>
+
+	* sys/osxaudio/gstosxcoreaudiocommon.c:
+	  osxaudiosrc: Fix render callback removal when pausing/stopping
+	  At least on iOS, the 'input' callback kept being called after going to PAUSED.
+	  Specifying the right type (like in gst_core_audio_io_proc_start()) fixes that.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8263>
+
+2024-11-08 11:57:49 +0100  Piotr Brzeziński <piotr@centricular.com>
+
+	* sys/osxaudio/gstosxcoreaudiocommon.c:
+	  osxaudio: Fix AudioOutputUnitStart() deadlock on iOS >=17
+	  At some point in iOS 17, this call started waiting for the first render callback (io_proc) to finish.
+	  In our case, that callback also takes the ringbuf object lock by calling gst_audio_ring_buffer_set_timestamp(),
+	  which results in a deadlock.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8263>
+
+2025-01-06 20:11:58 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* meson.build:
+	  Back to development after 1.24.11
+
+=== release 1.24.11 ===
+
+2025-01-06 19:48:08 +0000  Tim-Philipp Müller <tim@centricular.com>
 
 	* NEWS:
 	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.11
+
+2025-01-06 20:30:51 +0800  Dean Zhang (张安迪) <dean.zhang@mediatek.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2videodec: release decode only frame in input list
+	  For some frames with decode-only flag, the v4l2 decoder will not
+	  put them in output list. The corresponding decode-only frames will
+	  be still kept in input list, which may cause potential performance
+	  issue when the input list is full. So release the decode-only frames
+	  according to the decode-only flag after they are processed by decoder
+	  driver.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8247>
+
+2024-12-31 11:49:21 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-mux.c:
+	* tests/check/elements/matroskamux.c:
+	  matroskamux: Consider audio buffers as keyframes when writing out simpleblocks
+	  Otherwise mpv complains and considers the file broken.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4142
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8246>
+
+2024-12-31 11:42:04 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-mux.c:
+	  matroskamux: Fix audio-only stream conditions
+	  The num_a_streams and related counters are used for pad numbers and don't give
+	  the absolute number of streams in this run of the muxer.
+	  Also, consider the output audio-only if there are more than 1 audio stream too.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4142
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8246>
+
+2024-12-17 20:08:24 +0100  Christian Meissl <meissl.christian@gmail.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: fix accumulated base offset in segment seeks
+	  analog to fix for matroska-demux
+	  commit f3c126d07c8a85e76bf5abdfa7f140bbf20545ea
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8244>
+
+2024-12-21 14:39:58 +0100  Robert Mader <robert.mader@collabora.com>
+
 	* docs/gst_plugins_cache.json:
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Add P010 format
+	  For 10bit content. Tested with HEVC on a Pixel3a (qcom).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8197>
+
+2024-12-12 12:05:04 +1100  Matthew Waters <matthew@centricular.com>
+
+	* gst/rtp/gstrtppassthroughpay.c:
+	  rtppassthroughpay: ensure buffer is writable before mapping writable
+	  It is entirely possible that the incoming buffer into _chain() is not writable
+	  and will result in a critical when trying to map().
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8137>
+
+2024-12-04 16:10:46 +0000  Philippe Normand <philn@igalia.com>
+
+	* gst/rtpmanager/gstrtpsession.c:
+	* gst/rtpmanager/rtpsession.c:
+	* gst/rtpmanager/rtpsession.h:
+	  rtpsession: Fix twcc stats structure leaks
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8115>
+
+2024-12-03 23:39:54 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* meson.build:
+	  Back to development after 1.24.10
+
+=== release 1.24.10 ===
+
+2024-12-03 23:29:07 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* NEWS:
+	* RELEASE:
 	* gst-plugins-good.doap:
 	* meson.build:
-	  Release 1.21.90
+	  Release 1.24.10
+
+2024-09-27 00:31:36 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Add size check for parsing SMI / SEQH atom
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-244
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3853
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-26 19:16:19 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Check for invalid atom length when extracting Closed Caption data
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-243
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3849
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-27 10:39:30 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Actually handle errors returns from various functions instead of ignoring them
+	  Ignoring them might cause the element to continue as if all is fine despite the
+	  internal state being inconsistent. This can lead to all kinds of follow-up
+	  issues, including memory safety issues.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-245
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3847
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-27 10:38:50 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Make sure there are enough offsets to read when parsing samples
+	  While this specific case is also caught when initializing co_chunk, the error
+	  is ignored in various places and calling into the function would lead to out of
+	  bounds reads if the error message doesn't cause the pipeline to be shut down
+	  fast enough.
+	  To avoid this, no matter what, make sure enough offsets are available when
+	  parsing them. While this is potentially slower, the same is already done in the
+	  non-chunks_are_samples case.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-245
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3847
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-27 09:47:50 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix error handling when parsing cenc sample groups fails
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-238, GHSL-2024-239, GHSL-2024-240
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3846
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-27 00:12:57 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix length checks and offsets in stsd entry parsing
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-242
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3845
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-26 14:17:02 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Make sure enough data is available before reading wave header node
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-236
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3843
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-26 09:20:28 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Make sure only an even number of bytes is processed when handling CEA608 data
+	  An odd number of bytes would lead to out of bound reads and writes, and doesn't
+	  make any sense as CEA608 comes in byte pairs.
+	  Strip off any leftover bytes and assume everything before that is valid.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-195
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3841
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-27 15:50:54 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Check sizes of stsc/stco/stts before trying to merge entries
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-246
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3854
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-26 18:41:39 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux_dump.c:
+	  qtdemux: Don't iterate over all trun entries if none of the flags are set
+	  Nothing would be printed anyway.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-26 18:40:56 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix debug output during trun parsing
+	  Various integers are unsigned so print them as such. Also print the actual
+	  allocation size if allocation fails, not only parts of it.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-09-26 18:39:37 +0300  Antonio Morales <antonio-morales@github.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix integer overflow when allocating the samples table for fragmented MP4
+	  This can lead to out of bounds writes and NULL pointer dereferences.
+	  Fixes GHSL-2024-094, GHSL-2024-237, GHSL-2024-241
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3839
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
+
+2024-10-09 11:52:52 -0400  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-demux.c:
+	  matroskademux: Put a copy of the codec data into the A_MS/ACM caps
+	  The original codec data buffer is owned by matroskademux and does not
+	  necessarily live as long as the caps.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-280
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3894
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-09-30 19:19:42 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-ids.c:
+	  matroskademux: Skip over zero-sized Xiph stream headers
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-251
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3867
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-09-30 19:06:03 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-demux.c:
+	  matroskademux: Skip over laces directly when postprocessing the frame fails
+	  Otherwise NULL buffers might be handled afterwards.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-249
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3865
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-09-30 19:04:51 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-demux.c:
+	  matroskademux: Don't take data out of an empty adapter when processing WavPack frames
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-249
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3865
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-09-30 18:25:53 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-demux.c:
+	  matroskademux: Check for big enough WavPack codec private data before accessing it
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-250
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3866
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-09-30 16:33:39 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-demux.c:
+	  matroskademux: Fix off-by-one when parsing multi-channel WavPack
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-09-30 16:32:48 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-demux.c:
+	  matroskademux: Only unmap GstMapInfo in WavPack header extraction error paths if previously mapped
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-197
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3863
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
+
+2024-10-04 14:04:03 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2023-01-12 14:32:30 -0500  Olivier Crête <olivier.crete@ocrete.ca>
+	* gst/avi/gstavisubtitle.c:
+	  avisubtitle: Fix size checks and avoid overflows when checking sizes
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-262
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3890
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8055>
 
-	* gst/rtp/gstrtpopuspay.c:
-	  rtopuspay: Use GstStaticCaps to cache parsed caps
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3674>
+2024-10-04 13:51:00 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2023-01-12 14:25:52 -0500  Olivier Crête <olivier.crete@ocrete.ca>
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Check size before reading ds64 chunk
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-261
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3889
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
+
+2024-10-04 13:27:27 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Fix clipping of size to the file size
+	  The size does not include the 8 bytes tag and length, so an additional 8 bytes
+	  must be removed here. 8 bytes are always available at this point because
+	  otherwise the parsing of the tag and length right above would've failed.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-260
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3888
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
+
+2024-10-04 13:22:02 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Check that at least 32 bytes are available before parsing smpl chunks
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-259
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3887
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
+
+2024-10-04 13:21:44 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Check that at least 4 bytes are available before parsing cue chunks
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
+
+2024-10-04 13:15:27 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Fix parsing of acid chunk
+	  Simply casting the bytes to a struct can lead to crashes because of unaligned
+	  reads, and is also missing the endianness swapping that is necessary on big
+	  endian architectures.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
+
+2024-10-04 13:09:43 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Make sure enough data for the tag list tag is available before parsing
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-258
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3886
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
 
-	* gst/rtp/gstrtpopuspay.c:
-	* tests/check/elements/rtpopus.c:
-	  rtopuspay: Ignore the stereo parameter in multiopus caps
-	  Also add unit tests for the various variants
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3674>
+2024-10-04 13:00:57 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2023-01-03 18:55:24 -0500  Olivier Crête <olivier.crete@collabora.com>
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: Check for short reads when parsing headers in pull mode
+	  And also return the actual flow return to the caller instead of always returning
+	  GST_FLOW_ERROR.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-258, GHSL-2024-260
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3886
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3888
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
+
+2024-10-02 14:44:21 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* ext/gdk_pixbuf/gstgdkpixbufdec.c:
+	  gdkpixbufdec: Check if initializing the video info actually succeeded
+	  Otherwise a 0-byte buffer would be allocated, which gives NULL memory when
+	  mapped.
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-118
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3876
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8053>
+
+2024-09-30 16:22:19 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* ext/jpeg/gstjpegdec.c:
+	  jpegdec: Directly error out on negotiation failures
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-247
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3862
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8052>
 
-	* gst/rtp/gstrtpopuspay.c:
-	  rtpopuspay: Leave original caps as-is
-	  This should make it work if someone specifies stereo with MULTIOPUS
-	  somehow.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3674>
+2024-09-26 22:16:06 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2023-01-03 18:06:56 -0500  Olivier Crête <olivier.crete@collabora.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Avoid integer overflow when parsing Theora extension
+	  Thanks to Antonio Morales for finding and reporting the issue.
+	  Fixes GHSL-2024-166
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3851
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8044>
+
+2024-11-29 13:41:54 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* gst/level/gstlevel.c:
+	  level: Fix integer overflow when filling LevelMeta
+	  The level in GstAudioLevelMeta is represented as a signed 8bit value from 0 to
+	  127 (with 127 meaning silence). When converting from double, make sure to clip
+	  the value, this also prevent integer overflow in the conversion. This fixes an
+	  issue where a lower then -127db is reported and random level with near silent
+	  streams (due to integer overflow).
+	  Fixes #4068
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8029>
+
+2024-11-28 12:56:33 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* gst/rtp/gstrtpopuspay.c:
-	  rtpopuspay: Return upstream channel filter based on OPUS vs MULTICAPS
-	  Only allow 1 or 2 channels if the caps are OPUS, or 3+ if they are
-	  MULTIOPUS.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3674>
+	* ext/shout2/gstshout2.c:
+	  shout2send: Unref event at the end of the event function
+	  The function takes ownership of it and should get rid of it at the end.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8006>
 
-2023-01-03 17:44:37 -0500  Olivier Crête <olivier.crete@collabora.com>
+2024-04-03 15:01:52 +0300  Sebastian Dröge <sebastian@centricular.com>
 
 	* docs/gst_plugins_cache.json:
-	* gst/rtp/gstrtpopusdepay.c:
-	* gst/rtp/gstrtpopuspay.c:
-	  rtpopus: Put MULTIOPUS in all caps
-	  The RTP payload encoding-name are always in caps in GStreamer.
-	  In SDP, they are not case-sensitive, but since caps are, we need to pick
-	  a caps, and we picked upper-case along time ago.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3674>
+	* gst/rtsp/gstrtspsrc.c:
+	* gst/rtsp/gstrtspsrc.h:
+	  rtspsrc: Optionally timestamp RTP packets with their receive times in TCP/HTTP mode
+	  Until https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6509
+	  this was accidentally done inside rtpjitterbuffer for many years, and
+	  doing so potentially solves problems on some streams while introducing
+	  problems on others.
+	  Make this configurable on rtspsrc and default to not set timestamps.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8004>
+
+2024-11-27 12:16:37 +0100  Jonas Rebmann <jre@pengutronix.de>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2videodec: fix freeze race condition
+	  This fixes a possible deadlock between gst_v4l2_video_dec_change_state
+	  and gst_v4l2_video_dec_loop on the buffer pool.
+	  When stopping capture, the flushing state of the v4l2 capture buffer
+	  pool gets reverted in the processing loop after it was set via
+	  gst_v4l2_object_unlock (self->v4l2capture) (in
+	  gst_v4l2_video_dec_change_state). As a result, gst_v4l2_video_dec_loop
+	  does not return and consequently, gst_pad_stop_task gets stuck waiting
+	  for the GST_PAD_STREAM_LOCK. To circumvent this, skip acquiring the
+	  buffer pool if stopping capture.
+	  Suggested-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7987>
+
+2024-11-22 18:59:53 +1100  Matthew Waters <matthew@centricular.com>
+
+	* ext/qt/gstqsgmaterial.cc:
+	* ext/qt6/gstqsg6material.cc:
+	  qt(6)/material: ensure that we always update the context in setBuffer()
+	  Scenario is that there are two (or more) GstGLContext's wrapping Qt's GL
+	  context from either multiple qml(6)glsink or qml(6)glsrc elements.  Call flow is this:
+	  1. material 1 setBuffer()
+	  2. material 1 bind()
+	  3. material 2 setBuffer()
+	  4. material 2 bind()
+	  If the call to setBuffer() reuses the same buffer as previous call, then the
+	  qt context is not updated in the material.  If however the previously used qt
+	  context by the material had been deactivated or freed, then bind() would fail
+	  and could result in a critical like so:
+	  gst_gl_context_thread_add: assertion 'context->priv->active_thread == g_thread_self ()' failed
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7978>
+
+2024-11-25 15:47:22 +0100  Tomáš Polomský <1155369-polomsky@users.noreply.gitlab.freedesktop.org>
+
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Fixed incorrect maximum value for int range
+	  There are objects where maximum is not multiplication of the step,
+	  e.g. there was a combination where max was 65535 with step 2.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7961>
+
+2024-11-25 14:25:52 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/multifile/gstsplitmuxpartreader.c:
+	* gst/multifile/gstsplitmuxpartreader.h:
+	  splitmuxsrc: Convert part reader to a bin with a non-async bus
+	  A pipeline always has an async bus, which involves allocating an fd pair. As
+	  splitmuxsrc only uses the bus' sync handler, this is not required and can easily
+	  cause splitmuxsrc to exceed the fd limit for no good reason.
+	  The other features of GstPipeline are also not needed here, e.g. clock selection.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7957>
+
+2024-11-15 11:44:17 +0100  Albert Sjolund <alberts@axis.com>
+
+	* gst/rtpmanager/gstrtphdrext-twcc.c:
+	  rtpmanager: don't map READWRITE in twcc header ext
+	  There is no need to map the buffer as writable, as there is
+	  only a read performed on the mapped buffer. This is in line
+	  with other header extensions, as no other extensions maps
+	  it as readwrite.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7910>
+
+2020-09-21 16:48:38 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
 
-2023-01-11 15:41:09 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* gst/flv/gstflvmux.c:
+	  flvmux: Mux timestampless buffers immediately
+	  Instead of leaving them queued indefinitely, or until we're timing out
+	  and it's the only buffer queued.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7902>
+
+2024-11-12 11:25:38 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/flv/gstflvmux.c:
+	  flvmux: Don't time out in live mode if no timestamped next buffer is available
+	  But also don't wait for a buffer on both pads, which might take forever in case
+	  of gaps in one of the streams.
+	  The muxer can only advance the time if it has a timestamped buffer that can be
+	  output, otherwise it will just busy-wait and use up a lot of CPU.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7902>
+
+2024-11-13 15:49:57 +0100  Robert Rosengren <robertr@axis.com>
+
+	* gst/udp/gstudpsrc.c:
+	  udpsrc: protect cancellable from unlock/unlock_stop race
+	  Protect cancellable from simultaneous unlock and unlock_stop calls from
+	  basesrc class.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7897>
+
+2024-11-04 10:49:25 +0100  Stefan Riedmüller <s.riedmueller@phytec.de>
+
+	* docs/gst_plugins_cache.json:
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Remove little endian marker on 8 bit bayer format names
+	  There is no point in having an endian marker on 8 bit bayer format names since
+	  it is just one byte. Thus remove it.
+	  This also fixes an incompatibility with plugins bad where there is no endian
+	  marker on 8 bit bayer format names as well.
+	  Fixes: #3729
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7845>
+
+2024-11-05 11:49:32 +0000  Philippe Normand <philn@igalia.com>
+
+	* gst/rtpmanager/gstrtpfunnel.c:
+	* tests/check/elements/rtpfunnel.c:
+	  rtpfunnel: Ensure segment events are forwarded after flushs
+	  gst_rtp_funnel_forward_segment() returns early when the current_pad is set.
+	  Without clearing current_pad a critical warning would be emitted when
+	  attempting to chain a buffer following a flush.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7835>
+
+2024-11-03 17:36:46 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* po/LINGUAS:
 	* po/af.po:
 	* po/az.po:
 	* po/bg.po:
@@ -148,4938 +708,6596 @@
 	* po/zh_HK.po:
 	* po/zh_TW.po:
 	  gst-plugins-good: update translations
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3711>
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7820>
 
-2023-01-11 14:53:39 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-10-31 17:46:40 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* scripts/dist-translations.py:
-	* scripts/meson.build:
-	  Fix translation pot files when creating dist tarballs
-	  Add version as per Translation Project requirements and
-	  also add a .pot file without the ABI suffix.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3711>
+	* gst/rtp/gstrtph264depay.c:
+	* gst/rtp/gstrtph265depay.c:
+	  rtph264depay, rtph265depay: various parameter-set string handling fixes
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7812>
 
-2023-01-07 20:03:31 +0100  Marek Vasut <marex@denx.de>
+2024-10-30 20:40:12 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/jpeg/gstjpegdec.c:
-	  jpegdec: Disable libjpeg-turbo SIMD acceleration support for now
-	  The libjpeg-turbo SIMD acceleration support suffers from multiple
-	  unresolved cornercases. Disable the libjpeg-turbo for now until
-	  those cornercases are resolved.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3694>
+	* meson.build:
+	  Back to development after 1.24.9
 
-2022-08-07 14:23:04 +1000  Jan Schmidt <jan@centricular.com>
+=== release 1.24.9 ===
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux: Consider starting stream time in presentation offset
-	  When calculating the presentation offset for CMAF input in live
-	  playback, subtract the stream_time of the fragment from the
-	  calculated presentation offset, so that the first fragment
-	  is played at running time zero.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3680>
+2024-10-30 20:33:30 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-12-22 21:23:39 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.9
 
-	* ext/qt/meson.build:
-	* ext/qt6/meson.build:
-	* meson_options.txt:
-	  meson: Add an option to select the method for finding Qt
-	  This is needed by Cerbero, since we want to force the use of qmake to
-	  find Qt on non-Linux platforms.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3628>
+2024-10-29 17:39:02 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-12-26 23:00:18 +0900  Seungha Yang <seungha@centricular.com>
+	* gst/flv/gstflvmux.c:
+	  flvmux: Consider timestamps before segment start to map to segment start
+	  Instead of mapping them to running time 0, which is wrong if e.g. the segment
+	  base is not equal to 0.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7798>
 
-	* ext/gtk/gstgtkbasesink.c:
-	  gtkbasesink: Fix widget leak
-	  gst_gtk_base_sink_get_widget() will increase refcount and it should
-	  be released after use
-	  Fixing regression introduced by the commit
-	  941c0e81ddf5c0ad6a9fac59b1d6bf7723025434
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3644>
+2024-10-29 15:30:59 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-12-27 23:33:31 +0900  Seungha Yang <seungha@centricular.com>
+	* gst/flv/gstflvmux.c:
+	  flvmux: Use first running time on the initial header instead of 0
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7798>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Fix string leak
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3645>
+2024-08-08 12:28:11 +0000  Johan Sternerup <johast@axis.com>
 
-2022-12-27 23:26:48 +0900  Seungha Yang <seungha@centricular.com>
+	* gst/rtpmanager/rtptwcc.c:
+	* tests/check/elements/rtpsession.c:
+	  twcc: Handle wrapping of reference time
+	  Previously the wrapping of the 24-bit reference time was not handled
+	  correctly when transforming it into GstClockTime. Given the unit of 64ms
+	  the span that could be represented by 24 bits is 12 days and depending
+	  on the start value we could get a wrapping problem anytime within this
+	  time frame. This turned out to be particularly problematic for the GCC
+	  algorithm in gst-plugins-rs which tried to evict old packages based on
+	  the "oldest" timestamp, which due to wrapping problems could be in the
+	  future. Thus, the container managing the packets could grow without
+	  limits for a long time thereby creating both CPU and memory problems.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7792>
+
+2024-10-29 16:43:33 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/rtpmanager/rtptimerqueue.c:
-	  rtptimerqueue: Fix memory leak
-	  Should chain up to parent's finalize
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3645>
+	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtppassthroughpay.c:
+	  rtppassthrough: fix rtp-stats message compatibility with GstRTPBasePayload
+	  "clock-rate" and "pt" are G_TYPE_UINT in the base class, so let's
+	  keep them like that here too, since the entire purposes of the
+	  passthrough element is to fake being a payloader. The types in the
+	  message don't have to be consistent with the types in the caps.
+	  Reverts part of commit a6fa53b7 of !7526
+	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7552#note_2576653
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7784>
+
+2024-10-25 12:02:54 +0200  Ognyan Tonchev <ognyan@axis.com>
 
-2022-12-22 11:16:26 +0100  Patricia Muscalu <patricia@axis.com>
+	* gst/rtpmanager/rtpsession.c:
+	* gst/rtpmanager/rtpsource.c:
+	  rtpmanager: skip RTPSources which are not ready in the RTCP generation
+	  If a stream has an 'irregular' frame rate (e.g. metadata) RTCP SR
+	  may be generated way too early, before the RTPSource has received
+	  the first packet after Latency was configured in the pipeline.
+	  We skip such RTPSources in the RTCP generation.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7777>
 
-	* gst/isomp4/gstqtmux.c:
-	  qtmux: Fix buffer leak in fragment_buffers
-	  When pushing buffers from one of the sink pads fail,
-	  make sure that all buffers added to fragment_buffers on other pads
-	  are freed as well.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3624>
+2024-10-03 12:48:31 +0200  Andoni Morales Alastruey <ylatuya@gmail.com>
 
-2022-09-19 21:11:39 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: fix parsing of matrix with 180 rotation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7665>
 
-	* gst/isomp4/atoms.c:
-	  qtmux: For video with N/1001 framerates use N as timescale instead of centiframes
-	  This is recommended by various specifications for such framerates, while
-	  for integer framerates we continue using centiframes to allow for some
-	  more accuracy.
-	  Using N means that no rounding error accumulates, eventually leading to
-	  outputting a packet with a different duration.
-	  Some tools such as MediaInfo determine that a stream is variable
-	  framerate if any packet has a different duration than the others, and
-	  there is no reason I can see for not using the full 4 bytes of
-	  resolution that the mp4 timescale offers.
-	  Example problematic pipeline:
-	  ```
-	  videotestsrc num-buffers=5001 ! video/x-raw,framerate=60000/1001,width=320,height=240 ! \
-	  videoconvert ! x264enc bitrate=80000 speed-preset=1 tune=zerolatency ! h264parse ! \
-	  video/x-h264,profile=high-10 ! mp4mux ! filesink location="result2.mp4"
-	  ```
-	  This results in a media file that MediaInfo detects as variable
-	  framerate because the 5000th packet has duration 99 instead of 100.
-	  With this patch, the timescale is 60000 and all packets have duration
-	  1001.
-	  Related issue for context: https://bugzilla.gnome.org/show_bug.cgi?id=769041
-	  Co-authored-by: Sebastian Dröge <sebastian@centricular.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3049>
+2024-09-26 09:15:34 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-12-21 02:37:58 +1100  Jan Schmidt <jan@centricular.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Check fourcc of a second CEA608 atom instead of assuming it's cdt2
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7583>
 
-	* ext/qt/qtwindow.cc:
-	  qmlglsrc: Handle HiDPI scaling
-	  When calculating the capture framebuffer size, include
-	  any device scaling applied to the rendered framebuffer
-	  Fixes only capturing part of the window when there is
-	  a global scale factor.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3612>
+2024-09-23 16:48:26 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-12-21 02:32:41 +1100  Jan Schmidt <jan@centricular.com>
+	* docs/gst_plugins_cache.json:
+	  doc: good: Update documentation cache
+	  video4linux2 plugin now maps RGB15 which his didn't before.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-	* ext/qt/qtwindow.cc:
-	  qmlglsrc: Unmap buffer before adding sync meta
-	  Adding a sync meta to a GstBuffer requires that it
-	  be writable. Mapping the buffer with the video frame API
-	  holds an extra ref on the buffer, so unmap before
-	  trying to modify it.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3612>
+2024-09-18 13:14:32 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-12-21 02:31:05 +1100  Jan Schmidt <jan@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Fix a gvalue leak on error
+	  In case we failed enumerating the supported interlacing mode, we leaked the
+	  gvalue.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-	* ext/qt/gstqtsrc.cc:
-	  qmlglsrc: Stop when basesrc calls unlock()
-	  Instead of stopping capture when the state changes,
-	  handle other cases of basesrc stopping capture by - such
-	  as handling an EOS event - by implementing an unlock()
-	  method
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3612>
+2024-09-17 14:27:46 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-12-16 17:27:33 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2videodec.c:
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2: dec/enc: Flag leaked caps
+	  We never free class held template caps, so flag the one that wasn't already
+	  flagged.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Always use `tfdt` if available in BYTE segments
-	  This reverts the decision from
-	  https://bugzilla.gnome.org/show_bug.cgi?id=754230
-	  where it was decided that we rather play safe and only use the `tfdt` if
-	  it is "significantly different" to the sum of sample durations.
-	  As the specification says
-	  If the time expressed in the track fragment decode time (‘tfdt’) box
-	  exceeds the sum of the durations of the samples in the preceding
-	  movie and movie fragments, then the duration of the last sample
-	  preceding this track fragment is extended such that the sum now
-	  equals the time given in this box.
-	  we have to use the `tfdt` in general to allow for it to signal gaps in
-	  the stream.
-	  A muxer producing fragments might not yet know the full duration of the
-	  last sample of a previous fragment if the next fragment starts with a
-	  gap, and knowing the actual start of the next fragment would potentially
-	  require to violate latency requirements.
-	  Additionally, the existence of `tfdt` allows to avoid accumulating
-	  rounding errors from summing up the durations.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3586>
-
-2022-12-16 01:00:46 -0600  A. Wilcox <AWilcox@Wilcox-Tech.com>
+2024-08-15 16:54:25 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* tests/check/elements/rtpjitterbuffer.c:
-	  tests: Cast drop-messages-interval type properly
-	  The rtpjitterbuffer test drop_messages_interval uses a GstClockTime for
-	  the message drop interval.  This property is defined as a guint.  On
-	  systems with 64-bit time_t but 32-bit uint, this can cause the
-	  g_object_set function to fail to read the arguments properly.
-	  Fixes: #1656
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3580>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Fix condition check to emit error
+	  The check was reversed, so we could only emit a pipeline error
+	  if there was no element associated with the object.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-2022-12-13 12:23:56 +0100  Thibault Saunier <tsaunier@igalia.com>
+2024-09-17 13:28:45 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/qt/qtitem.cc:
-	* ext/qt6/qt6glitem.cc:
-	  base:navigation: Cleanup navigation key modifiers enum
-	  We were exposing the 'ALT' modifier as if we were guaranteeing its
-	  accuracy but truth is we were only exposing configuration dependent
-	  values.
-	  Make the API simpler for now, the same way as Gtk3 was exposing it, and
-	  when we have time to guarantee more values by making them take backends
-	  configuration into account, we will expose those values in a accurate
-	  way.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1402
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3565>
-
-2022-12-09 11:35:25 +0100  Xabier Rodriguez Calvar <calvaris@igalia.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Always tell capture queue that we want to set the CSC
+	  Not all drivers supports it, but in general we want to try and match the
+	  negotiated caps, so lets always try to set the CSC.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Clear protection events when we get new ones
-	  If we keep the old events they can be end up being passed to the app, that could
-	  discard the protection information because it has been seen before.
-	  Drive by improvement: use g_queue_clear_full instead of foreach+clear for
-	  protection events.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3547>
+2024-08-15 16:01:03 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-05-22 10:46:12 +0200  Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Fix support for format:Interlaced in caps probe
+	  This notably follow the way we order the template and keeps the
+	  format:Interlaced caps at the end. This change also fixes
+	  an early skip check, that would skip if a driver only supports
+	  alternate interlacing for a specific format. It also fixes
+	  a bug where only the last resolution of a discrete frame size
+	  was allowed to use format:Interlaced. Finally, similar to template
+	  caps code, simplify the caps for earch featurs, making the debug output
+	  manageable and (marginally) improve negotiation speed.
+	  This change will make it easier to introduce memory:DMABuf.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
+
+2024-08-15 13:07:31 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/jpeg/gstjpegdec.c:
-	  jpegdec: Enable packetized if sink caps contains parsed as true.
-	  jpegdec is capable to parse input frames, but if jpegparse is before,
-	  there's no need to reparse frames. This patch configure jpegdec as
-	  packetized, skipping parsing, if negotiated sink caps has the boolean
-	  field 'parsed' as true.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2464>
+	* sys/v4l2/gstv4l2.c:
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2: Move M2M template caps probe into v4l2object
+	  This allow reusing the code that produces output and capture devices
+	  templates. This fixes the lack of Interlaced caps feature for M2M
+	  devices such as decoder, encoder or converters.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-2022-09-27 09:44:51 +0000  Henry Hoegelow <henry@hoegelow.com>
+2024-08-14 15:26:58 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/pulse/pulsesink.c:
-	  pulsesink: Fix occasional period of silence on resume
-	  According to comment in gst_pulsering_stream_latency_cb, latency updates
-	  happen every 100 ms. The code in gst_pulsering_stream_latency_cb does
-	  not care about the actual state of the ringbuffer, pbuf->acquired or
-	  not.
-	  Thus, every 100 ms the ringbuf->segdone may be set, even though the
-	  object itself might be in 'destroyed' state. On next
-	  gst_pulseringbuffer_acquire the segdone is not touched, so playback may
-	  resume with invalid/wrong segdone value. This finally leads to a period
-	  of silence playing after resuming the pipeline.
-	  The problem was found on 'not-yet-released'-hardware and so far was not
-	  reproducible on desktop computer.
-	  Removing the callback as long as the ringbuffer is not in 'acquired'
-	  state solves the problem reliably on the hardware device that the issue
-	  was detected on.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3082>
-
-2021-05-14 23:06:43 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Remove over indentation
+	  This is a style fix, no functional changes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtp/gstrtpvp9depay.c:
-	* gst/rtp/gstrtpvp9depay.h:
-	  rtpvp9depay: expose keyframe-related properties
-	  This simply brings in the wait-for-keyframe and request-keyframe
-	  properties from rtpvp8depay.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/909>
+2024-08-14 15:21:44 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Map GST/V4L2 formats in a C array
+	  This makes it easier to add new format in the future without
+	  forgetting to update one of the numerous switch case. This
+	  will also help mapping DRM formats.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-2022-12-07 09:47:49 +0100  Jacek Skiba <jacek.skiba@consult.red>
+2024-08-14 10:13:44 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: exit when protection caps are not defined during PIFF parsing
-	  Reproduction testcase (uses PlayReady):
-	  https://developers.canal-plus.com/rx-player/upc/?appTileLocation=[object%20Object]
-	  In test streams we are using PIFF box, but caps did not had
-	  present GST_PROTECTION_SYSTEM_ID_CAPS_FIELD. In consequence, invalid
-	  system_id was returned which caused SIGSEGV crash.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3535>
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2object: Expose convertion from v4l2 fourcc to GstVideoFormat
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
 
-2022-12-05 10:55:57 +0100  Edward Hervey <edward@centricular.com>
+2024-08-14 09:52:54 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Don't allow stream selection while switching periods
-	  The stream selection is done on the currently outputting tracks, but in order to
-	  (de)activate the backing streams we can only do it if the input and output
-	  period are identical.
-	  Fixes crash when doing stream selection during period migration
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3525>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Change dimensions format desc field to flag
+	  The boolean naming wasn't obvious, and having this as a flag makes
+	  the structure a little more compact.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7563>
+
+2024-07-29 09:07:40 +0800  Shengqi Yu <shengqi.yu@mediatek.com>
+
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: append non colorimetry structure to probed caps
+	  If the stream has a special colorimetry that is not in the colorimetry
+	  list, it will cause negotiation to fail. We should allow passing any
+	  colorimetry, so add an extra structure without the colorimetry field.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7570>
+
+2024-09-24 09:50:34 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-12-05 02:29:08 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Skip zero-sized boxes instead of stopping to look at further boxes
+	  A zero-sized box is not really a problem and can be skipped to look at any
+	  possibly following ones.
+	  BMD ATEM devices specifically write a zero-sized bmdc box in the sample
+	  description, followed by the avcC box in case of h264. Previously the avcC box
+	  would simply not be read at all and the file would be unplayable.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7565>
+
+2024-08-13 15:07:07 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* ext/qt6/qt6glwindow.cc:
+	* ext/qt6/qt6glwindow.h:
+	  qml6glsrc: Reduce capture delay
+	  In qml6glsrc, we capture the application by copying the back buffer into
+	  our own FBO. The afterRendering() signal is too soon as from the apitrace, the
+	  application has been rendered into a QT internal buffer, to be used as a cache
+	  for refresh.
+	  Use afterFrameEnd() signal instead. This works with no delay on GLES. With GL
+	  it seems to reduce from 2 to 1 frame delay (this may be platform specific). A
+	  different recording technique would need to be used to completely remove this
+	  delay.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7562>
+
+2024-09-18 12:34:39 +0200  Piotr Brzeziński <piotr@centricular.com>
 
 	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtppassthroughpay.c:
+	* gst/rtp/gstrtppassthroughpay.h:
+	  rtppassthroughpay: Fix reading clock-rate and payload type from caps
+	  They were using wrong types - while uint is correct technically, for compatibility reasons caps have them as signed int.
+	  Values are now correctly read + added simple guards just to be sure.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7552>
+
+2024-09-19 12:12:53 +0200  Tim-Philipp Müller <tim@centricular.com>
+
 	* meson.build:
-	  Back to development
+	  Back to development after 1.24.8
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7549>
 
-=== release 1.21.3 ===
+=== release 1.24.8 ===
 
-2022-12-05 01:28:21 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-09-19 12:01:21 +0200  Tim-Philipp Müller <tim@centricular.com>
 
 	* NEWS:
 	* RELEASE:
-	* docs/gst_plugins_cache.json:
 	* gst-plugins-good.doap:
 	* meson.build:
-	  Release 1.21.3
+	  Release 1.24.8
 
-2022-12-04 12:25:41 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-08-13 16:38:37 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ChangeLog:
-	  Remove ChangeLog files from git repository
-	  This information is tracked fully in the git repository, so
-	  no point having the ChangeLog duplicate it, and it interferes
-	  with grepping the repository.
-	  We are going to create the ChangeLogs on the fly when generating
-	  tarballs going forward (with a limited history), since it's still
-	  valuable for tarball consumers to be able to easily see a list of
-	  recent changes.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-project/-/issues/73
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3521>
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2: encoder: Add dynamic framerate support
+	  This is not trully supported in V4L2, but we can emulate this similar to
+	  what other elements do. In this patch we ensure that 0/1 is supported by
+	  encoders (caps query),and uses a default of 30fps whenever we need to
+	  set a framerate into the driver.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7545>
 
-2022-11-07 00:10:39 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-09-11 13:23:35 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* meson.build:
-	* scripts/gen-changelog.py:
-	  meson: Generate ChangeLog files for release tarballs on dist
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3521>
+	* gst/matroska/matroska-mux.c:
+	  matroskamux: Include end padding in the block duration for Opus streams
+	  It has to be included in the block duration but in GStreamer we're not
+	  including it in the buffer duration, so it has to be added again here.
+	  Not including it in the block duration can lead to fatal errors when playing
+	  back with Firefox if there are more padding samples than actual samples, e.g.
+	  > D/MediaDemuxer WebMDemuxer[7f6a0808b900] ::GetNextPacket: Padding frames larger
+	  > than packet size, flagging the packet for error (padding: {13500000,1000000000},
+	  > duration: {6000,1000000}, already processed: false)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7517>
+
+2024-09-05 22:07:24 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-12-04 11:44:17 +0000  Philippe Normand <philn@igalia.com>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	* gst/deinterlace/gstdeinterlace.c:
+	* gst/monoscope/gstmonoscope.c:
+	* gst/shapewipe/gstshapewipe.c:
+	* gst/videomixer/videomixer2.c:
+	  video: Don't overshoot QoS earliest time by a factor of 2
+	  By setting the earliest time to timestamp + 2 * diff there would be a difference
+	  of 1 * diff between the current clock time and the earliest time the element
+	  would let through in the future. If e.g. a frame is arriving 30s late at the
+	  sink, then not just all frames up to that point would be dropped but also 30s of
+	  frames after the current clock time.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7518>
+
+2024-09-11 08:28:39 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* gst/audioparsers/gstflacparse.c:
-	  flacparse: Fix handling of headers advertising 32bps
-	  According to the flac bitstream format specification, the sample size in bits
-	  corresponding to `111` is 32 bits per sample.
-	  https://xiph.org/flac/format.html#frame_header
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3517>
+	* gst/multifile/gstsplitmuxsink.c:
+	  splitmuxsink: Override LATENCY query to pretend to downstream that we're not live
+	  splitmuxsink can't possibly know how much latency it will introduce as it always
+	  keeps one GOP around before outputting something. This breaks the latency
+	  configuration of the pipeline and we're better off just pretending that
+	  everything downstream of the sinkpads is not live.
+	  Especially muxers that are based on aggregator and time out on the latency
+	  deadline can easily misbehave otherwise as the deadline will be exceeded usually.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7515>
 
-2022-12-02 12:15:34 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2024-09-09 13:49:58 +0200  Wim Taymans <wtaymans@redhat.com>
 
-	* sys/v4l2/gstv4l2src.c:
-	  v4l2src: Fix crash in renegotiation
-	  This regression was introduce by fix for making buffer pool thread safe. When
-	  we renegotiate, the pool will be setup after we set the format. But the code
-	  has been simplified to only get the pool once before, which caused a null
-	  pointer deref.
-	  Fixes 94ba019 ("v4l2: Fix SIGSEGV on 'change state' during 'format change'")
-	  Related to !3481
-	  Fixes #1626
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3513>
-
-2022-12-01 10:34:10 +0100  Aleksandr Slobodeniuk <aslobodeniuk@fluendo.com>
+	* ext/jack/gstjackaudiosrc.c:
+	  jackaudiosrc: actually use the queried ports from JACK
+	  When no ports are given, gst_jack_get_ports() is called to get all the
+	  (physical) output ports but then the result is ignored, triggering the
+	  "No physical output ports found..." error.
+	  Instead, move the queried ports to the variable we're going to use
+	  later.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7488>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: fix seek event leaks
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3500>
+2024-09-09 11:23:53 -0400  Randy Li (ayaka) <ayaka@soulik.info>
 
-2022-11-30 14:32:52 +0100  Bo Elmgreen <bo.elmgreen@gmail.com>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2bufferpool: actually queue back the empty buffer flagged LAST
+	  The buffer would fail at gst_v4l2_is_buffer_valid() before,
+	  since it has a reference on it, it is not writable.
+	  Fixes: 105d232fdec1 ("v4l2bufferpool: queue back the buffer flagged LAST but empty")
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7485>
 
-	* ext/qt/gstqtglutility.cc:
-	* ext/qt6/gstqt6glutility.cc:
-	  qt: deactivate context if fill_info fails
-	  Now the OpenGL context is deactivated if call to gst_gl_context_fill_info()
-	  fails in gst_qt_get_gl_wrapcontext(), preventing that the context is left
-	  activated, which could lead to invalid memory reads.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3492>
+2024-09-02 17:45:30 +0900  Hou Qi <qi.hou@nxp.com>
 
-2022-11-28 22:26:50 +0000  Pawel Stawicki <stawel+gstreamer@gmail.com>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: unref buffer pool after usage properly
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7478>
 
-	* sys/v4l2/gstv4l2bufferpool.c:
-	* sys/v4l2/gstv4l2bufferpool.h:
-	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2object.h:
-	* sys/v4l2/gstv4l2sink.c:
-	* sys/v4l2/gstv4l2src.c:
-	* sys/v4l2/gstv4l2transform.c:
-	* sys/v4l2/gstv4l2videodec.c:
-	* sys/v4l2/gstv4l2videoenc.c:
-	  v4l2: Fix SIGSEGV on 'change state' during 'format change'
-	  Ensure all access to v4l2object->pool imply taking a lock and a hard ref on the pool
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3481>
+2024-08-29 12:10:23 -0400  Thibault Saunier <tsaunier@igalia.com>
 
-2022-11-22 11:32:57 -0500  Matt Crane <matt@standard.ai>
+	* sys/osxaudio/gstosxaudiosink.c:
+	* sys/osxaudio/gstosxaudiosrc.c:
+	  osxaudio: Avoid dangling pointer on shutdown
+	  When tearing down the elements we were still referring to the ringbuffer unique_id
+	  as our property while it was already freed, leading to potential segfaults when
+	  accessing the property.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7451>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpbin.h:
-	* gst/rtpmanager/gstrtpsession.c:
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtpsession.h:
-	  rtpsession: Support disabling late adjustment of ntp-64 header ext
-	  Currently in rtp_session_send_rtp(), the existing ntp-64 RTP header
-	  extension timestamp is updated with the actual NTP time before sending
-	  the packet. However, there are some circumstances where we would like
-	  to preserve the original timestamp obtained from reference timestamp
-	  buffer metadata.
-	  This commit provides the ability to configure whether or not to update
-	  the ntp-64 header extension timestamp with the actual NTP time via the
-	  update-ntp64-header-ext boolean property. The property is also exposed
-	  via rtpbin. Default property value of TRUE will preserve existing
-	  behavior (update ntp-64 header ext with actual NTP time).
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1580
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3451>
-
-2022-10-06 17:08:54 +1100  Matthew Waters <matthew@centricular.com>
+2024-08-21 12:33:28 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/meson.build:
-	* ext/qt6/gstplugin.cc:
-	* ext/qt6/gstqml6glsink.cc:
-	* ext/qt6/gstqml6glsink.h:
-	* ext/qt6/gstqsg6glnode.cc:
-	* ext/qt6/gstqsg6glnode.h:
-	* ext/qt6/gstqt6element.cc:
-	* ext/qt6/gstqt6elements.h:
-	* ext/qt6/gstqt6gl.h:
-	* ext/qt6/gstqt6glutility.cc:
-	* ext/qt6/gstqt6glutility.h:
-	* ext/qt6/meson.build:
-	* ext/qt6/qt6glitem.cc:
-	* ext/qt6/qt6glitem.h:
-	* meson_options.txt:
-	* tests/examples/meson.build:
-	* tests/examples/qt6/meson.build:
-	* tests/examples/qt6/qmlsink/main.cpp:
-	* tests/examples/qt6/qmlsink/main.qml:
-	* tests/examples/qt6/qmlsink/meson.build:
-	* tests/examples/qt6/qmlsink/play.pro:
-	* tests/examples/qt6/qmlsink/qmlsink.qrc:
-	  add new plugin for Qt 6 rendering inside a QML scene
-	  - Based heavily on the existing Qt5 integration however:
-	  - The sharing of OpenGL resources is slightly different
-	  - The integration with the scengraph is a bit different
-	  - Wayland, XCB and KMS have been smoke tested.  Android, MacOS/iOS,
-	  Windows may or may not work.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3281>
-
-2022-11-21 15:35:58 +0800  Elliot Chen <elliot.chen@nxp.com>
+	* meson.build:
+	  Back to development after 1.24.7
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2: bypass check some transfer types in colorimetry
-	  v4l2 will report fail for some streams whose colorimetry value such as 2:4:8:3.
-	  Can bypass check these transfer types in colorimetry to avoid error.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3440>
+=== release 1.24.7 ===
 
-2022-10-27 17:30:28 +0200  Johan Sternerup <johast@axis.com>
+2024-08-21 12:25:15 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/rtpmanager/rtpsession.c:
-	* tests/check/elements/rtpsession.c:
-	  Use the correct SSRC(s) when routing a RTCB FB FIR
-	  Previously we tried to route an incoming RTCP FB FIR to the correct ssrc
-	  using the "media source" component of the RTCP FB message. However,
-	  according to RFC5104 (section 4.3.1.2) the "media source" SHALL be set
-	  to 0. Instead the ssrc(s) in use are propagated via the FCI data. Now
-	  a specific GstForceKeyUnit event is sent for every ssrc.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3292>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.7
 
-2022-11-23 16:37:40 +1100  Jan Schmidt <jan@centricular.com>
+2024-07-22 19:28:13 +0800  Qian Hu (胡骞) <qian.hu@mediatek.com>
 
-	* gst/rtpmanager/rtpsource.c:
-	  rtpsource: Track the seqnum for senders
-	  RTP source statistics are tracked for local senders by
-	  treating them as a receiver of their own outbound packets.
-	  Accordingly, track the highest packet seqnum so that the
-	  packets-lost calculation generates a sensible number instead
-	  of always reporting -$number_of_packets_sent
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3454>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: handle unsupported hlg colorimetry gracefully
+	  This patch addresses the issue where GStreamer would throw an error when
+	  attempting to use bt2100-hlg colorimetry with V4L2, which is not
+	  supported by the current V4L2 kernel. When bt2100-hlg colorimetry is set
+	  from caps, the check for transfer (GST_VIDEO_TRANSFER_ARIB_STD_B67) is
+	  bypassed.
+	  The main improvement is to avoid checking the transfer value in
+	  gst_v4l2_video_colorimetry_matches when it is
+	  GST_VIDEO_TRANSFER_ARIB_STD_B67. This is because the transfer value in
+	  the cinfo parameter comes from gst_v4l2_object_get_colorspace, which
+	  converts the transfer to another value, causing a mismatch.
+	  Since the kernel does not support GST_VIDEO_TRANSFER_ARIB_STD_B67,
+	  gst_v4l2_object_get_colorspace cannot map it correctly from V4L2 to
+	  GStreamer. Therefore, we ignore this check to prevent errors.
+	  changes:
+	  - Added a condition in gst_v4l2_video_colorimetry_matches to bypass the
+	  transfer check when the transfer is GST_VIDEO_TRANSFER_ARIB_STD_B67.
+	  - Ensured that the pipeline does not throw errors due to unsupported
+	  bt2100-hlg colorimetry in V4L2.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7369>
+
+2024-07-03 16:03:47 +0200  Mathieu Duponchelle <mathieu@centricular.com>
 
-2022-11-22 21:57:16 +1100  Jan Schmidt <jan@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtsp/gstrtspsrc.c:
+	* gst/rtsp/gstrtspsrc.h:
+	  rtspsrc: expose property for forcing usage of non-compliant URLs
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7346>
 
-	* ext/adaptivedemux2/meson.build:
-	  adaptivedemux2: Add GStreamer to the deps list
-	  Explicitly dep on GStreamer so as not to accidentally
-	  link to the system version in a git build
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3453>
+2024-08-07 15:56:08 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-11-15 11:56:35 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* ext/qt6/qt6glwindow.cc:
+	  qt6glwindow: Fallback to GL_RGB on CopyTexImage2D error
+	  With GLES 2.0 we are forced to use CopyTextImage2D which requires
+	  passing an internal format. With QT6 eglfs, we need to pass GL_RGB
+	  instead, probably because of how the texture has been created. As its
+	  hard to guess, simply fallback to GL_RGB on failure. This fixes usage
+	  or qml6glsrc with eglfs backend, without loosing support for
+	  semi-transparent window on other platforms.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7345>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Don't replace 404 errors with "no auth protocol found"
-	  When getting a "404 Not Found" response from the DESCRIBE request, the
-	  source produced a "No supported authentication protocol was found" error
-	  instead of passing on the 404, which was confusing.
-	  Only produce this error message when we're handling a response of "401
-	  Unauthorized" without a compatible WWW-Authenticate header.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3414>
+2024-08-01 11:21:09 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-11-21 17:24:38 +0100  Edward Hervey <edward@centricular.com>
+	* ext/qt6/qt6glwindow.cc:
+	  qmlgl6src: Fix crash when use-default-fbo is false
+	  When that property is set to its default qmlgl6src element simply crash
+	  as it will call gst_video_frame_unmap() twice.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7344>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	  adaptivedemux2: Don't leak caps in debug statements
-	  Instead just directly use the stream object (which will report the caps)
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3443>
+2024-08-01 12:15:17 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-11-21 17:23:44 +0100  Edward Hervey <edward@centricular.com>
+	* ext/qt6/qt6glwindow.cc:
+	  qt6glwindow: Only use GL_READ_FRAMEBUFFER when we do blits
+	  This fbo target is not always supported, and should only be used
+	  along with the frame buffer blit extension.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7295>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	  adaptivedemux2: Don't leak tags
-	  If we got them from GstStream, we should unref them when done
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3443>
+2024-07-31 09:06:21 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-11-21 17:23:10 +0100  Edward Hervey <edward@centricular.com>
+	* ext/qt6/qt6glwindow.cc:
+	  qt6: glwindow: Don't leak previously rendered buffer
+	  If the consumer reads the buffers too slowily, simply unref the
+	  previously rendered buffer instead of leaking it.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7289>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux: Use gst_clear_tag_list_where applicable
-	  Clearer and ensures fields are reset
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3443>
+2023-02-13 16:49:40 +0800  Hou Qi <qi.hou@nxp.com>
 
-2022-11-21 15:11:21 +0100  Edward Hervey <edward@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2: Fix colorimetry mismatch for encoded format with RGB color-matrix
+	  video-info supports encoded format to have RGB color-matrix, while
+	  v4l2object just leave the v4l2 matrix to default when mapping
+	  GST_VIDEO_COLOR_MATRIX_RGB. It causes gst matrix changed to be
+	  GST_VIDEO_COLOR_MATRIX_BT601 when mapping v4l2 colorimetry.
+	  So add support for encoded format with RGB color-matrix in v4l2object.
+	  Note that for M2M encoders, we should in theory assume that that we can
+	  transfer this value from OUTPUT to CAPTURE queues, though its only true
+	  if the drivers does not do CSC. For now, we don't support any RGB
+	  codecs, but leaving a note for the future.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7284>
+
+2024-07-29 14:39:06 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Don't leak sticky events
-	  We have incremented the reference 2 lines above, and
-	  gst_pad_store_sticky_event() does not take a reference, therefore release it
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3443>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: SRGB colorspace is documented limited-range
+	  Split JPEG and SRGB so that we can follow the specified difference. The
+	  SRGB definition in V4L2 does not follow the standard, and is document
+	  so. This is also why JPEG colorspace exists.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7284>
 
-2022-11-15 21:42:34 +1100  Jan Schmidt <jan@centricular.com>
+2024-07-29 11:40:01 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
-	  adaptivedemux2: Fix sticky event storage.
-	  Use the new gst_event_type_to_sticky_ordering() method to retrieve
-	  the order that sticky events should be sent / stored in, instead
-	  of assuming it's the event type value.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3387>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Fix size of plane_size array calculation
+	  Due to missing parenthesys, only the first element of the array was
+	  being cleared. As it is a staticly sized array in the object, this
+	  code could also be simplified.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7284>
 
-2022-11-15 13:50:13 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2024-07-29 11:37:29 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* sys/v4l2/gstv4l2bufferpool.c:
 	* sys/v4l2/gstv4l2object.c:
-	  video: Add arbitrary tile dimensions support
-	  In current tile representation, only tiles with power of two
-	  width and height in bytes are supported. This limitation
-	  prevents adding more complex tiles formats.
-	  In this patch, we deprecate tile_ws and tile_hs from GstVideoFormatInfo and
-	  replace if with an array of GstVideoTileInfo. Each plane tiles are then
-	  described with their pixels width/height, line stride and total size.
-	  The helper gst_video_format_info_get_tile_sizes() that depends on the
-	  deprecated API is also being removed. This can simply be removed as it wasn't
-	  in any stable release yet.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3424>
-
-2022-09-09 13:13:32 +0300  Vivia Nikolaidou <vivia@ahiru.eu>
+	  v4l2object: Fix translation of quantization
+	  The V4L2_MAP_QUANTIZATION macro has been fixed to something a lot saner,
+	  fix our replica accordingly. The new macro now simply set the quantization
+	  to full range is the pixel formats is RGB based, or if the JPEG
+	  colorspace is used.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7284>
 
-	* gst/multifile/gstsplitmuxsink.c:
-	  splitmuxsink: Avoid assertion when WAITING_GOP_COLLECT on reference context
-	  I have seen a backtrace out in the wild where this happened. Maybe after
-	  receiving EOS and stream-start on the reference context.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3005>
+2024-07-29 16:48:02 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-14 13:53:02 +0100  Edward Hervey <edward@centricular.com>
+	* meson.build:
+	  Back to development after 1.24.6
 
-	* gst/multifile/gstimagesequencesrc.c:
-	  imagesequencesrc: Don't leak caps
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3428>
+=== release 1.24.6 ===
 
-2022-05-03 18:36:54 +0200  Enrique Ocaña González <eocanha@igalia.com>
+2024-07-29 16:41:37 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.h:
-	  hlsdemux2: Expose EXT-X-PROGRAM-DATE-TIME as tags.
-	  This allows an application to use timestamps associated
-	  with fragments.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1417>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.6
 
-2022-11-17 20:59:35 +1100  Matthew Waters <matthew@centricular.com>
+2024-06-01 15:15:18 +0300  Piotr Brzeziński <piotr@centricular.com>
 
-	* gst/isomp4/atoms.c:
-	  qtmux: use trun with multiple entries in more cases
-	  The only case where we definitely need to write a new trun is when the
-	  data_offset value does not match the end of the list of entries.
-	  Needing multiple trun atoms is required when interleaving multiple
-	  streams together.
-	  All other cases can be covered by adding more entries to the existing
-	  trun atom.
-	  Fixes playback of fragemented mp4 in ffplay and chrome.
-	  Using e.g. mp4mux fragment-duration=1000 fragment-mode=dash-or-mss
-	  and
-	  mp4mux fragment-duration=1000 fragment-mode=first-moov-then-finalise
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3426>
-
-2022-10-26 07:04:32 +0200  Edward Hervey <edward@centricular.com>
+	* sys/osxaudio/gstosxaudiodeviceprovider.c:
+	  macos: Listen for audio devices being added/removed
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7258>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Notify that we are streams-aware
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2784>
+2024-06-12 23:25:52 +1000  Matthew Waters <matthew@centricular.com>
 
-2022-11-16 13:33:39 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+	* ext/qt/gstqsgmaterial.cc:
+	  qml/glsink: also support GLES2 needing shader 'precision' directives
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3616
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7125>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Fix regression when using hostname in the location property
-	  When the address can't be parsed as an IP address, it should just be
-	  treated as a hostname and used as-is.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1576
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3420>
+2024-07-23 15:32:22 +0200  Loïc Yhuel <loic.yhuel@softathome.com>
 
-2022-11-13 19:19:41 -0500  F. Duncanh <fduncanh@gmail.com>
+	* meson.build:
+	  meson: fix SIZEOF_OFF_T when cross-compiling with Meson >= 1.3.0
+	  https://mesonbuild.com/Release-notes-for-1-3-0.html#clarify-of-implicitlyincluded-headers-in-clike-compiler-checks
+	  With only stddef.h, off_t is not defined, so when cross-compiling SIZEOF_OFF_T is -1.
+	  We now use sys/types.h which should define off_t.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7218>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: Fixes three H.264/HEVC ITU conformance tests
-	  Postpone the cleanup of any consecutive sequence of lost frames
-	  which starts at frame 0, until frame 100 is dequeued from driver.
-	  This allows fluster tests JVT/CVWP2_TOSHIBA_E, JVC/CVWP3_TOSHIBA_E
-	  and HEVC/POC_A_Bossen_3 that sends out-of-order frames to successfully
-	  complete  (e.g., test of Amphion vpu driver).
-	  Fixes #1569
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3398>
+2024-07-01 10:00:42 +0800  Shengqi Yu <shengqi.yu@mediatek.com>
 
-2022-11-15 17:26:18 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: use v4l2 reported width for padded_width when complex video formats
+	  Stride means bytes per line, and padded_width means pixels. Here,
+	  padded_width shoule be pix width reported by v4l2 instead of stride.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7122>
 
-	* tests/check/elements/rtpjitterbuffer.c:
-	  rtpjitterbuffer: Add test for rescheduling timers to negative times
-	  This tests the changes introduced by 4d3b8d1129d8b863e4156cd0334e93257b9d0cc4
-	  for issue #571.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3416>
+2024-06-02 11:40:04 +0300  Jan Schmidt <jan@centricular.com>
 
-2022-09-07 11:07:40 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  adaptivedemux: Fix handling closed caption streams
+	  Fix a typo "CLOSED_CAPTION" -> "CLOSED-CAPTION" and
+	  a broken if statement that always bailed out for
+	  closed captions
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7105>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Reschedule timers when updating their offset
-	  As EXPECTED timers are skipped the order of the timers relative to each
-	  other can change if there are EXPECTED timers and rescheduling needs to
-	  happen.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1422
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3416>
+2024-06-20 13:02:19 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-15 21:22:17 +0530  Sanchayan Maity <sanchayan@asymptotic.io>
+	* meson.build:
+	  Back to development after 1.24.5
 
-	* gst/wavparse/gstwavparse.c:
-	  wavparse: Do not run all typefinders for all output
-	  In order to figure out if the "raw" audio contained within the wav
-	  container is actually DTS, wavparse calls the typefinder helper
-	  except that means it runs all typefinders.
-	  Since it only cares about checking for DTS, we should only run the
-	  audio/x-dts typefinder (if present). Commit 858e516 did not really
-	  fix things.
-	  Use the new type helper with the caps to fix this.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3417>
-
-2022-11-14 21:19:12 +0200  Sebastian Dröge <sebastian@centricular.com>
+=== release 1.24.5 ===
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Consistently set seqnums on events
-	  Set udpsrc seqnums on all events sent to the udpsrc's, and before
-	  forwarding events out of rtspsrc set the latest seek seqnum on them if
-	  any.
-	  Also produce a consistent seqnum in rtspsrc from the very beginning.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3409>
+2024-06-20 12:54:15 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-14 19:14:27 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.5
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Make segment event writable before overriding the seqnum and use the proper API to do so
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3409>
+2024-06-19 02:10:29 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-14 19:10:05 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* gst/dtmf/gstrtpdtmfsrc.c:
+	  rtpdtmfsrc: minor logging clean-up
+	  Only serialise event structure for debug logging purposes
+	  if logging is actually enabled.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7062>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Intercept and handle events when using no manager too
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3409>
+2024-06-19 01:55:57 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-14 19:08:54 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* gst/dtmf/gstrtpdtmfsrc.c:
+	* tests/check/elements/dtmf.c:
+	  rtpdtmfsrc: fix leak when shutting down mid-event
+	  .. and update rtpdtmfdepay unit test to trigger
+	  the potential leak more reliably (without the fix).
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3633
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7062>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Don't blindly copy over sticky events from manager pad to external source pad
-	  This would get around the code that modifies some events when they go
-	  through the ghost pad's proxypad. Instead go via the event function.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3409>
+2024-06-14 16:50:12 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-14 19:03:21 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* tests/check/elements/dtmf.c:
+	  rtpdtmfdepay: add unit test for caps fixation issue with downstream audioconvert
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7048>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Don't make udpsrc segment events writable just to retrieve their seqnum
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3409>
+2024-06-14 16:20:31 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-14 18:40:56 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/dtmf/gstrtpdtmfdepay.c:
+	* tests/check/elements/dtmf.c:
+	  rtpdtmfdepay: fix caps negotiation with audioconvert
+	  Specify "layout" field in src template to make sure it's
+	  set and gets fixated properly if the downstream element
+	  supports both interleaved and non-interleaved caps.
+	  Fixes
+	  gst_pad_set_caps: assertion 'caps != NULL && gst_caps_is_fixed (caps)' failed
+	  critical with e.g.
+	  gst-launch-1.0 rtpdtmfsrc ! rtpdtmfdepay ! audioconvert ! fakesink
+	  Not that the layout really matters in our case since we always
+	  output mono anyway, but non-interleaved requires adding AudioMeta,
+	  so this is the easiest fix.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7048>
+
+2024-06-13 18:23:46 +0200  Mathieu Duponchelle <mathieu@centricular.com>
 
 	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Reset EOS flag also on FLUSH_STOP and not only on ssrc-active
-	  Also don't bother not sending EOS if EOS was sent already:
-	  gst_pad_push_event() takes care of that for us already.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3409>
-
-2022-11-15 13:38:18 +0000  Tim-Philipp Müller <tim@centricular.com>
+	  rtspsrc: fix invalid seqnum assertions
+	  Upon fatal errors the loop function will first post an error message
+	  then push out an EOS event.
+	  An application may react immediately to the error message by setting the
+	  state of the pipeline to NULL, meaning by the time we push out the EOS
+	  event PAUSED_TO_READY may have reset the seek seqnum to -1.
+	  While this is harmless, the assertion when setting an invalid seqnum
+	  isn't tidy, fix this by simply not resetting to INVALID as it serves no
+	  practical purpose and the next READY_TO_PAUSED will select a new seqnum
+	  anyway.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7034>
+
+2024-06-11 19:03:21 +0000  Jakub Vaněk <linuxtardis@gmail.com>
 
-	* ext/gdk_pixbuf/gstgdkpixbufoverlay.c:
-	  gdkpixbufoverlay: fix docs - changing images at runtime is supported
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3415>
-
-2022-11-13 08:38:25 +0100  Edward Hervey <edward@centricular.com>
-
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Unlock timer waits on flushing
-	  If there is a pending EOS wait for example, we would never unblock on flushing
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3401>
+	* sys/v4l2/gstv4l2src.c:
+	  v4l2src: Interpret V4L2 report of sync loss as video signal loss
+	  Certain V4L2 drivers can report that a video receiver is seeing
+	  some signal, but that it is unable to synchronize to it. IOW: the driver
+	  can sometimes report V4L2_IN_ST_NO_SYNC and not report V4L2_IN_ST_NO_SIGNAL.
+	  In particular, I've seen the tc358743 (HDMI-to-CSI2 converter) driver
+	  sometimes report this when deployed to a fleet of embedded Raspberry Pis.
+	  The relevant kernel code is in [1]. The video output is not practically
+	  usable when V4L2_IN_ST_NO_SYNC is reported (only visually corrupted frames,
+	  sometimes with random "snow", are received). I assume that this happens when
+	  either the HDMI cable is poorly plugged in or damaged or when a CSI2 FFC
+	  cable is used and is damaged.
+	  The change in this commit is useful for detecting this working-but-not-really
+	  condition in application code. Applications already listening for the "Signal lost"
+	  message will gain the ability to handle this condition.
+	  There seem to be more V4L2 error flags like this, see [2]. However, I do not
+	  have practical experience with them and adding only V4L2_IN_ST_NO_SYNC seems
+	  like a safer option.
+	  [1]: https://github.com/raspberrypi/linux/blob/be8498ee21aa/drivers/media/i2c/tc358743.c#L1534
+	  [2]: https://www.kernel.org/doc/html/v6.6/userspace-api/media/v4l/vidioc-enuminput.html
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7027>
+
+2024-06-04 15:30:37 +0000  Corentin Damman <c.damman@intopix.com>
+
+	* ext/qt6/gstqsg6material.cc:
+	  gstqsg6material: fix RGB format support
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6997>
+
+2024-05-27 14:14:24 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2021-10-14 11:56:58 +0200  Rafał Dzięgiel <rafostar.github@gmail.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Only update from the Content-Base header in the initial OPTION / DESCRIBE response
+	  Some servers send a new content base in the SETUP response, which is
+	  just the non-aggregate control URL of the individual streams.
+	  See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3563
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6982>
 
-	* tests/check/elements/dash_mpd.c:
-	  tests: Add DASH MPD baseURL with query test
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1147>
+2024-05-26 14:03:11 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2021-10-14 10:12:51 +0200  Rafał Dzięgiel <rafostar.github@gmail.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Handle the case of `*` as session-wide control URL from the SDP
+	  Just like the comment above says this is supposed to indicate that the
+	  same URL should be used as for the connection so far. If encountering
+	  this case simply do nothing.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6982>
 
-	* ext/adaptivedemux2/dash/gstmpdparser.c:
-	  mpdparser: Fix missing baseURL query
-	  When no initializationURL or mediaURL, return baseURL that also
-	  contains original URI query if available. This fixes a problem
-	  where URI query was being omitted in the HTTP requests.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1147>
+2024-05-26 14:02:12 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2021-10-14 10:09:31 +0200  Rafał Dzięgiel <rafostar.github@gmail.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Also handle `rtsps://` and similar URLs as absolute in other places
+	  Previously a direct comparison with `rtsp://` was performed, which
+	  didn't catch cases like `rtsps://`.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3563
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6982>
 
-	* ext/adaptivedemux2/dash/gstmpdclient.c:
-	* ext/adaptivedemux2/dash/gstmpdparser.c:
-	* ext/adaptivedemux2/dash/gstmpdparser.h:
-	  mpdparser: Be consistent about returning duplicated URL
-	  Instead of returning a "const gchar" or a "gchar" that should not be freed, always
-	  return a duplicated string as those functions were used together with g_strdup anyway.
-	  This is needed to prepare support for returning modified strings in next commit.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1147>
+2024-05-26 13:00:02 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2021-10-14 10:18:40 +0200  Rafał Dzięgiel <rafostar.github@gmail.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Don't try the SETUP workaround for broken servers with absolute control URIs
+	  Previously only control URIs that started with "rtsp://" were ignored
+	  but it makes more sense to ignore all absolute URIs.
+	  gst_uri_is_valid() conveniently checks for exactly that.
+	  See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3563
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6982>
 
-	* ext/adaptivedemux2/dash/gstmpdparser.c:
-	  mpdparser: Return correct mediaURL value
-	  This fixes a problem where get_mediaURL was returning NULL when segmentURL
-	  was unavailable instead of baseURL as a fallback.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1147>
+2024-05-28 13:04:20 +0200  Edward Hervey <bilboed@bilboed.com>
 
-2022-11-14 12:51:19 +0100  Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Minor refactoring of starting segment check
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
 
-	* gst/matroska/matroska-demux.c:
-	  matroskademux: Handle element's duration query.
-	  This is small regression from commit f7abd81a.
-	  When calling `gst_element_query()` no pad is associated with that query, but the
-	  current code always forwards the query to the associated pad, which is NULL in
-	  previous case. This patch checks for the pad before forwarding the query.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3404>
+2024-04-15 08:47:12 +0200  Edward Hervey <edward@centricular.com>
 
-2022-11-13 12:18:54 +0100  Rafał Dzięgiel <rafostar.github@gmail.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Be more tolerant when matching segments with PDT
+	  Some servers might not provide 100% matching PDT when doing updates, or accross
+	  variants. This would cause the code matching segments using PDT to fail if the
+	  segment PDT was 1 microsecond (or whatever small value) before the candidate
+	  segment. And would pick the (wrong) following segment as the matching one.
+	  In order to be more tolerant when matching, we instead check whether the
+	  candidate segment is within the first segment of the segment we are trying to
+	  match.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
+
+2024-04-12 15:53:08 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Fix failure to find a replacement segment on resync
+	  If we end up with a segment with an internal time that varies from the supposed
+	  one, this could be for two reasons:
+	  * We guess-timated the wrong segment to go to when advancing or switching
+	  variants. In that case we try to find the actual segment to go to (just before
+	  this change).
+	  * There was a complete playlist change (for whatever reason) and we can't find a
+	  replacement. In that case we want to carry on playback from this position but
+	  need to remember that we moved (by setting the stream to DISCONT, and
+	  resetting the new mapping).
+	  Fixes playback on several broken stream
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
+
+2024-04-12 15:52:23 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	  hlsdemux2: Refactor update of GstHLSTimeMap values
+	  This was also missing transferring the PDT if present
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Fix parent object leak
-	  gst_object_get_parent() method is transfer-full, thus unref is needed
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3396>
+2024-04-12 15:48:34 +0200  Edward Hervey <edward@centricular.com>
 
-2022-10-10 19:58:12 +0100  Colin Kinloch <colin.kinloch@collabora.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Fix parsing of EXT-X-DISCONTINUITY-SEQUENCE:0
+	  Since the default value of `m3u8->discont_sequence` (before parsing of the
+	  playlist data) was 0 .. we would never properly detect the presence of that
+	  field if it was present with a value of 0.
+	  This would later on cause havoc in playlist synchronization where we would
+	  assume it didn't have a discontinuity sequence specified (whereas it did, and it
+	  was 0).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
+
+2024-04-12 15:45:16 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Increase tolerance for discontinuity detection
+	  A lot of streams will do a poor job of estimating proper duration of fragments
+	  in the playlist, but over several fragments have it correct.
+	  Instead of constantly trying to realign the estimated stream time, allow for a
+	  more realistic tolerance of 3-4 video frames
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
+
+2024-04-11 16:37:36 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Ensure a discont will be set when resetting for lost sync
+	  This is to ensures we inform the demuxer/parsers that what follows is not contiguous
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
+
+2024-04-11 15:30:27 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Fix handling of variant switching and playlist updates
+	  When updating playlists, we want to know whether the updated playlist is
+	  continuous with the previous one. That is : if we advance, will the next
+	  fragment need to have the DISCONT buffer set on it or not.
+	  If that happens (because we switched variants, or the playlist all of a sudden
+	  changed) we remember that there is a pending discont for the next fragment. That
+	  will be used and resetted the next time we get the fragment information.
+	  Previously this was only partially done. And it was racy because it was set
+	  directly on `GstAdaptiveDemux2Stream->discont` when a playlist was updated,
+	  instead of when the next fragment was prepared.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
+
+2024-04-11 15:19:50 +0200  Edward Hervey <edward@centricular.com>
 
-	* gst/videobox/gstvideobox.c:
-	* gst/videocrop/gstvideocrop.c:
-	  videocrop, videobox: Simplify navigation event handling and support touch events
-	  Signed-off-by: Colin Kinloch <colin.kinloch@collabora.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3053>
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	  adaptivedemux2: Only set DISCONT on beginning of fragments
+	  This avoids accidentally setting it in the middle of a fragment, which could
+	  cause havoc in demuxer/parsers
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
 
-2022-09-19 18:06:44 +0100  Colin Kinloch <colin.kinloch@collabora.com>
+2024-04-08 16:13:13 +0200  Edward Hervey <edward@centricular.com>
 
-	* gst/videofilter/gstvideoflip.c:
-	  videoflip: Use gst_video_orientation_from_tag to parse orientation
-	  Signed-off-by: Colin Kinloch <colin.kinloch@collabora.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3053>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Fix getting starting segment on live playlists
+	  When dealing with live streams, the function was assuming that all segments of
+	  the playlist had valid stream_time. But that isn't TRUE, for example in the case
+	  of failing to synchronize playlists.
+	  Fixes losing sync due to not being able to match playlist on updates
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6961>
 
-2022-11-09 12:04:08 +0000  Christian Wick <c.wick@mail.de>
+2024-05-13 11:08:15 +0300  Sergey Krivohatskiy <s.krivohatskiy@gmail.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtsp/gstrtspsrc.c:
-	* gst/rtsp/gstrtspsrc.h:
-	* tests/examples/rtsp/test-onvif.c:
-	  rtspsrc: Introduce new action signal `push-backchannel-sample` with correct ownership semantics
-	  Signals are not supposed to take ownership of their arguments but only
-	  borrow them for the scope of the signal emission.
-	  The old action signal `push-backchannel-buffer` is now marked deprecated.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3363>
+	* gst/audioparsers/gstflacparse.c:
+	  flacparse: fix buffer overflow in gst_flac_parse_frame_is_valid
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6960>
 
-2022-11-08 02:08:08 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-05-29 13:51:27 +0300  Tim-Philipp Müller <tim@centricular.com>
 
-	* docs/gst_plugins_cache.json:
 	* meson.build:
-	  Back to development
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3358>
+	  Back to development after 1.24.4
 
-=== release 1.21.2 ===
+=== release 1.24.4 ===
 
-2022-11-07 23:53:59 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-05-29 13:44:50 +0300  Tim-Philipp Müller <tim@centricular.com>
 
-	* ChangeLog:
 	* NEWS:
 	* RELEASE:
-	* docs/gst_plugins_cache.json:
 	* gst-plugins-good.doap:
 	* meson.build:
-	  Release 1.21.2
+	  Release 1.24.4
 
-2022-11-07 23:53:57 +0000  Tim-Philipp Müller <tim@centricular.com>
+2024-05-21 17:49:33 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ChangeLog:
-	  Update ChangeLogs for 1.21.2
+	* ext/gtk/gstgtkbasesink.c:
+	  gtk: Fail initialization of the sink if GTK4 is already initialized in the same process
+	  Initializing GTK3 and GTK4 in the same process does not work and is not
+	  supported.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6905>
 
-2020-09-02 10:49:40 +0100  Justin Chadwell <me@jedevc.com>
+2024-05-21 17:49:42 +0200  Piotr Brzeziński <piotr@centricular.com>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: use unsigned int types to store result of QT_UINT32
-	  In a few cases throughout qtdemux, the results of QT_UINT32 were being
-	  stored in a signed integer, which could cause subtle bugs in the case of
-	  an integer overflow, even allowing the the result to equal a negative
-	  number!
-	  This patch prevents this by simply storing the results of this function
-	  call properly in an unsigned integer type. Additionally, we fix up the
-	  length checking with stsd parsing to prevent cases of child atoms
-	  exceeding their parent atom sizes.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3344>
-
-2022-11-04 17:48:01 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* sys/osxaudio/gstosxcoreaudio.c:
+	* sys/osxaudio/gstosxcoreaudio.h:
+	* sys/osxaudio/gstosxcoreaudiocommon.h:
+	  osxaudio: Avoid using private APIs on iOS
+	  Turns out AudioConvertHostTimeToNanos and AudioGetCurrentHostTime are macOS-only APIs, which prevents apps using
+	  GStreamer on iOS from being accepted into App Store.
+	  This commit replaces those functions with a manual version of what they do - mach_absolute_time() for the current time,
+	  and data from mach_timebase_info() at the beginning to convert host timestamps to nanoseconds.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6899>
 
-	* ext/qt/gstqtglutility.cc:
-	  qt: initialize GError properly in gst_qt_get_gl_wrapcontext()
-	  Spotted by Claus Stovgaard.
-	  Fixes #1545
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3327>
+2024-05-09 10:01:22 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-11-04 11:10:52 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* gst/level/gstlevel.c:
+	  level: Don't post a message on EOS without a valid audio info
+	  If EOS is received before caps, e.g. because of an error, then rate and
+	  number of channels would be 0 and some divisions by zero would happen.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6828>
 
-	* gst/isomp4/gstqtmux.c:
-	  qtmux: Add durations to raw audio buffers from the raw audio adapter in prefill mode
-	  This ensures that a duration can also be calculated and stored for the
-	  last buffer at EOS.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3321>
+2024-05-05 18:29:39 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-11-04 10:49:31 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* tests/check/elements/qtdemux.c:
+	  qtdemux: Use `G_GUINT64_CONSTANT` when creating test caps
+	  Otherwise this fails on 32 bit platforms.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3521
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6806>
 
-	* gst/isomp4/gstqtmux.c:
-	  qtmux: Release object lock before posting an error message
-	  GST_ELEMENT_ERROR() also takes the object lock and this would then
-	  deadlock.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3321>
+2024-04-30 00:36:59 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-11-03 14:08:57 +0100  Edward Hervey <edward@centricular.com>
+	* meson.build:
+	  Back to development after 1.24.3
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6772>
 
-	* gst/multifile/gstimagesequencesrc.c:
-	  imagesequencesrc; Fix leaks
-	  * The path was leaked
-	  * The custom buffer was never freed
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3319>
+=== release 1.24.3 ===
 
-2022-11-03 14:08:02 +0100  Edward Hervey <edward@centricular.com>
+2024-04-30 00:15:23 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Fix cenc-related leaks
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3319>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.3
 
-2022-11-03 14:06:58 +0100  Edward Hervey <edward@centricular.com>
+2024-04-22 10:40:13 -0400  William Wedler <william.wedler@videoray.com>
 
-	* gst/deinterlace/gstdeinterlace.c:
-	  deinterlace: Don't leak metas
-	  There is no correlation between the frame being NULL and the metas not being
-	  present.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3319>
+	* ext/qt/qtitem.cc:
+	  fix: qmlglsink: video content resizes to new item size
+	  Mark geometry dirty when the item rectangle changes in the
+	  QtGLVideoItem::updatePaintNode method. This allows changes in the bounding
+	  rectangle to be applied to the scene graph geometry node.
+	  Fixes #3493
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6759>
 
-2022-10-31 16:08:23 +0100  Edward Hervey <edward@centricular.com>
+2024-04-22 10:35:18 -0400  William Wedler <william.wedler@videoray.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-period.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Fix collection leaks
-	  * The collection on the period was never unreffed
-	  * The collection in the message handler was never unreffed
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3319>
+	* ext/qt6/qt6glitem.cc:
+	  fix: qml6glsink: video content resizes to new item size
+	  Mark geometry dirty when the item rectangle changes in the
+	  QtGLVideoItem::updatePaintNode method. This allows changes in the bounding
+	  rectangle to be applied to the scene graph geometry node.
+	  Fixes #3493
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6759>
 
-2022-11-05 03:23:43 +1100  Jan Schmidt <jan@centricular.com>
+2024-02-19 21:49:21 +0800  Tim Blechmann <tim@klingt.org>
 
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	  mssdemux2: Update for adaptivedemux2 refactoring
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+	* sys/v4l2/v4l2_calls.c:
+	  v4l2: silence valgrind warning
+	  Valgrind complains about uninitialized memory used in an ioctl
+	  Syscall param ioctl(VKI_V4L2_G_TUNER).reserved points to uninitialised byte(s)
+	  at 0x719294F: ioctl (ioctl.c:36)
+	  by 0x3126A817: gst_v4l2_fill_lists (v4l2_calls.c:185)
+	  by 0x3126A817: gst_v4l2_open (v4l2_calls.c:589)
+	  by 0x3123F1C2: gst_v4l2_device_provider_probe_device (gstv4l2deviceprovider.c:122)
+	  by 0x3123F648: gst_v4l2_device_provider_device_from_udev (gstv4l2deviceprovider.c:301)
+	  by 0x3123F998: provider_thread (gstv4l2deviceprovider.c:395)
+	  by 0x796FA50: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4)
+	  by 0x710CAC2: start_thread (pthread_create.c:442)
+	  by 0x719DA03: clone (clone.S:100)
+	  Address 0x44008a34 is on thread 11's stack
+	  in frame #1, created by gst_v4l2_open (v4l2_calls.c:524)
+	  Uninitialised value was created by a stack allocation
+	  at 0x3126A024: gst_v4l2_open (v4l2_calls.c:524)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6761>
+
+2024-04-26 09:45:22 +0800  Tim Blechmann <tim@klingt.org>
 
-2022-11-03 01:48:08 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/soup/gstsouphttpsrc.c:
+	  soup: fix thread name
+	  thread names should be below 16char, otherwise they won't be shown on
+	  linux.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6739>
+
+2024-04-19 11:31:04 +0200  Edward Hervey <edward@centricular.com>
 
-	* ext/adaptivedemux2/dash/gstdashdemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
 	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  adaptivedemux2: Move stream_seek() to the Stream class
-	  Move the last stream specific vfunc from the demux
-	  class to the stream class.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+	  adaptivedemux2: Answer GST_QUERY_CAPS
+	  If we have a generic caps, we can answer the query.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6716>
 
-2022-08-21 04:31:53 +1000  Jan Schmidt <jan@centricular.com>
+2024-04-19 11:29:25 +0200  Edward Hervey <edward@centricular.com>
 
-	* ext/adaptivedemux2/dash/gstdashdemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
-	* ext/adaptivedemux2/gstadaptivedemux-types.h:
 	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  adaptivedemux2: Refactor stream methods into the stream
-	  Unlike the legacy elements, GstAdaptiveDemuxStream is a GObject now,
-	  so a bunch of things that were actually stream methods on the
-	  parent demux object can directly become stream methods now.
-	  Move the stream class out to a header of its own.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
-
-2022-06-07 14:36:24 +1000  Jan Schmidt <jan@centricular.com>
+	  adaptivedemux2: Refactor output slot creation
+	  Set as much information as possible on the slot (including the associated
+	  track) *before* the associated source pad is added to the element.
+	  We need this so that incoming event/queries can be replied to if they are
+	  received when adding the pad
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6716>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/hls/m3u8.h:
-	  hlsdemux2/m3u8: Implement EXT-X-GAP parsing
-	  Read the EXT-X-GAP tag and set is_gap on the segment.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-05 17:30:01 +0200  Philipp Zabel <p.zabel@pengutronix.de>
 
-2022-06-07 14:13:39 +1000  Jan Schmidt <jan@centricular.com>
+	* sys/v4l2/gstv4l2allocator.c:
+	  v4l2bufferpool: Ensure freshly created buffers are not marked as queued
+	  Otherwise, if we run in to the copy case, this can cause these
+	  groups to stay around with queued flag set, but never actually
+	  queued, until gst_v4l2_allocator_flush() is called, which then
+	  erroneously frees the associated memories, causing the release
+	  function to decrement the allocator refcount where it was never
+	  incremented, resulting in early allocator disposal, and either
+	  deadlock or use after free.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6685>
+
+2024-04-12 17:17:53 +0800  Qian Hu (胡骞) <qian.hu@mediatek.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2/m3u8: Refactor parsing for readability
-	  Small readability improvements in the parsing code
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+	* docs/gst_plugins_cache.json:
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: add multiplane y42b(yuv422m)
+	  for some jpg file, mediatek v4l2 jpeg decoder
+	  hardware produce multi plane YUV 4:2:2 data
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6657>
 
-2022-10-14 06:21:41 +1100  Jan Schmidt <jan@centricular.com>
+2024-04-09 11:29:46 +0900  Hou Qi <qi.hou@nxp.com>
 
-	* ext/adaptivedemux2/downloadrequest.c:
-	* ext/adaptivedemux2/downloadrequest.h:
-	  adaptivedemux2/downloadhelper: Remove return val for download_request_add_buffer()
-	  The function can't actually fail, and the only caller
-	  was ignoring the result anyway, so remove the return value.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2bufferpool: queue back the buffer flagged LAST but empty
+	  Some decoder drivers need to wait enough capture buffers before
+	  starting to decode. But the dequeued buffer flag LAST but empty
+	  has no chance to queue back to driver, which makes decode hang
+	  after seek. So need to queue back such kind of buffer to driver.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6649>
 
-2022-10-14 06:20:06 +1100  Jan Schmidt <jan@centricular.com>
+2024-04-05 14:09:18 +0200  Philipp Zabel <p.zabel@pengutronix.de>
 
-	* ext/adaptivedemux2/downloadhelper.c:
-	  adaptivedemux2/downloadhelper: Add debug output of response headers
-	  Dump the HTTP response headers at TRACE level
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Drop writable check on output pool process
+	  Output buffers don't have to be writable. Accepting read-only buffers
+	  from the V4L2 buffer pool allows upstream elements to write directly
+	  into the V4L2 buffers without triggering a CPU copy into a new buffer
+	  from the same V4L2 buffer pool every time.
+	  Tested with the vivid output device:
+	  GST_DEBUG=GST_PERFORMANCE:7 gst-launch-1.0 videotestsrc ! v4l2sink device=/dev/video5
+	  With this change, gst_v4l2_buffer_pool_dqbuf() must be allowed to not
+	  resize read-only memories of output buffers.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6648>
+
+2024-04-13 10:57:43 +0100  Philippe Normand <philn@igalia.com>
 
-2022-10-14 06:19:11 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/vpx/gstvpxcompat.h:
+	* ext/vpx/gstvpxdec.c:
+	  vpxdec: Include vpx error details in errors and warnings
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6636>
 
-	* ext/adaptivedemux2/downloadhelper.c:
-	  adaptivedemux2/downloadhelper: Don't mark transfer as complete/error if cancelled.
-	  If the state of the download request was reset to UNSENT,
-	  it was cancelled. Don't update the state to COMPLETE or ERRORED
-	  in on_read_ready().
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-13 10:56:29 +0100  Philippe Normand <philn@igalia.com>
 
-2022-10-14 06:17:00 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/vpx/gstvp9enc.c:
+	  vp9enc: Include vpx error details in errors and warnings
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6636>
 
-	* ext/adaptivedemux2/downloadhelper.c:
-	  adaptivedemux2/downloadhelper: Ignore spurious read failure
-	  Sometimes g_input_stream_read_all_finish() can return
-	  0 bytes, but still succeed (return TRUE) and have more
-	  data available later. Only finish the transfer
-	  if it returns 0 bytes *and* FALSE with no error.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-13 10:55:55 +0100  Philippe Normand <philn@igalia.com>
 
-2022-10-14 06:15:45 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/vpx/gstvpxcompat.h:
+	* ext/vpx/gstvpxenc.c:
+	  vpxenc: Rename GST_VPX_WARN to GST_VPX_ENC_WARN
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6636>
 
-	* ext/adaptivedemux2/downloadhelper.c:
-	* ext/adaptivedemux2/downloadrequest.c:
-	* ext/adaptivedemux2/downloadrequest.h:
-	  adaptivedemux2/downloadhelper: Fix function name
-	  Fix a typo in the name of function download_request_despatch_progress()
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-12 16:57:00 +0800  Qian Hu (胡骞) <qian.hu@mediatek.com>
 
-2022-10-12 02:14:32 +1100  Jan Schmidt <jan@centricular.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: fix wrong full_range offset when parsing colr box
+	  use colr_data[18] >> 7 to get full range information, instead
+	  of colr_data[17] >> 7
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6634>
 
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Remove scheduler_lock mutex
-	  Remove the old unused scheduler_lock
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-11 10:10:19 -0400  William Wedler <william.wedler@videoray.com>
 
-2022-10-11 03:20:11 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/qt6/qt6glitem.cc:
+	  fix: qml6glsink: Notify that the returned QSGNode node has changes
+	  Sets the QSGNode::DirtyMaterial bit when a new buffer is used for the material's texture
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3469
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6633>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Hold tracks lock accessing input_period
-	  The input_period is protected by the TRACKS_LOCK,
-	  so make sure to hold that when accessing it.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-07 19:39:58 +0900  Jimmy Ohn <yongjin.ohn@lge.com>
 
-2022-08-16 23:01:46 +1000  Jan Schmidt <jan@centricular.com>
+	* ext/pulse/pulsedeviceprovider.c:
+	  pulsedeviceprovider: Add is_default_device_name function and missing lock
+	  Add is_default_device_name function to simplify compare device type
+	  name and fix the missing lock when accessing default_sink_name and
+	  default_source_name.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6599>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: Add state checks and clean up obsolete variables
-	  The cancelled flag was only set in the stream finalize()
-	  method, after all activity on the stream has stopped anyway.
-	  Replace uses of cancelled with checks on the stream state.
-	  Remove the replaced flag, which was checked but never set
-	  to TRUE anywhere any more.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3314>
+2024-04-10 00:04:02 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-10-30 20:28:25 +0900  Seungha Yang <seungha@centricular.com>
+	* meson.build:
+	  Back to development after 1.24.2
 
-	* docs/gst_plugins_cache.json:
-	* ext/vpx/gstvp9dec.c:
-	* ext/vpx/gstvp9enc.c:
-	* ext/vpx/gstvpxenc.c:
-	  vpx: Complete high bitdepth vp9 en/decoding support
-	  Adding 12bits variant formats to en/decoder, and high bitdepth
-	  4:4:4 (except for GBR) encoding support
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3298>
+=== release 1.24.2 ===
 
-2022-10-30 20:03:10 +0900  Seungha Yang <seungha@centricular.com>
+2024-04-09 21:48:55 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/vpx/gstvp9dec.c:
-	* ext/vpx/gstvp9enc.c:
-	* ext/vpx/gstvpxcompat.h:
-	* ext/vpx/gstvpxdec.h:
-	* ext/vpx/gstvpxenc.h:
-	  vpx: Define formats for compatibility
-	  ifdef for enum values never work. Instead, define new enum type
-	  and use it
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3298>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.2
 
-2022-10-27 23:57:58 +1100  Jan Schmidt <jan@centricular.com>
+2024-04-08 13:04:31 +0100  Philippe Normand <philn@igalia.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: m3u8: Use PDT to offset stream time when aligning playlist
-	  When matching segments across playlists with Program-Date-Times,
-	  use the difference in segment PDTs to adjust the stream time
-	  that's being transferred. This can fix cases where the
-	  segment boundaries don't align across different streams
-	  and the first download gets thrown away once the PTS
-	  is seen and found not to match.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3309>
+	* ext/vpx/gstvpxenc.c:
+	  vpxenc: Include vpx error details in errors and warnings
+	  The vpx_codec_t err_detail string usually provides additional context about the
+	  error, so include it in GStreamer warnings and errors, when it's not NULL.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6584>
+
+2024-04-08 19:53:06 +0200  Jochen Henneberg <jochen@centricular.com>
+
+	  qt6: Fixes for dummy texture
+	  * RED_OR_ALPHA8 will map value to alpha for OpenGL, use R8 to avoid
+	  2nd shader
+	  * Determine texel size for proper texture memory preparation
+	  * QByteArray::fromRawData() does shallow copy and thus leads to use of
+	  corrupted memory
+	  * Make sure RGBA dummy texture is fully opaque
+	  * QRhiTexture::create() must be called to allocate texture resources
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6581>
+
+2024-04-06 02:28:44 +0200  Jochen Henneberg <jochen@centricular.com>
+
+	* ext/qt/gstqsgmaterial.cc:
+	* ext/qt/gstqsgmaterial.h:
+	  qt: Fixup for dummy textures
+	  * Initialize dummy texture Ids
+	  * Ensure YUV->RGB matrix set for dummy textures
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6581>
+
+2024-04-04 13:21:44 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-11-01 02:17:46 +1100  Jan Schmidt <jan@centricular.com>
+	* gst/rtpmanager/gstrtpbin.c:
+	  rtpbin: Don't re-use a variable for a completely different purpose temporarily
+	  During RTP-Info synchronization, clock_base was temporarily switched
+	  from the actual clock-base to the base RTP time and then back some lines
+	  later.
+	  Instead directly work with the base RTP time. The comment about using a
+	  signed variable for convenience doesn't make any sense because all
+	  calculations done with the value are unsigned.
+	  Similarly, rtp_clock_base was overridden with the rtp_delta when
+	  calculating it, which was fine because it is not used anymore
+	  afterwards. Instead, introduce a new variable `rtp_delta` to make this
+	  calculation clearer.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6575>
+
+2024-04-04 13:19:18 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.h:
-	  hlsdemux2: Download new header when it changes
-	  Check whether the init file / MAP data for a segment
-	  is different to the current data and trigger an
-	  update if so. Previously, the header would only
-	  be checked in HLS after switching bitrate or
-	  after a seek / first download.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3307>
+	* gst/rtpmanager/gstrtpbin.c:
+	  rtpbin: Convert clock-base to extended RTP timestamp correctly
+	  It's not in the same period as the current RTP base time but always in
+	  the very first period. This avoids using it again at a much later time.
+	  The code in question is only triggered with rtcp-sync=rtp-info.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6575>
 
-2022-11-01 01:41:35 +1100  Jan Schmidt <jan@centricular.com>
+2024-04-04 13:11:54 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/hls/m3u8.h:
-	  m3u8: Expose GstM3U8InitFile methods
-	  Exposure ref/unref methods for the GstM3U8InitFile type,
-	  and add a gst_m3u8_init_file_equal() comparison method.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3307>
+	* gst/rtpmanager/gstrtpjitterbuffer.c:
+	  rtpjitterbuffer: Use an extended RTP timestamp for the clock-base
+	  It is compared to other extended RTP timestamps all over rtpjitterbuffer
+	  and since 4df3da3bab8 the initial extended RTP timestamp is not equal
+	  anymore to the plain RTP time.
+	  Continue passing a non-extended RTP timestamp via the `sync` signal for
+	  backwards compatibility. It will always be a timestamp inside the first
+	  extended timestamp period anyway.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6575>
 
-2022-10-21 17:24:41 +0200  Edward Hervey <edward@centricular.com>
+2024-03-26 13:33:56 +0200  Sebastian Dröge <sebastian@centricular.com>
 
 	* docs/gst_plugins_cache.json:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/hls/m3u8.h:
-	  adaptivedemux2: Improve minimum buffering threshold
-	  Previously the minimum buffering threshold was hardcoded to a specific
-	  value (10s). This is suboptimal this an actual value will depend on the actual
-	  stream being played.
-	  This commit sets the low watermark threshold in time to 0, which is an automatic
-	  mode. Subclasses can provide a stream `recommended_buffering_threshold` when
-	  update_stream_info() is called.
-	  Currently implemented for HLS, where we recommended 1.5 average segment
-	  duration. This will result in buffering being at 100% when the 2nd segment has
-	  been downloaded (minus a bit already being consumed downstream)
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3240>
-
-2022-10-28 18:57:44 +0530  Sanchayan Maity <sanchayan@asymptotic.io>
+	* gst/rtpmanager/gstrtphdrext-ntp.c:
+	  rtphdrext-ntp: Fix typo of the RFC number in the element metadata
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3417
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6537>
 
-	* gst/wavparse/gstwavparse.c:
-	  wavparse: Speed up type finding for DTS
-	  In order to figure out if the "raw" audio contained within the wav
-	  container is actually DTS, right now we call the typefinder helper
-	  which runs all typefinders.
-	  Speed up this type finding process by specifying the extension.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3294>
+2024-03-28 09:46:08 +0100  Robert Guziolowski <robert.guziolowski@gmail.com>
 
-2022-10-25 13:30:15 +1100  Matthew Waters <matthew@centricular.com>
+	* ext/qt6/gstqsg6material.cc:
+	  qml6glsink: fix destruction of underlying texture
+	  One should not directly delete the QRhiTexture instance.
+	  Instead it should be marked as to be deleted once QRhi::endFrame()
+	  is called (see: https://doc.qt.io/qt-6/qrhiresource.html#deleteLater )
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3443
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6506>
 
-	* docs/gst_plugins_cache.json:
-	* gst/isomp4/gstqtmuxmap.c:
-	  mp4mux: enable muxing VP9 streams
-	  As specified in https://www.webmproject.org/vp9/mp4/
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3260>
+2024-04-02 21:30:50 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-10-25 13:28:26 +1100  Matthew Waters <matthew@centricular.com>
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	  rtpjitterbuffer: Don't use estimated_dts to do default skew adjustment
+	  When the buffer DTS is estimated based on arrival time at the
+	  jitterbuffer (rather than provided on the incoming buffer itself),
+	  it shouldn't be used for skew adjustment. The typical case is
+	  packets being deinterleaved from a tunnelled TCP/HTTP RTSP stream,
+	  and the arrival times at the jitter buffer are not well enough
+	  correlated to usefully do skew adjustments.
+	  This restores the original intended behaviour for the 'estimated dts'
+	  path, that was broken years ago during other jitterbuffer refactoring.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6561>
+
+2024-04-06 12:26:39 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/isomp4/atoms.c:
-	* gst/isomp4/atoms.h:
-	* gst/isomp4/gstqtmux.c:
-	* gst/isomp4/gstqtmuxmap.c:
-	  qtmux: add support for writing vpcC box for VP9
-	  Increases compatibility for VP9 in .mov in at least VLC.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3260>
+	* ext/flac/meson.build:
+	  flac: Add wrap file and add fallback for it to the flac plugin
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6560>
 
-2022-10-04 18:21:15 -0300  Thibault Saunier <tsaunier@igalia.com>
+2024-02-28 11:28:23 +0800  Tim Blechmann <tim@klingt.org>
 
-	* ext/adaptivedemux2/dash/gstdashdemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  dashdemux2: Fix the way we determine current_position after seeks
-	  Without that the current_position was off after seeks, potentially
-	  leading to not properly push a last fragment when a `.stop` time was
-	  set.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3159>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: enforce a pixel aspect ratio of 1/1 if no data are available
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6559>
 
-2022-09-20 15:32:52 -0300  Thibault Saunier <tsaunier@igalia.com>
+2024-04-05 14:21:38 +0200  Philipp Zabel <p.zabel@pengutronix.de>
 
-	* ext/adaptivedemux2/dash/gstmpdclient.c:
-	  dash: Fix computing `repeat_index` when seeking in stream with a start !=0 on the first fragment
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3159>
+	* sys/v4l2/gstv4l2allocator.c:
+	  v4l2: allocator: Fix unref log/trace on memory release
+	  Use gst_object_unref() instead of g_object_unref() in
+	  gst_v4l2_allocator_release(), so refcounting log and
+	  tracer get to know about this unref.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6556>
 
-2022-09-22 11:20:55 -0300  Thibault Saunier <tsaunier@igalia.com>
+2024-03-29 11:14:54 +0900  Elliot Chen <elliot.chen@nxp.com>
 
-	* gst/matroska/matroska-demux.c:
-	  matroskademux: Let upstream handle seeking/duration query in time if possible
-	  So proper response are given for dash streams
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3159>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: fix error in calculating padding bottom for tile format
+	  This is a regression while porting to arbitrary tile dimensions
+	  introduced in !3424.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6554>
 
-2022-09-21 15:01:39 -0300  Thibault Saunier <tsaunier@igalia.com>
+2024-04-01 14:41:21 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* gst/matroska/matroska-demux.c:
-	* gst/matroska/matroska-demux.h:
-	  matroskademux: Start support for upstream segments in TIME format
-	  So we can use matroskademux for dash webm dash streams.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3159>
+	* gst/audioparsers/gstwavpackparse.c:
+	  wavpackparse: Use an unsigned integer for the block size calculations
+	  It's never negative.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6541>
 
-2022-01-24 16:49:52 +0100  Jakub Adam <jakub.adam@collabora.com>
+2024-04-01 14:36:19 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* sys/ximage/gstximagesrc.c:
-	  ximagesrc: grab the server while capturing screen image
-	  Makes sure screen resolution doesn't change in the middle of the
-	  process.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1562>
+	* gst/audioparsers/gstwavpackparse.c:
+	  wavpackparse: Fix potential integer overflow on ID_ODD_SIZE blocks
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6541>
 
-2021-12-17 14:57:57 +0100  Jakub Adam <jakub.adam@collabora.com>
+2024-04-01 14:33:05 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* sys/ximage/gstximagesrc.c:
-	* sys/ximage/gstximagesrc.h:
-	  ximagesrc: change video resolution when X11 screen gets resized
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1562>
+	* gst/audioparsers/gstwavpackparse.c:
+	* gst/audioparsers/gstwavpackparse.h:
+	  wavpackparse: Explicitly handle ID_WVX_NEW_BITSTREAM
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6541>
 
-2022-10-23 20:32:35 +0100  Tim-Philipp Müller <tim@centricular.com>
+2024-03-28 19:49:46 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/meson.build:
-	* gst/xingmux/gstxingmux.c:
-	* gst/xingmux/gstxingmux.h:
-	* gst/xingmux/meson.build:
-	* gst/xingmux/plugin.c:
-	* meson_options.txt:
-	* tests/check/elements/xingmux.c:
-	* tests/check/elements/xingmux_testdata.h:
-	* tests/check/meson.build:
-	  xingmux: move from gst-plugins-ugly to gst-plugins-good
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/415
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3251>
+	* tests/check/elements/rtpred.c:
+	  tests: rtpred: fix out-of-bound writes
+	  Don't write more data to the buffer than we allocated
+	  space for.
+	  Fixes #3312
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6478>
 
-2022-10-21 16:23:08 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-03-27 15:21:56 +0900  Haihua Hu <jared.hu@nxp.com>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Only EOS on timeout if all streams are timed out/EOS
-	  Otherwise a stream that is just temporarily inactive might time out and
-	  then can never become active again because the EOS event was sent
-	  already.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3238>
+	* sys/v4l2/gstv4l2src.c:
+	  v4l2src: need maintain the caps order in caps compare when fixate
+	  if the calculated "distance" of caps A and B from the preference are
+	  equal, need to keep the original order instead of swap them
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6473>
 
-2022-10-18 16:51:39 +1100  Matthew Waters <matthew@centricular.com>
+2024-03-26 23:53:30 +1100  Jan Schmidt <jan@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtp/gstrtpulpfecdec.c:
-	* gst/rtp/gstrtpulpfecdec.h:
-	  rtpulpfecdec: add property for passthrough
-	  Support for enabling and disabling decoding of FEC data decoding on
-	  packet loss events and unconditional seqnum rewriting of packets.
-	  See
-	  https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/581
-	  for background.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3212>
-
-2022-10-14 01:23:04 +0000  Devin Anderson <danderson@microsoft.com>
+	* gst/rtp/gstrtpmp4adepay.c:
+	  rtpmp4adepay: Set duration on outgoing buffers
+	  If we can calculate timestamps for buffers, then set the duration
+	  on outgoing buffers based on the number of samples depayloaded.
+	  This can fix the muxing to mp4, where otherwise the last packet
+	  in a muxed file will have 0 duration in the mp4 file.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6456>
 
-	* gst/wavparse/gstwavparse.c:
-	  wavparse: Avoid occasional crash due to referencing freed buffer.
-	  We've seen occasional crashes in the `wavparse` module associated with
-	  referencing a buffer in `gst_wavparse_chain` that's already been freed.  The
-	  reference is stolen when the buffer is transferred to the adapter with
-	  `gst_adapter_push` and, IIUC, assuming the source doesn't hold a reference to
-	  the buffer, the buffer could be freed during interaction with the adapter in
-	  `gst_wavparse_stream_headers`.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3179>
+2024-03-21 18:07:42 +0900  Hou Qi <qi.hou@nxp.com>
 
-2022-10-13 11:21:42 -0400  Julian Bouzas <julian.bouzas@collabora.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: Also set max_width/max_height if enum framesize fail
+	  Some driver doesn't implement enum_framesize. The maximum supported
+	  size can be got by trying format with a very large size. Also need
+	  to set max_width/max_height for this case, otherwise default encoded
+	  buffer size 256kB is too small.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6430>
 
-	* docs/gst_plugins_cache.json:
-	  riff: Mark jpeg as parsed
-	  This is needed so that autoplugging works with avidemux and JPEG decoders that
-	  need parsed sink caps (eg rockchip 'mppjpegdec' decoder). It also works fine
-	  with 'jpegdec' decoder regardless.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3175>
+2024-03-22 01:38:06 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-10-13 00:20:45 +0000  Devin Anderson <danderson@microsoft.com>
+	* meson.build:
+	  Back to development
 
-	* gst/wavparse/gstwavparse.c:
-	* tests/check/elements/wavparse.c:
-	* tests/files/corruptheadertestsrc.wav:
-	  wavparse: Fix crash that occurs in push mode when header chunks are corrupted in certain ways.
-	  In the case that a test is provided for, the size of the `fmt ` chunk is
-	  changed from 16 bytes to 18 bytes (bytes 17 - 20 below):
-	  ```
-	  $ hexdump -C corruptheadertestsrc.wav
-	  00000000  52 49 46 46 e4 fd 00 00  57 41 56 45 66 6d 74 20  |RIFF....WAVEfmt |
-	  00000010  12 00 00 00 01 00 01 00  80 3e 00 00 00 7d 00 00  |.........>...}..|
-	  00000020  02 00 10 00 64 61 74 61                           |....data|
-	  00000028
-	  ```
-	  (Note that the original file is much larger.  This was the smallest sub-file
-	  I could find that would generate the crash.)
-	  Note that, while the same issue doesn't cause a crash in pull mode, there's a
-	  different issue in that the file is processed successfully as if it was a .wav
-	  file with zero samples.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3173>
+=== release 1.24.1 ===
 
-2022-10-11 15:00:37 +0200  Edward Hervey <edward@centricular.com>
+2024-03-21 21:47:53 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* sys/oss4/oss4-sink.c:
-	* sys/oss4/oss4-source.c:
-	  oss4: Fix debug category initialization
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1456
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3158>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.24.1
 
-2022-10-08 01:03:13 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+2024-03-18 15:07:28 +0100  Edward Hervey <edward@centricular.com>
 
-	* gst/multifile/gstsplitmuxpartreader.c:
-	  splitmuxsrc: don't queue data on unlinked pads
-	  Once a pad has returned NOT_LINKED, the part reader shouldn't let its
-	  corresponding data queue run full and eventually (after 20 seconds)
-	  stall playback.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3145>
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  adaptivedemux2: Don't use g_str_equal on potentially NULL strings
+	  It is only meant to be used as a callback. The fallback macro uses strcmp which
+	  doesn't handle NULL strings gracefully. Instead use g_strcmp0
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6410>
 
-2022-10-03 20:28:47 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-03-18 15:02:29 +0100  Edward Hervey <edward@centricular.com>
 
-	* gst/rtpmanager/gstrtpsession.c:
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtpsession.h:
-	* gst/rtpmanager/rtpsource.c:
-	* gst/rtpmanager/rtpsource.h:
-	  rtpsource: Don't do probation for RTX sources
-	  Disable probation for RTX sources as packets will arrive very
-	  irregularly and waiting for a second packet usually exceeds the deadline
-	  of the retransmission.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/181
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3112>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Avoid NULL pointer usage
+	  The pending/current variant are both NULL when the demuxer is resetted.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6410>
 
-2022-10-03 19:58:38 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-03-18 15:00:02 +0100  Edward Hervey <edward@centricular.com>
 
-	* tests/examples/rtp/client-rtpaux.c:
-	  rtp: examples: client-rtpaux: Provide correct caps by payload type and RTX pt map by session
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3112>
+	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
+	  adaptivedemux2: Handle context going away
+	  This issue can happen when the scheduler loop was stopped (and context went
+	  away). We no longer want to push/pop main context threads.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6410>
 
-2019-01-25 17:04:50 -0500  George Kiagiadakis <george.kiagiadakis@collabora.com>
+2024-03-18 14:57:43 +0100  Edward Hervey <edward@centricular.com>
 
-	* tests/check/elements/rtpsession.c:
-	  tests/check/rtpsession: extend test_internal_sources_timeout
-	  to verify that rtx SSRCs do not BYE after timeout
-	  See https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/issues/360
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3112>
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	  hlsdemux2: Improve detection of playlist updates
+	  In the case we are not updating an existing playlist, we only want to reset the
+	  download error count if the URI we are downloading was not the previous one we
+	  were trying to load
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6410>
 
-2022-10-03 19:12:55 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-03-13 13:39:21 +0100  Alexander Slobodeniuk <aslobodeniuk@fluendo.com>
 
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtpsource.c:
-	* gst/rtpmanager/rtpsource.h:
-	  rtpsession: Remember the corresponding media SSRC for RTX sources
-	  This allows timing out the RTX source and sending BYE for it when the
-	  actual media source belonging to it is timed out.
-	  This change only applies to sending sources from this session.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/issues/360
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3112>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: remove 'deprecated' flag from the 'push-backchannel-sample' signal
+	  It seems that it was added by accident when copying from push-backchannel-buffer
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6363>
 
-2022-10-03 19:20:14 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-03-11 10:39:27 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtpsource.c:
-	* gst/rtpmanager/rtpsource.h:
-	  rtpsource: Rename rtp_source_update_caps to rtp_source_update_send_caps
-	  To make it clear that this is only used for sending RTP sources.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3112>
+	* ext/mpg123/gstmpg123audiodec.c:
+	  mpg123audiodec: Correctly handle the case of clipping all decoded samples
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3365
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6357>
 
-2022-10-03 13:48:36 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-03-11 18:13:37 +0100  Piotr Brzeziński <piotr@centricular.com>
 
-	* gst/rtpmanager/gstrtpsession.c:
-	  rtpsession: Rename gst_rtp_session_sink_setcaps to gst_rtp_session_setcaps_recv_rtp
-	  to make it clearer that this is for setting receiver caps and to make it
-	  more consistent with gst_rtp_session_setcaps_send_rtp.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3112>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix wrapping temporary memory in buffers
+	  That memory can disappear at any moment, doesn't cost much to just copy those few bytes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6339>
 
-2022-10-06 15:02:22 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-01-31 00:32:22 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
 	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Retry SETUP with non-compliant URL resolution on "Bad Request" and "Not found"
-	  Various RTSP servers/cameras assume base and control URL to be simply
-	  appended instead of being resolved according to the relative URL
-	  resolution algorithm as mandated by the RTSP specification.
-	  To work around this, try using such a non-compliant control URL if the
-	  server didn't like the URL used in the first SETUP request.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1447
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/922
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3127>
-
-2022-10-04 03:57:31 +0100  Tim-Philipp Müller <tim@centricular.com>
-
-	* docs/gst_plugins_cache.json:
-	* meson.build:
-	  Back to development
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3115>
+	  rtspsrc: Don't invoke close when stopping if we've started cleanup
+	  When we're doing a state change from PLAYING to NULL, first we invoke
+	  gst_rtspsrc_loop_send_cmd_and_wait (..., CMD_CLOSE, ...) during
+	  PAUSED_TO_READY which will schedule a TEARDOWN to happen async on the
+	  task thread.
+	  The task thread will call gst_rtspsrc_close(), which will send the
+	  TEARDOWN and once it's complete, it will call gst_rtspsrc_cleanup()
+	  without taking any locks, which frees src->streams.
+	  At the same time however, the state change in the app thread will
+	  progress further and in READY_TO_NULL it will call gst_rtspsrc_stop()
+	  which calls gst_rtspsrc_close() a second time, which accesses
+	  src->streams (without a lock again), which leads to simultaneous
+	  access of src->streams, and a segfault.
+	  So the state change and the cleanup are racing, but they almost always
+	  complete sequentially. Either the cleanup sets src->streams to NULL or
+	  _stop() completes first. Very rarely, _stop() can start while
+	  src->streams is being freed in a for loop. That causes the segfault.
+	  This is unlocked access is unfixable with more locking, it just leads
+	  to deadlocks. This pattern has been observed in rtspsrc a lot: state
+	  changes and cleanup in the element are unfixably racy, and that
+	  foundational issue is being addressed separately via a rewrite.
+	  The bandage fix here is to prevent gst_rtspsrc_stop() from accessing
+	  src->streams after it has already been freed by setting src->state to
+	  INVALID.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6330>
+
+2024-03-07 16:17:41 +0100  Michael Tretter <m.tretter@pengutronix.de>
 
-=== release 1.21.1 ===
+	* meson_options.txt:
+	  meson: Fix description in qt options
+	  The qt-x11 description contains a copy/paste error from the qt-wayland option.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6313>
 
-2022-10-04 01:14:01 +0100  Tim-Philipp Müller <tim@centricular.com>
+2024-02-16 18:11:07 +0100  Mathieu Duponchelle <mathieu@centricular.com>
 
-	* ChangeLog:
-	* NEWS:
-	* RELEASE:
-	* docs/gst_plugins_cache.json:
-	* gst-plugins-good.doap:
-	* meson.build:
-	  Release 1.21.1
+	* gst/rtp/gstrtpgstpay.c:
+	  rtpgstpay: flush on EOS
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6298>
 
-2022-10-04 01:13:59 +0100  Tim-Philipp Müller <tim@centricular.com>
+2023-08-11 13:06:24 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ChangeLog:
-	  Update ChangeLogs for 1.21.1
+	* gst/rtp/gstrtpgstpay.c:
+	* gst/rtp/gstrtpgstpay.h:
+	  rtpgstpay: Delay pushing of event packets until the next buffer
+	  And also re-timestamp them with the current buffer's PTS.
+	  Not doing so keeps the timestamps of event packets as
+	  GST_CLOCK_TIME_NONE or the timestamp of the previous buffer, both of
+	  which are bogus.
+	  Making sure that (especially) the first packet has a valid timestamp
+	  allows putting e.g. the NTP timestamp RTP header extension on it.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6298>
 
-2022-07-27 11:19:50 +0200  Edward Hervey <edward@centricular.com>
+2024-02-26 19:17:27 -0600  Elizabeth Figura <zfigura@codeweavers.com>
 
 	* gst/isomp4/qtdemux.c:
-	  qtdemux: Don't stop task when resetting
-	  This is a regression that was introduced in
-	  cca2f555d14b5751f7f9d466b66127544dad5138 (yes, 9 years ago).
-	  The only place where a demuxer streaming thread should be stopped is when the
-	  sinkpad is deactivated from pull mode (i.e. PAUSED->READY).
-	  Attempting to stop the task in this function would cause this to happen when a
-	  FLUSH_STOP or STREAM_START event is received... which can cause deadlocks.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3109>
+	  qtdemux: Do not set channel-mask to zero
+	  Leave it uninitialized, so that the downstream decoder will initialize it appropriately. Setting it to zero is wrong.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6296>
 
-2022-09-30 18:57:01 +0200  Mathieu Duponchelle <mathieu@centricular.com>
-
-	* gst/multifile/gstsplitmuxpartreader.c:
-	  splitmuxsrc: don't consider unlinked pads when deactivating part
-	  If splitmuxsrc exposes multiple pads, but only one is linked, part pads
-	  will never see an EOS event. This shouldn't prevent the part from being
-	  eventually deactivated.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3099>
-
-2022-03-01 16:30:10 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2024-03-01 02:44:57 +1100  Jan Schmidt <jan@centricular.com>
 
 	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Fix usage of IPv6 connections in SETUP
-	  If the SETUP request returns an IPv6 server address in the Transport
-	  field, we would generate an incorrect URI, and multiudpsink would fail
-	  to initialize:
-	  ```
-	  rtspsrc gstrtspsrc.c:9780:dump_key_value:<source>    key: 'Transport', value: 'RTP/AVP;unicast;source=fe80::dc27:25ff:fe5e:bd13:8080;client_port=62696-62697;server_port=4000-4001'
-	  ...
-	  rtspsrc gstrtspsrc.c:4595:gst_rtspsrc_stream_configure_udp_sinks:<source> configure RTP UDP sink for fe80::dc27:25ff:fe5e:bd13:8080:4000
-	  ...
-	  multiudpsink gstmultiudpsink.c:1229:gst_multiudpsink_configure_client:<udpsink0> error: Invalid address family (got 23)
-	  ```
-	  We can't look at stream->is_ipv6 because we can't rely on the server
-	  returning the right value there. In the issue reported about this,
-	  server reported itself as `KuP RTSP Server/0.1`, and the SDP was:
-	  ```
-	  c=IN IP4
-	  m=video 54608 RTP/AVP 96
-	  a=rtpmap:96 H264/90000
-	  ```
-	  So we need to parse the string value and figure out the family
-	  ourselves.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1058
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1819>
+	  rtspsrc: Parse Speed/Scale before Range in responses
+	  Parse the speed and scale in the server's response
+	  *before* the range, so that the range start/stop
+	  are swapped (or not swapped) correctly based
+	  on the server's actual chosen values. Otherwise,
+	  the old rate from the segment is used - what the
+	  last seek asked for, but not necessarily what
+	  the server chooses.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6295>
+
+2024-02-29 12:06:25 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-09-27 13:56:54 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Handle queries and events with no manager
+	  When doing direct output with no session manager, we still
+	  want to respond to queries and events from downstream, so
+	  install the handlers
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6295>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: guard against timestamp calculation overflow in gap event loop
-	  Could possibly cause an endless loop.
-	  Fixes #1400.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3084>
+2024-02-29 11:14:47 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-09-27 00:08:41 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: return NO_PREROLL on PLAYING->PAUSED too
+	  When transitioning back to PAUSED and rtspsrc is live, return
+	  NO_PREROLL so the pipeline knows to skip preroll here too.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6295>
 
-	* tests/check/elements/dash_mpd.c:
-	  dashdemux2: fix mpd unit test expectations
-	  Update unit test for some mpd cases that were reporting
-	  timestamps including the period start time, while
-	  dashdemux2 expects that it needs to add the period
-	  start time itself.
-	  Fix the tests to not expect the period start time
-	  to be included.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3025>
+2024-02-24 11:07:26 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-08-23 23:12:44 +0900  Junsoo Park <junsoo81.park@lge.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Consider 503 Service Not Available when handling broken control urls
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6267>
 
-	* ext/adaptivedemux2/dash/gstmpdclient.c:
-	  dashdemux2: Set timestamp relative to period start
-	  These values will be referred to as timestamp relative to period start
-	  so need to subtract period start time from the values.
-	  Fixes a problem with determining the start position when playing Live content
-	  with SegmentTimeline, presentationTimeOffset and a non-0 period start time.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3025>
+2024-03-05 13:45:27 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-09-22 19:02:10 +0200  Jakub Adam <jakub.adam@collabora.com>
+	* meson.build:
+	  Back to development
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6264>
 
-	* ext/vpx/gstvp9dec.c:
-	* ext/vpx/gstvp9enc.c:
-	  vp9: check if libvpx supports high bit depth
-	  Detect at runtime if libvpx is compiled with --enable-vp9-highbitdepth
-	  and enable 10bit video formats in element caps accordingly.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3067>
+=== release 1.24.0 ===
 
-2022-09-21 19:19:45 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2024-03-04 23:51:42 +0000  Tim-Philipp Müller <tim@centricular.com>
 
+	* NEWS:
+	* README.md:
+	* RELEASE:
+	* gst-plugins-good.doap:
 	* meson.build:
-	  meson: Use implicit builtin dirs in pkgconfig generation
-	  Starting with Meson 0.62, meson automatically populates the variables
-	  list in the pkgconfig file if you reference builtin directories in the
-	  pkgconfig file (whether via a custom pkgconfig variable or elsewhere).
-	  We need this, because ${prefix}/libexec is a hard-coded value which is
-	  incorrect on, for example, Debian.
-	  Bump requirement to 0.62, and remove version compares that retained
-	  support for older Meson versions.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1245
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3061>
-
-2022-09-21 00:26:38 +0900  Seungha Yang <seungha@centricular.com>
+	  Release 1.24.0
 
-	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
-	  adaptivedemux2: Always wake up loop thread on unpause
-	  Otherwise loop thread will sleep forever because
-	  GstAdaptiveDemuxLoop.paused flag update is not signalled
-	  when loop was marked as stopped already from other thread.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3057>
+2024-02-27 14:22:58 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-2022-09-16 02:01:58 +1000  Jan Schmidt <jan@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Increase rank to PRIMARY for autoplug purposes
+	  This affects autoplug by gst_element_make_from_uri() in, for example,
+	  uridecodebin. The element should've already been PRIMARY rank, but it
+	  was NONE because gst_element_make_from_uri() doesn't ignore NONE rank
+	  elements when searching for element factories, unlike decodebin.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/502
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6226>
 
-	* ext/adaptivedemux2/gstadaptivedemux-period.c:
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux-track.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: Rework input download wakeups
-	  Change the way streams are woken up to download more data.
-	  Instead of checking the level on tracks that are being
-	  output as data is dequeued, calculate a 'wakeup time'
-	  at which it should download more data, and wake up
-	  the stream when the global output position crosses
-	  that threshold.
-	  For efficiency, compute the earliest wakeup time
-	  for all streams and store it on the period, so the
-	  output loop can quickly check only a single value
-	  to decide if something needs waking up.
-	  Does the same buffering as the previous method,
-	  but ensures that as we approach the end of
-	  one period, the next period continues incrementally
-	  downloading data so that it is fully buffered when
-	  the period starts.
-	  Fixes issues with multi-period VOD content where
-	  download of the second period resumes only after
-	  the first period is completely drained.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3055>
-
-2022-09-15 09:04:10 +0200  Edward Hervey <edward@centricular.com>
+2024-02-26 09:27:40 +0100  Edward Hervey <edward@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Handle negative time mappings
-	  Some servers can return playlists with "old" media playlists and different
-	  Discont Sequence.
-	  In those cases, the segment stream times would be negative when creating a new
-	  time mapping. In order to properly handle such scenarios, shift the values to
-	  stored accordingly to end up with non-negative reference stream time.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3054>
+	* README.md:
+	* RELEASE:
+	  docs: Use Discourse and Matrix as prefered communication channels
+	  Part of: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6220
 
-2022-09-19 20:48:01 +0300  Mart Raudsepp <mart@leio.tech>
+2024-02-22 20:35:28 +0900  Seungha Yang <seungha@centricular.com>
 
-	* ext/shout2/meson.build:
-	  shout: fix minimum requirement to libshout >= 2.4.3
-	  commit e64c6f0b93ced added usage of the SHOUT_USAGE_UNKNOWN symbol, but this
-	  became available in the Icecast-libshout 2.4.3 release, not 2.4.2
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3048>
+	* ext/jpeg/gstjpegdec.c:
+	  jpegdec: Fix progressive/interlaced detection
+	  If input height and parsed one are identical, do not consider it as interlaced
+	  Fixing below pipeline:
+	  gst-launch-1.0 videotestsrc ! video/x-raw,format=I420,width=640,height=10 \
+	  ! jpegenc ! jpegparse ! jpegdec ! videoconvert ! autovideosink
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6181>
 
-2022-08-31 11:13:06 +0800  Elliot Chen <elliot.chen@nxp.com>
+2024-02-26 23:14:54 +0900  Seungha Yang <seungha@centricular.com>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: add cap negotiation fail check in the capture configuration change
-	  The capture configuration change may cause negotiation fail.
-	  Need to check it to avoid enter the endless loop.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2960>
+	* ext/jpeg/gstjpegdec.c:
+	  jpegdec: Remove trailing white space
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6181>
 
-2022-08-29 10:17:45 -0400  Thibault Saunier <tsaunier@igalia.com>
+=== release 1.23.90 ===
 
-	* docs/meson.build:
-	  doc: Do not build plugins to build the doc
-	  It is not actually necessary
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2954>
+2024-02-23 18:20:11 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-09-15 12:14:56 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.23.90
 
-	* tests/check/meson.build:
-	  tests: add a few more orc tests
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3029>
+2024-02-23 11:45:50 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-2022-09-14 10:15:41 +0200  Edward Hervey <edward@centricular.com>
+	* ext/soup/meson.build:
+	* meson_options.txt:
+	  soup: Re-add soup-lookup-dep option
+	  It's still useful on Linux since it ensures that the tests are going
+	  to be built, since they use the same dep lookup as the plugin now.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6197>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: Fix crash on live playlist with single entry
-	  If there is a single entry, we would end up computing a minimum distance of 0,
-	  and would therefore read entries from after the segment array
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3024>
+2024-02-21 19:13:45 +1100  Matthew Waters <matthew@centricular.com>
 
-2021-03-24 14:20:18 -0500  Zebediah Figura <z.figura12@gmail.com>
+	* tests/examples/qt/qmlsink-multisink/main.cpp:
+	  examples/qmlsinnk-multisink: allow running with leaks tracer
+	  Include a gst_deinit() after the qml engine has been destroyed.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6160>
 
-	* meson.build:
-	  meson: Build with -Wl,-z,nodelete to prevent unloading of dynamic libraries and plugins
-	  GLib made the unfortunate decision to prevent libgobject from ever being
-	  unloaded, which means that now any library which registers a static type
-	  can't ever be unloaded either (and any library that depends on those,
-	  ad nauseam).
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/778>
+2024-02-21 19:11:59 +1100  Matthew Waters <matthew@centricular.com>
 
-2022-09-13 00:56:53 +1000  Jan Schmidt <jan@centricular.com>
+	* tests/examples/qt/qmlsink-multisink/videoitem/videoitem.cpp:
+	  examples/qml: fix some leaks in the multisink example
+	  A GstPad was being leaked and possibly the qmlglsink element depending
+	  on if Qt runs the scenegraph thread again when destroying the example
+	  video item.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6160>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Always check bitrate in live
-	  When advancing fragment in live, it's normal to return
-	  GST_FLOW_EOS when playing at the live edge of the available
-	  fragments. In that case, we still want to adjust bitrate
-	  dynamically.
-	  Fixes issue with dashdemux2 where the current bitrate of
-	  each adaptation set is changed to the lowest one when
-	  updating the mpd for a live stream.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3020>
-
-2022-09-12 14:18:47 -0400  Matt Crane <matt@standard.ai>
+2024-02-21 19:09:20 +1100  Matthew Waters <matthew@centricular.com>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Fix calculation of reference timestamp metadata
-	  Add support for RTCP SRs that contain RTP timestamps later than the
-	  current timestamps in the RTP stream packet buffers.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3019>
+	* ext/qt/qtitem.cc:
+	* ext/qt6/qt6glitem.cc:
+	  qml, qml6: Fix leak of QSGMaterial/Geometry (and therefore a possible GstBuffer)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6160>
 
-2022-09-08 17:06:26 +0000  Filip Hanes <filip@hanes.tech>
+2024-02-07 15:04:53 +1100  Matthew Waters <matthew@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* ext/libpng/gstpngenc.c:
-	  pngenc: lower minimum width and height to 1x1
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3003>
+	* ext/qt6/gstqsg6material.cc:
+	  qml6: fix a leak of the wrapped QSGTextures
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6160>
 
-2022-08-30 11:45:05 +0200  Edward Hervey <edward@centricular.com>
+2024-02-21 19:21:57 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/dash/gstxmlhelper.c:
-	* tests/check/elements/dash_mpd.c:
-	  dashdemux2: Remove bogus limitation checks for duration fields
-	  Just like for the seconds field, there are no limitations on the hours and
-	  minutes fields. The specification for xml schema duration fields doesn't forbid
-	  specifying durations with only (huge) minutes or hours values.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2951>
+	* gst/rtpmanager/gstrtphdrext-clientaudiolevel.c:
+	  rtphdrext-clientaudiolevel: Fix typo in documentation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6175>
 
-2022-08-21 03:37:40 +1000  Jan Schmidt <jan@centricular.com>
+2024-02-21 13:45:47 +0100  Arnaud Vrac <avrac@freebox.fr>
 
-	* ext/adaptivedemux2/dash/gstdashdemux.c:
-	* ext/adaptivedemux2/dash/gstdashdemux.h:
-	* ext/adaptivedemux2/dash/gstmpdclient.c:
-	* ext/adaptivedemux2/dash/gstmpdclient.h:
-	  dashdemux2: Preserve current representation on live manifest updates
-	  When updating a manifest during live playback, preserve the current
-	  representation for each stream.
-	  During update_fragment_info, if the current representation changed
-	  because it couldn't be matched, trigger a caps change and new
-	  header download.
-	  This reverts commit e0e1db212fd0df2239583b9099fc4361adeded05
-	  and reapplies "dashdemux: Fix issue when manifest update sets slow start
-	  without passing necessary header & caps changes downstream" with
-	  changes.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2920>
-
-2022-09-02 17:21:43 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/adaptivedemux2/meson.build:
+	  adaptivedemux2: fix build with recent meson
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6168>
 
-	* tests/check/elements/rtpjitterbuffer.c:
-	  rtpjitterbuffer: Add test for crash caused by removing timers twice
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2973>
+2024-02-20 11:55:25 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-09-02 12:20:58 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtppassthroughpay.c:
+	  rtppassthroughpay: fix critical in gst-inspect
+	  gst_segment_to_running_time() will fail noisily
+	  if the segment has not been initialised yet.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6151>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Make it more explicit that update_rtx_timers() takes ownership of the passed in timer
-	  It is not valid anymore afterwards and must not be used, otherwise an
-	  already freed pointer might be used.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2973>
+2024-02-21 02:30:11 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-2022-09-02 12:20:30 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/adaptivedemux2/gstadaptivedemuxelement.c:
+	* ext/adaptivedemux2/meson.build:
+	* ext/soup/gstsoupelement.c:
+	* ext/soup/gstsouphttpsrc.c:
+	* ext/soup/gstsouploader.c:
+	* ext/soup/gstsouploader.h:
+	* ext/soup/gstsouputils.h:
+	* ext/soup/meson.build:
+	* meson_options.txt:
+	* tests/check/meson.build:
+	  soup: Link to libsoup in all cases on non-Linux
+	  We have unsolvable issues on macOS because of this, and the feature
+	  was added specifically for issues that occur on Linux distros since
+	  they ship both libsoup 2.4 and 3.0.
+	  Everyone else should just pick one and use it, since you cannot mix
+	  the two in a single process anyway.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1171
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6156>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Don't shadow variable
-	  While this didn't cause any problems in this context it is simply
-	  confusing.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2973>
+2024-02-17 23:33:26 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-09-02 12:19:26 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Reset combined flows after a seek before restarting
+	  After a flushing seek, rtspsrc doesn't reset the last_ret value for
+	  streams, so might immediately shut down again when it resumes pushing
+	  buffers to pads due to a cached `GST_FLOW_FLUSHING` result
+	  Prevent a stored flushing value from immediately stopping
+	  playback again by resetting pad flows before (re)starting
+	  playback.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6137>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Change RTX timer availability checks to assertions
-	  It's impossible to end up in the corresponding code without a timer for
-	  RTX packets because otherwise it would be an unsolicited RTX packet and
-	  we would've already returned early.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2973>
+2023-11-29 17:43:30 +0200  Maksym Khomenko <maksym.khomenko@skelia.partners>
 
-2022-09-02 12:17:39 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/osxaudio/gstosxcoreaudiocommon.c:
+	  osxaudio: add mapping for top/left/right surround channels
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5731>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Only unschedule timers for late packets if they're not RTX packets and only once
-	  Timers for RTX packets are dealt with later in update_rtx_timers(), and
-	  timers for non-RTX packets would potentially also be unscheduled a
-	  second time from there so avoid that.
-	  Also don't shadow the timer variable from the outer scope but instead
-	  make use of it directly.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2973>
+2023-11-29 17:40:52 +0200  Maksym Khomenko <maksym.khomenko@skelia.partners>
 
-2022-07-05 16:15:19 +0200  Patricia Muscalu <patricia@axis.com>
+	* sys/osxaudio/gstosxcoreaudiocommon.c:
+	  osxaudio: correct mapping for left/right surround
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5731>
 
-	* gst/rtp/gstrtph265pay.c:
-	* gst/rtp/gstrtph265pay.h:
-	* tests/check/elements/rtph265.c:
-	  rtph265: keep delta unit flag
-	  Without this patch all buffers that pass the payloader
-	  are marked as non-delta-unit buffers.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2969>
+2024-02-12 09:55:24 +0100  Marc Leeman <m.leeman@televic.com>
 
-2022-09-01 15:11:31 -0400  Thibault Saunier <tsaunier@igalia.com>
+	* ext/qt6/meson.build:
+	  qt6: search in /usr/lib/qt6/bin/ for qsb
+	  In Debian and possibly other distributions, qsb (qt6-shader-baker) is
+	  not in the default path, but in a QT6 specific path. Search there too
+	  Applied changes from Nirbheek
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6095>
 
-	* ext/aalib/meson.build:
-	* ext/cairo/meson.build:
-	* ext/dv/meson.build:
-	* ext/flac/meson.build:
-	* ext/gdk_pixbuf/meson.build:
-	* ext/gtk/meson.build:
-	* ext/jack/meson.build:
-	* ext/jpeg/meson.build:
-	* ext/lame/meson.build:
-	* ext/libpng/meson.build:
-	* ext/mpg123/meson.build:
-	* ext/pulse/meson.build:
-	* ext/qt/meson.build:
-	* ext/shout2/meson.build:
-	* ext/speex/meson.build:
-	* ext/taglib/meson.build:
-	* ext/twolame/meson.build:
-	* ext/vpx/meson.build:
-	* ext/wavpack/meson.build:
-	* gst/alpha/meson.build:
-	* gst/apetag/meson.build:
-	* gst/audiofx/meson.build:
-	* gst/audioparsers/meson.build:
-	* gst/auparse/meson.build:
-	* gst/autodetect/meson.build:
-	* gst/avi/meson.build:
-	* gst/cutter/meson.build:
-	* gst/debugutils/meson.build:
-	* gst/deinterlace/meson.build:
-	* gst/dtmf/meson.build:
-	* gst/effectv/meson.build:
-	* gst/equalizer/meson.build:
-	* gst/flv/meson.build:
-	* gst/flx/meson.build:
-	* gst/goom/meson.build:
-	* gst/goom2k1/meson.build:
-	* gst/icydemux/meson.build:
-	* gst/id3demux/meson.build:
-	* gst/imagefreeze/meson.build:
-	* gst/interleave/meson.build:
-	* gst/isomp4/meson.build:
-	* gst/law/meson.build:
-	* gst/level/meson.build:
-	* gst/matroska/meson.build:
-	* gst/monoscope/meson.build:
-	* gst/multifile/meson.build:
-	* gst/multipart/meson.build:
-	* gst/replaygain/meson.build:
-	* gst/rtp/meson.build:
-	* gst/rtpmanager/meson.build:
-	* gst/rtsp/meson.build:
-	* gst/shapewipe/meson.build:
-	* gst/smpte/meson.build:
-	* gst/spectrum/meson.build:
-	* gst/udp/meson.build:
-	* gst/videobox/meson.build:
-	* gst/videocrop/meson.build:
-	* gst/videofilter/meson.build:
-	* gst/videomixer/meson.build:
-	* gst/wavenc/meson.build:
-	* gst/wavparse/meson.build:
-	* gst/y4m/meson.build:
-	* meson.build:
-	* sys/directsound/meson.build:
-	* sys/osxaudio/meson.build:
-	* sys/osxvideo/meson.build:
-	* sys/rpicamsrc/meson.build:
-	* sys/v4l2/meson.build:
-	* sys/waveform/meson.build:
-	* sys/ximage/meson.build:
-	  meson: Call pkgconfig.generate in the loop where we declare plugins dependencies
-	  Removing some copy pasted code
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2970>
+2023-09-17 18:48:13 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-09-01 11:51:48 -0400  Thibault Saunier <tsaunier@igalia.com>
+	* gst/isomp4/gstrtpxqtdepay.c:
+	  rtpxqtdepay: Enabled header extension aggregation
+	  Because this depayloader may build several output buffers within one
+	  process run we push them all into a GstBufferList and push them out at
+	  once to make sure that each buffer gets notified about each header
+	  extension.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* docs/meson.build:
-	  meson: Namespace the plugins_doc_dep/libraries variables
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2970>
+2023-09-10 19:06:35 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-31 18:44:14 -0400  Thibault Saunier <tsaunier@igalia.com>
+	* gst/rtp/gstrtpmp4gdepay.c:
+	* gst/rtp/gstrtpmp4gdepay.h:
+	  rtpmp4gdepay: Enabled header extension aggregation
+	  Because this depayloader may build several output buffers within one
+	  process run we push them all into a GstBufferList and push them out at
+	  once to make sure that each buffer gets notified about each header
+	  extension.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* meson.build:
-	  meson: Rename plugins list and make them "dependency" objects
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2970>
+2023-09-10 19:06:10 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-18 17:08:51 +0300  Raul Tambre <raul@tambre.ee>
+	* gst/rtp/gstrtpsbcdepay.c:
+	  rtpsbcdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	* tests/check/elements/rtpjitterbuffer.c:
-	  rtpjitterbuffer: remove lost timer for out of order packets
-	  When receiving old packets remove the running lost timer if present.
-	  This fixes incorrect reporting of a lost packet even if it arrived in time.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2922>
+2023-09-10 19:05:11 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-26 18:42:44 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* gst/rtp/gstrtpvorbisdepay.c:
+	  rtpvorbisdepay: Enabled header extension aggregation
+	  Because this depayloader may build several output buffers within one
+	  process run we push them all into a GstBufferList and push them out at
+	  once to make sure that each buffer gets notified about each header
+	  extension.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* gst/rtp/gstrtpvp8depay.c:
-	  rtpvp8depay: If configured to wait for keyframes after packet loss, also do that if incomplete frames are detected
-	  This can happen if the data inside the packets is incomplete without the
-	  seqnums being discontinuous because of ULPFEC being used.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2947>
+2023-09-10 19:04:00 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-05-31 15:18:03 +0200  Stéphane Cerveau <scerveau@collabora.com>
+	* gst/rtp/gstrtpmp4vdepay.c:
+	  rtpmp4vdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* docs/meson.build:
-	* meson.build:
-	  docs: disable in static build
-	  Following gst-plugins-base, disable docs if static_build
-	  in:
-	  - gstreamer
-	  - gst-plugins-good
-	  - gst-plugins-ugly
-	  - gst-libav
-	  - gstreamer-vaapi
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2529>
-
-2022-08-24 17:30:34 +0800  Jianhui Dai <jianhui.j.dai@intel.com>
+2023-09-04 17:33:52 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-	* sys/v4l2/gstv4l2allocator.c:
-	  v4l2allocator: Fix invalid imported dmabuf fd
-	  Fix a typo that set userptr to dmabuf fd. It leads to failure of
-	  dmabuf-import io-mode.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2935>
+	* gst/rtp/gstrtptheoradepay.c:
+	  rtptheoradepay: Enabled header extension aggregation
+	  Because this depayloader may build several output buffers within one
+	  process run we push them all into a GstBufferList and push them out at
+	  once to make sure that each buffer gets notified about each header
+	  extension.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-2022-07-22 13:41:17 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+2023-09-04 17:33:34 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-	* gst/isomp4/qtdemux.c:
-	  playback: add onvif metadata caps to raw caps
-	  + remove encoding from x-onvif-metadata caps output by qtdemux
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2889>
+	* gst/rtp/gstrtpsv3vdepay.c:
+	  rtpsv3vdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-2022-08-23 10:28:30 +0000  zhiyuan.liu <zhiyuan.liu@seraphic-corp.com>
+2023-09-04 17:31:57 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-	* gst/isomp4/gstisoff.c:
-	  isoff: Fix earliest pts field parse issue
-	  earliest pts will be covered by first_offset field on version 0 case.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2927>
+	* gst/rtp/gstrtpmp4adepay.c:
+	  rtpmp4adepay: Enabled header extension aggregation
+	  Because this depayloader may build several output buffers within one process
+	  run we push them all into a GstBufferList and push them out at once to
+	  make sure that each buffer gets notified about each header extension.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-2022-08-18 16:36:45 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+2023-09-04 17:31:41 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-	* ext/adaptivedemux2/downloadhelper.c:
-	  adaptivedemux2: Fix download helper with libsoup 3.0.x
-	  libsoup 3.0.x dispatches using a single source attached when the session
-	  is created, so we need to create the session with the same context that
-	  our download thread is later using.
-	  2.74 or 3.1 will dispatch a response using the context which sent the
-	  request. However, for any context other than the one that created the
-	  session, this will also create and destroy sources, so there's still
-	  some slight performance benefit.
-	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1384
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2913>
-
-2022-08-18 09:48:15 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtp/gstrtpklvdepay.c:
+	  rtpklvdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Implement send_event() vfunc
-	  Handle select-streams and seek events in an element
-	  level send_event() vfunc, so they can be received
-	  before any source pads are created.
-	  This allows preferred streams to be selected before
-	  segment downloading starts.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2912>
+2023-09-04 17:31:20 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-17 09:11:52 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtp/gstrtpjpegdepay.c:
+	  rtpjpegdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* gst/multifile/gstsplitmuxsrc.c:
-	  splitmuxsrc: Stop pad task before cleanup
-	  When stopping the element, make sure the pad task
-	  is stopped before destroying the part readers.
-	  Closes a race where the pad task might access
-	  a freed pointer.
-	  Also add a guard against this sort of thing
-	  by holding a ref to the reader in the pad loop.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2901>
+2023-09-04 17:30:48 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-12 20:20:43 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtp/gstrtpj2kdepay.c:
+	  rtpj2kdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Avoid crash on reconfiguring.
-	  When reconfiguring a stream that never created
-	  an output pad, don't access a NULL GstPad pointer.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2869>
+2023-09-04 17:30:31 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-16 19:01:19 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* gst/rtp/gstrtph263pdepay.c:
+	  rtph263pdepay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Set parsed=true on ONVIF Timed Metadata caps
-	  Inside MP4 the metadata must be properly parsed into frames and in
-	  order.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2897>
+2023-09-04 17:29:32 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-15 14:30:50 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	* gst/rtp/gstrtph263depay.c:
+	  rtph263depay: Enabled header extensions aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* sys/v4l2/gstv4l2bufferpool.c:
-	  v4l2bufferpool: Fix debug trace
-	  The tracing of the buffer pointer was done on the secondary pointer, which
-	  does not match with other traces of the same buffer. This made the trace
-	  confusing and less useful.
-	  Fixes #1379
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2893>
+2023-09-04 17:28:46 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-08-12 21:57:25 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtp/gstrtph261depay.c:
+	  rtph261depay: Enabled header extension aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5378>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Fix for period switching in live streams
-	  When playing live, it's possible that one stream reaches
-	  the end of the available playback window and goes to sleep
-	  waiting for a manifest update, and the manifest update
-	  introduces a new period. In that case, the sleeping
-	  stream needs to wake up and go 'properly' EOS before we
-	  can advance the input to the new period.
-	  Accordingly, make sure that a stream's last_ret value
-	  is not marked as EOS if it's just sleeping waiting for a live
-	  manifest update.
-	  Also fix the output loop to go back and re-check if it's
-	  time to switch to the next period after dequeuing and
-	  discarding an EOS event.
-	  https://livesim.dashif.org/livesim/periods_20/testpic_2s/Manifest.mpd
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2895>
-
-2022-08-11 09:34:58 +0900  Hosang Lee <hosang10.lee@lge.com>
+2014-06-18 14:27:22 +0200  Priit Laes <plaes@plaes.org>
 
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	  mssdemux2: Use gsturi structure to form fragment urls
-	  Utilize gsturi to form fragment url paths.
-	  A token query may contain the string "manifest" and this would lead
-	  to improper url creations.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2859>
+	* docs/gst_plugins_cache.json:
+	* ext/libcaca/gstcacasink.c:
+	* ext/libcaca/gstcacasink.h:
+	  cacasink: add driver selection support from the pipeline
+	  https://bugzilla.gnome.org/show_bug.cgi?id=599018
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5491>
 
-2022-08-12 13:20:01 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-02-15 16:38:53 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/rtsp/gstrtspsrc.c:
-	  rtspsrc: Consider the actual control base URI also in case the connection URI contains a query string
-	  That is, get rid of unnecessary and wrong special-casing.
-	  This could always use gst_rtsp_url_get_request_uri_with_control() but as
-	  we only have the control base URI as string it is easier to just call
-	  gst_uri_join_strings().
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2868>
+	* meson.build:
+	  Back to development
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6126>
 
-2022-08-11 18:37:18 +0300  Sebastian Dröge <sebastian@centricular.com>
+=== release 1.23.2 ===
 
-	* gst/isomp4/fourcc.h:
-	* gst/isomp4/qtdemux.c:
-	* gst/isomp4/qtdemux.h:
-	* gst/isomp4/qtdemux_types.c:
-	  qtdemux: Add reference timestamp meta with UTC times based on the ONVIF Export File Format CorrectStartTime box to outgoing buffers
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2525>
+2024-02-15 15:37:17 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-08-11 05:17:05 +1000  Jan Schmidt <jan@centricular.com>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.23.2
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Fix reference leak of variant stream
-	  When switching back to the previous variant stream
-	  in gst_hls_demux_change_playlist(), fix a couple of
-	  paths that would leak a reference to the previous
-	  variant.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2873>
+2024-02-14 15:49:43 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-08-11 05:07:10 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtp/dboolhuff.LICENSE:
+	* gst/rtp/dboolhuff.c:
+	* gst/rtp/dboolhuff.h:
+	* gst/rtp/gstrtpvp8pay.c:
+	* gst/rtp/meson.build:
+	  Revert "rtpvp8pay: Use GstBitReader instead of dboolhuff implementation from libvpx"
+	  This reverts commit b730e7a1b28ce4bcea90fbff7c5293fa2c0a76b2.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3300
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6116>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Don't leak the datetime in time map structs
-	  Add a function to clean up GstHLSTimeMap structs
-	  and free the ref on the optional associated GDateTime
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2873>
+2024-02-13 18:57:05 +0100  Mathieu Duponchelle <mathieu@centricular.com>
 
-2022-08-11 05:03:10 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtpmanager/gstrtpbin.c:
+	  webrtcbin, rtpbin: check before setting properties on jitterbuffer
+	  In rtpbin we already systematically check for all property names
+	  except latency, correct that.
+	  In webrtcbin we need to check before trying to use the do-retransmission
+	  property.
+	  This is useful for the case where an element like identity gets passed
+	  to rtpbin's request-jitterbuffer property, when the application wants
+	  to use webrtcbin in an SFU situation, with no reordering and no added
+	  latency
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6112>
+
+2024-02-13 17:43:15 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Fix typefind leak and invalid memory access
-	  When typefinding aggregates incoming data to a pending
-	  typefind buffer and then succeeds in typefinding, it
-	  leaks the aggregated buffer, and leaves the caller
-	  accessing an unreffed buffer.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2873>
+	* gst/rtpmanager/gstrtpfunnel.c:
+	  rtpfunnel: Handle NTP-64 RTP header extension in caps similar to TWCC
+	  This is another header extension that is handled by rtpsession and needs
+	  to be preserved in the caps that are created by rtpfunnel.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6109>
 
-2022-08-11 05:36:15 +1000  Jan Schmidt <jan@centricular.com>
+2024-02-14 00:37:38 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: m3u8: Fix memory leaks on parsing
-	  Fix memory leaks when parsing of an m3u8 file is
-	  incomplete, with EXTINF or EXT-X-PROGRAM-DATE-TIME
-	  directives, but no segment url.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2873>
+	* po/lv.po:
+	* po/ro.po:
+	* po/tr.po:
+	  gst-plugins-good: update translations
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6113>
 
-2022-08-11 00:59:50 +1000  Jan Schmidt <jan@centricular.com>
+2024-02-13 18:07:52 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: m3u8: Fix memory leak
-	  Clear the GValue holding intermediate GstStructure field
-	  data.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2873>
+	* gst/rtpmanager/gstrtpfunnel.c:
+	  rtpfunnel: Also write TWCC RTP header extension into buffer list buffers
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6110>
 
-2022-08-11 01:01:07 +1000  Jan Schmidt <jan@centricular.com>
+2024-02-03 11:54:41 +0000  Philippe Normand <philn@igalia.com>
 
-	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
-	  adaptivedemux2: Fix uninitialised memory usage in debug
-	  Fix printing uninitialised memory by clearing the
-	  GstAdaptiveDemuxClock structure when allocating.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2873>
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	  dashdemux2: Basic support for container-specific-track-id tag
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6041>
 
-2022-08-12 02:20:40 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2024-02-03 11:53:36 +0000  Philippe Normand <philn@igalia.com>
 
-	* gst/rtpmanager/gstrtpst2022-1-fecenc.c:
-	  rtpst2022-1-fecenc: Drain column packets on EOS
-	  Otherwise we won't send the protection packets for the last few
-	  packets when a stream ends.
-	  Also send EOS on the FEC src row pad immediately, and on the FEC src
-	  column pad after draining is complete. This makes it so that the FEC
-	  src pads on rtpbin behave the same way as the RTCP src pads on rtpbin
-	  when EOS is received on the send_rtp_sink pad.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2863>
+	* gst/matroska/matroska-demux.c:
+	  matroska-demux: Basic support for container-specific-track-id tag
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6041>
 
-2022-08-11 08:48:08 +0200  Edward Hervey <edward@centricular.com>
+2024-02-03 11:52:42 +0000  Philippe Normand <philn@igalia.com>
 
 	* gst/isomp4/qtdemux.c:
-	  qtdemux: Don't use invalid values from failed trex parsing
-	  If parsing the fragment default values (`trex` atom) failed, don't try to
-	  compute a bogus sample_description_id value.
-	  Fixes #1369
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2860>
+	  qtdemux: Basic support for container-specific-track-id tag
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6041>
 
-2022-08-09 09:42:23 +0200  Piotr Brzeziński <piotr@centricular.com>
-
-	* docs/gst_plugins_cache.json:
-	* gst/videofilter/gstvideoflip.c:
-	  videoflip: Add support for 10/12bit planar formats
-	  Implements support for I420, I422 and Y444 in 10/12 bit LE/BE variants.
-	  I422 is handled separately from the rest, as it needs to consider
-	  the endianness of the current format during most transforms.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2788>
+2024-02-09 11:13:16 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-2022-08-04 18:09:52 +0800  Haihua Hu <jared.hu@nxp.com>
+	* ext/qt6/meson.build:
+	  meson: Don't use fs.copyfile() for qt6 resources
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3285
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6077>
 
-	* gst/alpha/gstalpha.c:
-	  alpha: fix stride issue when out buffer has padding on right
-	  if outbuf has padding on right, need jump to next line use stride,
-	  otherwise downstream element will show a wrong picture when use the
-	  same stride
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2842>
+2023-12-01 14:49:37 +0100  Ignazio Pillai <ignazp@amazon.com>
 
-2022-08-09 00:12:58 +1000  Jan Schmidt <jan@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/cutter/gstcutter.c:
+	* gst/cutter/gstcutter.h:
+	  cutter: add audio-level-meta
+	  Set GstAudioLevelMeta on buffers
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5771>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Requeue header buffer when restarting fragment
-	  When returning GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT
-	  for the first segment data, we might need to requeue the
-	  header.
-	  This was leading to occasional prerolling stalls on
-	  HLS live streams with renditions.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2849>
+2024-02-05 23:04:45 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-2022-08-07 20:56:49 +1000  Jan Schmidt <jan@centricular.com>
+	* ext/qt6/meson.build:
+	  meson: Fix several warnings in the build
+	  Deprecations, incorrect options, etc.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6058>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Fix buffer leak when resynching
-	  Unref the buffer in gst_hls_demux_handle_buffer() when
-	  returning GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2849>
+2024-02-05 22:39:29 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-2022-08-04 23:54:27 +1000  Jan Schmidt <jan@centricular.com>
+	* tests/check/meson.build:
+	  good/tests: Don't enable soup tests if soup is disabled
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3268
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6058>
 
-	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
-	  adaptivedemux2: Fix a small race on shutdown
-	  Make sure gst_adaptive_demux_loop_cancel_call()
-	  never tries to operate on an invalidated main context. Make
-	  sure to clear the main context pointer while holding the lock,
-	  and to check it in gst_adaptive_demux_loop_cancel_call()
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2847>
+2024-02-06 18:09:02 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-07-22 03:32:39 +1000  Jan Schmidt <jan@centricular.com>
+	* meson.build:
+	  Back to development
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6066>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	  adaptivedemux2-stream: Silence a compiler warning
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+=== release 1.23.1 ===
 
-2022-07-28 04:17:26 +1000  Jan Schmidt <jan@centricular.com>
+2024-02-06 16:37:19 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	  adaptivedemux2: Move internal FLOW_SWITCH return value.
-	  Move the internal-only FLOW_SWITCH custom return value
-	  to GST_FLOW_CUSTOM_SUCCESS+2 to avoid collision with
-	  GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+	* NEWS:
+	* RELEASE:
+	* gst-plugins-good.doap:
+	* meson.build:
+	  Release 1.23.1
 
-2022-07-20 10:57:41 +0200  Edward Hervey <edward@centricular.com>
+2024-02-05 09:27:54 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: Modify custom sync loss flow return
-	  Make it a custom sucess and not an error
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+	* meson_options.txt:
+	  meson_options.txt: fix meson warning about default bool values being a string
 
-2022-07-12 10:44:51 +0200  Edward Hervey <edward@centricular.com>
+2024-02-02 23:04:29 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: Always check DSN if required
-	  We don't want to consider the candidate as being before the playlist if the DSN
-	  don't match
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+	* ext/qt/meson.build:
+	* ext/qt6/meson.build:
+	  meson: Print a useful error message when qt windowing is not found
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6040>
 
-2022-07-11 10:31:42 +0200  Edward Hervey <edward@centricular.com>
+2023-08-24 15:18:05 +0900  Hou Qi <qi.hou@nxp.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  adaptivedemux2/hlsdemux2: Handle loss of sync when dowloading.
-	  Media playlist updates and fragment downloads happen in an interleaved
-	  fashion. When a media playlist update fails *while* a segment is being
-	  downloaded, this means we lost synchronization.
-	  Properly propagate and handle this
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2/m3u8: use GstClockTimeDiff to do timestamp comparison
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5234>
 
-2022-07-08 10:48:05 +0200  Edward Hervey <edward@centricular.com>
+2023-12-13 20:43:14 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Fix initial playlist setup.
-	  There is now only a single case where we setup the initial playlist to 0, which
-	  is for the very first variant stream.
-	  Rendition streams will have the initial playlist "synchronized" against the
-	  variant stream media playlist.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+	* gst/rtp/dboolhuff.LICENSE:
+	* gst/rtp/dboolhuff.c:
+	* gst/rtp/dboolhuff.h:
+	* gst/rtp/gstrtpvp8pay.c:
+	* gst/rtp/meson.build:
+	  rtpvp8pay: Use GstBitReader instead of dboolhuff implementation from libvpx
+	  All compressed frame header values that are read as part of the
+	  payloader are encoded as bits with 50:50 probability, and as such are
+	  just the plain bits as they are.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5810>
 
-2022-07-08 10:44:51 +0200  Edward Hervey <edward@centricular.com>
+2023-08-22 16:11:06 -0400  Daniel Morin <daniel.morin@collabora.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Handle loss of synchronization in live
-	  Loss of synchronization happens when the updated media playlist has no
-	  relationship to the previous ones. This could happen because of network issues,
-	  server issues, etc...
-	  When this happens, we take no chance and "reset" ourselves so that we can "seek
-	  back to live" against the new updated playlists.
-	  Since this happens at the "media playlist update" level, make sure the custom
-	  flow return is propagated up.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
-
-2022-07-08 10:40:33 +0200  Edward Hervey <edward@centricular.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: update rtsp url on redirect
+	  - If a redirect took place on a GET when rtsp is tunneled we update the
+	  rtsp url too.
+	  - log source and final destination on redirect
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5222>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Handle synchronously to lost sync
-	  We are already in the main scheduler thread, therefore we can do the "seek back
-	  to live" directly. This also avoids other pending actions to take place.
-	  Also handle the loss of sync when doing manifest updates.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+2024-01-29 16:10:21 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-2022-07-06 11:44:57 +0200  Edward Hervey <edward@centricular.com>
+	* gst/matroska/matroska-read-common.c:
+	  matroskademux: Lower verbosity of some often happenning warnings
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6011>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Prune time maps when possible
-	  Add a new method to prune unused time mappings (i.e. which aren't used by any
-	  current media playlist).
-	  Do that when doing flushing seeks. Could be used in other places later too.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+2024-01-29 16:10:06 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-2022-06-13 15:26:22 +0200  Edward Hervey <edward@centricular.com>
+	* gst/isomp4/qtdemux.c:
+	* gst/isomp4/qtdemux_types.c:
+	  qtdemux: Lower verbosity of some often happenning warnings
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6011>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: Allow DSN mismatches when re-syncing playlists
-	  Some providers provide completely incompatible DSN across bitrates/renditions,
-	  but do keep MSN consistent.
-	  If we fail to synchronize playlist with DSN, retry without the DSN taken into
-	  account.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2839>
+2024-01-28 11:15:01 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-2022-07-12 01:58:30 +1000  Jan Schmidt <jan@centricular.com>
+	* meson.build:
+	  meson: bump Meson requirement to >= 1.1 for all modules
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6002>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Fixes for period switching in the output loop
-	  Close some race conditions in switching to the next period,
-	  by ensuring the tracks are completely drained first and by
-	  not outputting EOS events to the output source pad
-	  if there is another period pending.
-	  Fixes Manifest_MultiPeriod_1080p.mpd some more.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2838>
+2024-01-25 20:13:51 +0100  Jonas Kvinge <jonas@jkvinge.net>
 
-2022-07-12 01:55:54 +1000  Jan Schmidt <jan@centricular.com>
+	* ext/taglib/meson.build:
+	  meson: Set cpp_std to c++17 for TagLib
+	  TagLib uses C++17 as of version 2.0.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5995>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	  adaptivedemux2: stream: Set period has_next_period flag before EOS
-	  Before sending EOS, update the period's has_next_period
-	  flag and/or create the next period. This closes a race
-	  where the output loop might receive the EOS event
-	  and either push it downstream (causing premature EOS),
-	  or receive it and try and switch to the next period
-	  before that period is completely set up.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2838>
+2024-01-02 16:18:05 +0100  Mathieu Duponchelle <mathieu@centricular.com>
 
-2022-07-12 01:24:31 +1000  Jan Schmidt <jan@centricular.com>
+	* ext/vpx/gstvpxenc.c:
+	  vpxenc: fix warning about decreasing PTS on first frame
+	  The fields used to track this state should be initialized when
+	  codec->inited is FALSE on set_format, not TRUE
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3200
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5876>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: period: Rename 'closed' flag to 'has_next_period'
-	  The flag is used to tell the output loop that a
-	  next period is present, since the output loop
-	  can't call the gst_adaptive_demux_has_next_period()
-	  method.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2838>
+2024-01-17 15:59:15 +0100  Jonas K Danielsson <jonas.danielsson@spiideo.com>
 
-2022-07-01 02:07:05 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/multifile/gstsplitutils.c:
+	  splitmuxsrc: Use natural ordering to find files
+	  Today when using the `splitmuxsrc` on a collection of files named as:
+	  ```
+	  item0.mkv
+	  item1.mkv
+	  item2.mkv
+	  [...]
+	  item10.mkv
+	  item11.mkv
+	  [...]
+	  ```
+	  You will get a continuous stream made in the order of:
+	  ```
+	  item0.mkv -> item1.mkv -> item10.mkv -> item11.mkv -> [...]
+	  ```
+	  You can fix this by having smarter names of the items:
+	  ```
+	  item000.mkv
+	  item001.mkv
+	  item002.mkv
+	  [...]
+	  item010.mkv
+	  item011.mkv
+	  [...]
+	  ```
+	  Will get you:
+	  ```
+	  item000.mkv -> item001.mkv -> item003.mkv -> item004.mkv -> [...]
+	  ```
+	  But, we could also "fix" the former case by using natural ordering when
+	  comparing the files in gstsplitutils.c.
+	  Fixes #2523
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4491>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Recheck for a pending track on drain
-	  When a track is completely drained and EOS, but
-	  there's a pending track on the slot loop again
-	  to switch to that track.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2838>
+2024-01-22 17:01:19 -0500  Dan Searles <Dan.Searles@garmin.com>
 
-2022-07-01 02:05:36 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: fix ttl setting for udpsink[1]
+	  Fix ttl setting being incorrectly applied to udpsink[0] rather
+	  than to udpsink[1].
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5955>
 
-	* ext/adaptivedemux2/gstadaptivedemux-period.c:
-	  adaptivedemux2: Check stream selected instead of state
-	  When combining stream flows, ignore streams that
-	  are not selected, instead of checking whether
-	  the stream state has changed yet.
-	  Fixes another issue with dashdemux2 where it fails to
-	  change to the next period when playing content with
-	  several video, audio and text streams, as with
-	  Manifest_MultiPeriod_1080p.mpd when seeking to 730
-	  just before the end of the first period.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2838>
-
-2022-05-25 18:40:30 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2024-01-22 12:18:25 -0500  Dan Searles <Dan.Searles@garmin.com>
 
 	* gst/rtsp/gstrtspsrc.c:
-	  rtsp+rtmp: Forward warning added to tls-validation-flags to our users
-	  With the 2.72 release, glib-networking developers have decided that
-	  TLS certificate validation cannot be implemented correctly by them, so
-	  they've deprecated it.
-	  In a nutshell: a cert can have several validation errors, but there
-	  are no guarantees that the TLS backend will return all those errors,
-	  and things are made even more complicated by the fact that the list of
-	  errors might refer to certs that are added for backwards-compat and
-	  won't actually be used by the TLS library.
-	  Our best option is to ignore the deprecation and pass the warning onto
-	  users so they can make an appropriate security decision regarding
-	  this.
-	  We can't deprecate the tls-validation-flags property because it is
-	  very useful when connecting to RTSP cameras that will never get
-	  updates to fix certificate errors.
-	  Relevant upstream merge requests / issues:
-	  https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2214
-	  https://gitlab.gnome.org/GNOME/glib-networking/-/issues/179
-	  https://gitlab.gnome.org/GNOME/glib-networking/-/merge_requests/193
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2494>
-
-2022-07-22 14:51:11 +0200  Mark Nauwelaerts <mnauw@users.sourceforge.net>
-
-	* gst/videobox/gstvideobox.c:
-	  videobox: avoid dropping caps fields for passthrough caps transform
-	  Fixes potential negotiation failure in case downstream element
-	  is a bit picky regarding the fields in question.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2786>
-
-2022-07-27 15:44:09 +0200  Adrian Fiergolski <adrian.fiergolski@fastree3d.com>
-
-	* gst/videofilter/gstvideoflip.c:
-	  videoflip: Fix caps negotiation when method is selected
-	  The caps negotiation should respect the selected method to the test pipeline below works properly.
-	  gst-launch-1.0 videotestsrc ! video/x-raw,width=320,height=600 ! videoflip method=clockwise ! video/x-raw,width=600,height=320 ! fakesink
-	  Signed-off-by: Adrian Fiergolski <adrian.fiergolski@fastree3d.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2803>
+	  rtspsrc: set multicast-iface on udpsinks
+	  Copy rtspsrc property multicast-iface to its udpsinks to
+	  allow messages over those sinks back to the server to work (and
+	  prevent 'Network unreachable' warnings).
+	  Closes: #3239
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5955>
 
-2022-07-24 23:44:10 -0400  fduncanh <fduncanh@gmail.com>
+2024-01-18 16:54:22 +0100  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2object.c: add support for Apple's full-range bt709 colorspace variant
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2793>
+	* gst/flv/gstflvdemux.c:
+	  flvdemux: don't re-use segment from one stream if the other has buffer earlier
+	  Fix first audio buffers being out of segment because the audio stream
+	  is starting earlier than the video one which was the first demuxed.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5940>
 
-2022-07-08 18:22:58 +0800  Elliot Chen <elliot.chen@nxp.com>
+2024-01-18 16:50:20 +0100  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-	* sys/v4l2/gstv4l2bufferpool.c:
-	  v4l2: fix the critical log when unreference the buffer with no data
-	  In the trick mode, driver may queue a valid buffer follow by an
-	  empty buffer which has no valid data to indicate EOS.For the empty
-	  buffer whose memory is multi-plane, need to resize it before
-	  unreference it.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2731>
+	* gst/flv/gstflvdemux.c:
+	  flvdemux: factor out ensure_new_segment()
+	  - Use the pad instead of the element for logs, so it's clearer on which
+	  pad this segment will be pushed.
+	  - One copy was checking for invalid seq num, the other was not.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5940>
 
-2022-07-15 13:22:14 +0300  Sebastian Dröge <sebastian@centricular.com>
+2024-01-11 14:29:49 +0900  Hou Qi <qi.hou@nxp.com>
 
-	* ext/qt/qtglrenderer.cc:
-	  qt: Fix another instance of Qt/GStreamer both defining `GLsync` differently
-	  In file included from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/gstreamer-1.0/gst/gl/gstglfuncs.h:87,
-	  from ../gst-plugins-good-1.20.3/ext/qt/qtglrenderer.cc:14:
-	  ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/gstreamer-1.0/gst/gl/glprototypes/gstgl_compat.h:40:18: error: conflicting declaration 'typedef void* GLsync'
-	  40 | typedef gpointer GLsync;
-	  |                  ^~~~~~
-	  In file included from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtGui/qopengl.h:127,
-	  from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtQuick/qsggeometry.h:44,
-	  from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtQuick/qsgnode.h:43,
-	  from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtQuick/qsgrendererinterface.h:43,
-	  from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtQuick/qquickwindow.h:44,
-	  from ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtQuick/QQuickWindow:1,
-	  from ../gst-plugins-good-1.20.3/ext/qt/qtglrenderer.cc:6:
-	  ../gstreamer1.0-plugins-good/1.20.3-r0/recipe-sysroot/usr/include/QtGui/qopengles2ext.h:24:26: note: previous declaration as 'typedef struct __GLsync* GLsync'
-	  24 | typedef struct __GLsync *GLsync;
-	  |                          ^~~~~~
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2763>
-
-2022-07-15 06:40:05 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	  rtpjitterbuffer: Fix build warning in rtp_jitter_buffer_append_query()
+	  This is to fix build warnings when using [-Wmaybe-uninitialized]
+	  ../gst/rtpmanager/rtpjitterbuffer.c:1237:10: warning: 'head' may be used uninitialized [-Wmaybe-uninitialized]
+	  1237 |   return head;
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5907>
 
-	* sys/osxaudio/gstosxaudiodeviceprovider.c:
-	* sys/osxaudio/gstosxcoreaudiohal.c:
-	  osxaudio: Fix deprecation in macOS 12.0
-	  kAudioObjectPropertyElementMaster has been renamed to
-	  kAudioObjectPropertyElementMain
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2761>
+2024-01-12 12:51:27 +0000  Philippe Normand <philn@igalia.com>
 
-2022-07-12 21:19:35 +1000  Jan Schmidt <jan@centricular.com>
+	* ext/vpx/gstvpxdec.c:
+	  vpxdec: Use appropriate domain and code for decoding errors
+	  STREAM domain and DECODE error is commonly used in other decoders. ENCODE is for
+	  encoders.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5916>
 
-	* gst/multifile/gstsplitmuxsink.c:
-	  splitmuxsink: Fix memory leak
-	  Fix a leak of the buffer info struct when reaching
-	  EOS without data on the reference input.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2751>
+2024-01-10 19:10:55 -0500  Olivier Crête <olivier.crete@collabora.com>
 
-2022-06-24 13:32:34 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/soup/gstsouploader.c:
+	  soup: Avoid using GUri before GLib 2.66
+	  Let's use gpointer for now
+	  Fixes: #3169
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5906>
 
-	* gst/rtpmanager/rtpjitterbuffer.c:
-	  rtpjitterbuffer: Fix calculation of RFC7273 RTP time period start
-	  This has to be based directly on the current estimated clock time and
-	  has to allow for negative period starts.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2655>
+2024-01-08 11:04:22 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-04-14 01:19:51 +0900  Seungha Yang <seungha@centricular.com>
+	* gst/rtpmanager/rtpsession.c:
+	* gst/rtpmanager/rtpsession.h:
+	  rtpsession: Remove some unused fields
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5899>
 
-	* gst/multifile/gstsplitmuxsink.c:
-	* tests/check/elements/splitmuxsink.c:
-	  splitmuxsink: Don't crash on EOS without buffer
-	  Fix a case where upstream pushed EOS without buffers.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2174>
+2023-11-16 11:42:27 +0530  Sanchayan Maity <sanchayan@asymptotic.io>
 
-2022-06-30 15:15:22 +0000  Thibault Saunier <tsaunier@igalia.com>
+	* gst/rtpmanager/gstrtphdrext-clientaudiolevel.c:
+	  rtphdrext-clientaudiolevel: Fix level value being written by the extension
+	  When level value is greater than 127, it was being clamped but this clamped
+	  value was not the one being actually used. For level values greater than 127
+	  this resulted in an incorrect value being used. As an example, a level value
+	  of 187, after and'ed with 0x7F, it would result in 0x3B being reported as the
+	  level value.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5893>
 
-	* gst/rtpmanager/gstrtprtxreceive.c:
-	* gst/rtpmanager/gstrtprtxsend.c:
-	* tests/check/elements/rtprtx.c:
-	  rtprtx: Fix copying extension headers
-	  There was a typo leading to reading memory from the buffer we were
-	  writing to.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2696>
+2023-12-23 11:03:51 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-05-28 15:04:10 -0400  fduncanh <fduncanh@gmail.com>
+	* docs/gst_plugins_cache.json:
+	* ext/vpx/plugin.c:
+	  vpx: fix plugin description
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5864>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: replace multiple decoder bug warnings with single one
-	  Achieve this by dropping frames after a drain if the driver failed to so.
-	  This works around RaspberryPi driver issue [1].
-	  [1] https://github.com/raspberrypi/linux/issues/5059#issuecomment-
-	  Fixes #1103
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2518>
+2023-12-22 12:56:33 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-06-25 19:23:11 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* gst/rtpmanager/gstrtpsession.c:
+	  rtpsession: Only warn once if configured latency needs to be known but isn't yet
+	  Otherwise we would warn about this once for every single packet until
+	  the LATENCY event is received.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5854>
 
-	* tests/check/elements/udpsink.c:
-	  tests: udpsink: make test work in environments without IPv6
-	  Part-fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/939
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2659>
+2023-12-25 18:36:44 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-05-28 16:47:42 -0400  fduncanh <fduncanh@gmail.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtpvrawdepay.c:
+	  rtpvrawdepay: only announce supported formats in sink template
+	  For most video formats we currently just assume that they
+	  have a depth of 8 bits, whilst advertising that we can
+	  handle 8/10/12/16 bit depth.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5866>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: replace multiple decoder bug warnings by a single warning (warning due to incorrectly dropped frames at initial caps adjustment)
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2517>
+2023-12-15 21:52:03 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-06-29 00:51:28 +1000  Jan Schmidt <jan@centricular.com>
+	* gst/rtp/gstrtpvp8pay.c:
+	  rtpvp8pay: Also set partition IDs in the packets if meta exists but without temporal_scalability
+	  Encoders will add the meta to every single buffer, but we only cannot set
+	  partition IDs properly when the meta has temporal_scalability set
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5814>
 
-	* ext/adaptivedemux2/gstadaptivedemux-period.c:
-	  adaptivedemux2: Ignore stopped stream flow state
-	  When calculating the combined stream flow state
-	  for a period, don't consider stopped streams.
-	  Fixes switching to the next period in DASH streams
-	  with multiple video/audio/subtitle streams.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2684>
+2023-12-19 13:57:04 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-06-29 01:25:41 +1000  Jan Schmidt <jan@centricular.com>
+	* docs/gst_plugins_cache.json:
+	  video-format: Fix format order once again
+	  RGBA should be before RBGA. Both the Python script and the gstreamer-rs
+	  tests agree on that, but somehow this is not caught by the CI.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5837>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	  adaptivedemux2: Clean up stream parsebins on finalize
-	  Remove the parsebin for a stream from the overall
-	  bin when cleaning up the stream, to avoid
-	  keeping around old ones when moving between periods
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2676>
+2023-12-18 11:03:57 +0900  Chao Guo <chao.guo@nxp.com>
 
-2022-06-24 02:54:22 +1000  Jan Schmidt <jan@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: clear old fds in poll when closing v4l2object
+	  When reopening a v4l2 device, the v4l2object->poll will include some old fds,
+	  which was assigned to this device before. If the pipeline opens multiple v4l2
+	  devices, the old fd may been assigned to other v4l2 devices when reopening
+	  devices.
+	  This will cause the timing of the pipeline become confusing when polling devices,
+	  leading functional abnormalities.
+	  Therefore, when closing v4l2object, remove the old fds in poll to ensure that the
+	  pipeline timing is normal.
+	  Signed-off-by: Chao Guo <chao.guo@nxp.com>
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5820>
+
+2023-12-15 15:19:35 -0500  Arun Raghavan <arun@asymptotic.io>
+
+	* gst/rtp/gstrtpchannels.c:
+	  rtp: Fix incorrect RTP channel order lookup by name
+	  The g_ascii_strcasecmp() logic is inverted, since it returns 0 on equality.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5815>
+
+2023-12-11 10:49:39 +0100  Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
-	  adaptivedemux2: Fix memory leaks and use-after-free
-	  Fix various small memory leaks, and an invalid
-	  access to GstEvent after giving away the ref
-	  via gst_pad_push_event()
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2676>
+	* docs/gst_plugins_cache.json:
+	* ext/gtk/gstgtkglsink.c:
+	  gtkglsink: template caps to only 2D & rectangle texture targets
+	  Apparently external-oes is not supported by the plugin as texture target,
+	  while DMABuf uploading prefers it because it's zero copy.
+	  This patch enables DMABuf uploading and rendering by using either 2D or
+	  rectangle texture targets.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5795>
 
-2022-06-24 02:57:54 +1000  Jan Schmidt <jan@centricular.com>
+2023-12-06 11:24:35 -0600  Olivier Crête <olivier.crete@collabora.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
-	  hlsdemux2: Fix potential segfault
-	  Fix a potential segfault if we receive a ISO-FF stream
-	  with moof before moov.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2676>
+	* ext/adaptivedemux2/downloadhelper.c:
+	* ext/soup/gstsouploader.c:
+	* ext/soup/gstsouploader.h:
+	* ext/soup/stub/soup.h:
+	  adaptivedemux2: Parse cookies in downloadhelper
+	  We need to parse any cookie headers, otherwise we end up
+	  sending back attributes likes "Secure" and "httponly" which break
+	  some servers.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5776>
 
-2022-06-24 02:57:03 +1000  Jan Schmidt <jan@centricular.com>
+2023-12-09 13:15:38 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: Fix memory leaks
-	  Clean up various memory leaks
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2676>
+	* gst/rtp/gstrtpvp9pay.c:
+	  rtpvp9pay: Don't include unused dboolhuff.h header
+	  It's only used by the VP8 payloader.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5784>
 
-2022-06-23 19:24:03 +1000  Jan Schmidt <jan@centricular.com>
+2023-12-06 20:42:32 -0800  Xavier Claessens <xavier.claessens@collabora.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Free current_segment on finalize
-	  Avoid a memory leak by making sure to release the
-	  current segment on exit if non-NULL
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2676>
+	* sys/v4l2/gstv4l2src.c:
+	  v4l2src: Consider framerate during caps selection
+	  This simplifies the way it picks the closest caps to preference and take into
+	  consideration the framerate to avoid picking high resolution at 5fps or so.
+	  Simply calculate a "distance" of caps A and B from the preference and put
+	  closest first, sorting by framerate first.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5777>
 
-2022-06-29 10:55:13 +0100  Tim-Philipp Müller <tim@centricular.com>
+2023-12-05 09:25:22 +0100  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
+	* gst/isomp4/qtdemux_tags.c:
 	* meson.build:
-	  coding style: allow declarations after statement
-	  See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1243/
-	  and https://gitlab.freedesktop.org/gstreamer/gstreamer-project/-/issues/78
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2683>
+	  qtdemux: fix bug report URL
+	  Using PACKAGE_BUGREPORT as in other modules.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5762>
 
-2022-06-22 03:35:03 +1000  Jan Schmidt <jan@centricular.com>
+2023-11-28 11:43:53 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-track.c:
-	  adaptivedemux2: track: Fix buffering time calc before output
-	  Use the lowest track input time as the output
-	  time when calculating track buffering levels
-	  before anything has been dequeued.
-	  Fixes multi-period DASH not advancing to the
-	  next period in some cases.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2650>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Do not update `demux->offset` when droping data on EOS
+	  The offset is updated right after and we were breaking it by updating it
+	  twice.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5724>
 
-2022-06-22 03:34:19 +1000  Jan Schmidt <jan@centricular.com>
+2023-11-28 11:28:58 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-period.c:
-	* ext/adaptivedemux2/gstadaptivedemux-track.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: track: Add period number to most debug statements
-	  Store the period number the track belongs to, and
-	  add it in various debug statements
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2650>
-
-2022-06-22 03:30:16 +1000  Jan Schmidt <jan@centricular.com>
-
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	  adaptivedemux2: stream: Don't take TRACKS_LOCK when sending EOS event
-	  The stream tracks list can't change while we're
-	  iterating it from the scheduling thread,
-	  and the event handler immediately takes the
-	  tracks lock, causing a deadlock.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2650>
-
-2022-06-08 11:17:09 +0200  Edward Hervey <edward@centricular.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Do not mark stream as EOS only if all streams are EOS
+	  The `GstFlowCombiner` is responsible for tracking the flow of each
+	  stream and handle the overal flow return value. Without that, we
+	  can end up with the following scenario:
+	  - Audio+video stream
+	  - Only the video stream is linked downstream
+	  - The audio stream goes EOS, video doesn't yet
+	  -> We update the Flow in the combiner with OK as all streams are not EOS
+	  - Video goes EOS because downstream returned EOS
+	  -> `qtdemux` returns `FLOW_OK` forever because the unlinked audio pad
+	  has `last_flowret==FLOW_OK`
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5724>
+
+2023-11-28 11:27:45 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Improve media playlist updates
-	  * When dealing with rendition streams, we attempt to synchronize the media
-	  playlist against the variant stream. This helps with speeding up the correct
-	  initial fragment search and avoids issues when streams at activated at a much
-	  later time.
-	  * Also add checks for variant stream existence before attempting to use them
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Determine EOS based on the stream segment
+	  Depending on the stream segment might vary (because of edts for example)
+	  leading to EOS being sent at the wrong time (too early for example).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5724>
 
-2022-06-08 11:16:15 +0200  Edward Hervey <edward@centricular.com>
+2023-12-01 14:51:49 +0900  Hosang Lee <hosang10.lee@lge.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Only seek on selected streams
-	  When handling seeks, there is no need to seek on unselected streams. If they
-	  later get activated they will be properly seek onto
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Don't overflow sample index
+	  Don't reduce sample index if it is already at 0.
+	  Assigning -1 to a guint32 variable causes unexpected behavior.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5743>
 
-2022-06-08 09:33:22 +0200  Edward Hervey <edward@centricular.com>
+2023-12-01 15:05:41 +0900  Hosang Lee <hosang10.lee@lge.com>
 
-	* tests/check/elements/hlsdemux_m3u8.c:
-	  tests: check: Update hlsdemux2 tests for playlist changes
-	  We no longer do auto-magic fallbacks when synchronizing a disconnected
-	  playlist. It is handled at a higher level.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix reverse playback for pcm audio stream
+	  Some raw lpcm or adpcm may have larger sample sizes than the max
+	  buffer size value set.
+	  Trimming the buffer causes bogus size error on reverse playback.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5742>
 
-2022-06-03 10:25:34 +0200  Edward Hervey <edward@centricular.com>
+2023-11-22 00:07:57 +0900  Seungha Yang <seungha@centricular.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: Fix debug return statement
-	  Due to latest commits res could have been NULL.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* docs/gst_plugins_cache.json:
+	  video: Add RBGA format
+	  This new format is intended to be used by hardware decoders
+	  where VUYA is only supported 4:4:4 decoding surface but
+	  stream is encoded with GBR color space, HEVC and VP9 GBR streams
+	  for example.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5703>
 
-2022-06-02 09:46:22 +0200  Edward Hervey <edward@centricular.com>
+2023-11-24 15:43:15 +0000  Philippe Normand <philn@igalia.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: No longer re-add segments from before the playlist
-	  When updating playlists, there is a possibility that the playlists don't
-	  perfectly align, but the last entry of the previous playlist is *just* before
-	  the first entry of the new playlist.
-	  In those cases, we still can transfer the timing information from one playlist
-	  to another, but we do not want to return that segment as being the matching one.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	  dashdemux2: Fix a couple leaks and a use-after-move
+	  The tags and caps were leaked for unknown streams, I'm not sure they'd be valid
+	  in that case, but better safe than sorry.
+	  The tags ownership is transfered when calling `gst_adaptive_demux_track_new()`
+	  so unreffing those afterwards was a mistake.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5714>
 
-2022-06-01 15:45:23 +0200  Edward Hervey <edward@centricular.com>
+2023-06-07 14:38:18 +0200  Robin Gustavsson <robin.gustavsson@spiideo.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: Use variant stream as support for synchronizing playlists
-	  When matching playlists, there is a possibility that rendition streams will not
-	  have been updated in time (for example because that stream started later, or
-	  playback was paused). This would cause several playback failures and seeking
-	  failures.
-	  In order to still fall back on our feet, attempt to synchronize that rendition
-	  playlist against the current variant playlist. This will attempt to match the
-	  stream time using SN/DNS/PDT/...
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
-
-2022-05-23 15:51:23 +0200  Edward Hervey <edward@centricular.com>
+	* gst/rtp/gstrtpklvdepay.c:
+	* tests/check/elements/rtp-payloading.c:
+	  rtpklvdepay: Recover after invalid fragmented KLV unit
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4816>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/hls/m3u8.h:
-	  hlsdemux2: Detect synchronization loss
-	  If we have been updating too slowly and have gone out of the current live
-	  window, inform the baseclass accordingly.
-	  This is different from the case where we have been updating quicker than what
-	  the server provides.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+2023-11-13 15:51:44 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-05-23 15:48:16 +0200  Edward Hervey <edward@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtpmanager/gstrtpjitterbuffer.c:
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	* gst/rtpmanager/rtpjitterbuffer.h:
+	  rtpjitterbuffer: Add new "rfc7273-reference-timestamp-meta-only" property
+	  If this property is enabled then the jitterbuffer will do the normal PTS
+	  calculations according to the configured mode instead of making use of
+	  the RFC7273 media clock.
+	  The timestamp calculated from the RFC7273 media clock will only be
+	  stored in the reference timestamp meta, if addition of that meta is enabled.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5512>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	  hlsdemux2: More improvement/fixes to position tracking
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+2023-10-19 20:45:09 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-05-23 15:43:04 +0200  Edward Hervey <edward@centricular.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtpmanager/gstrtpjitterbuffer.c:
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	* gst/rtpmanager/rtpjitterbuffer.h:
+	  rtpjitterbuffer: Add new rfc7273-use-system-clock property
+	  When this property is used, it is assumed that the system clock is
+	  synced close enough to the media clock used by an RFC7273 stream.
+	  As long as both clocks are at most a few seconds from each other this
+	  will give the correct results and avoids having to create an actual
+	  network clock that has to sync first.
+	  If the system clock is actually synchronized to the media clock then
+	  everything will behave exactly the same, otherwise the reference
+	  timestamp meta will be correct but the buffer timestamps will be off by
+	  the difference between the two clocks.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5512>
+
+2023-10-19 20:17:04 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: Handle loss of synchronization
-	  Add a new custom GstFlowReturn so that subclasses can notify that they have lost
-	  live synchronization.
-	  When that happens, do a flushing seek back to the live position
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	  rtpjitterbuffer: Improve handling of media clocks
+	  Do more checks for clock equality than just checking pointers. The same
+	  NTP/PTP clock might be used as pipeline clock but a new instance, so
+	  instead also check what clock they are synced to.
+	  Also handling setting / resetting of the media clock and pipeline clock
+	  correctly by resetting the media clock's state accordingly.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5512>
 
-2022-05-23 09:11:24 +0200  Edward Hervey <edward@centricular.com>
+2023-06-29 15:20:29 +0200  Piotr Brzeziński <piotr@centricular.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Fix SNAP seek handling
-	  * Since only flushing seeks are allowed, the "current" position is always the
-	  global output position (and not "some" stream current position).
-	  * In terms of figuring out to which stream to "snap" to, we can send it to any
-	  selected stream. Removes the requirement of this function to a specific output
-	  pad.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Ignore raw audio streams when adjusting seek
+	  Because we treat raw audio chunks/samples as keyframes, they were interfering
+	  with seek time adjustment.
+	  Became apparent when the accompanying video stream was I-frame only,
+	  for example ProRes.
+	  Since raw audio streams can be seeked freely, it's fine to just ignore them here,
+	  giving priority to the real keyframes in the video stream.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4946>
 
-2022-05-17 07:16:48 +0200  Edward Hervey <edward@centricular.com>
+2023-11-14 15:36:34 +0900  Dongyun Seo <dongyun.seo@lge.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.h:
-	  hlsdemux2: Fix position tracking
-	  Remove the "pending advance" hack and instead rely on the base stream current
-	  position to track our position (instead of a potentially NULL "current
-	  segment").
-	  Also ensure the media playlists are always refreshed with valid stream time,
-	  even if there is no current segment.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/audioparsers/gstdcaparse.c:
+	  dcaparse: keep upstream buffer meta
+	  Some audio decoders cannot decode DTS stream if there is no
+	  valid timestamp. So, keep upstream buffer meta.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5655>
 
-2022-05-17 07:11:17 +0200  Edward Hervey <edward@centricular.com>
+2023-10-21 21:10:55 -0400  Olivier Crête <olivier.crete@collabora.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/hls/m3u8.h:
-	  hlsdemux2: Add a new method to synchronize two media playlist
-	  This allows transfering the stream time of the playlist to an updated
-	  variant/rendition.
-	  This allows updating that information without having a "current segment"
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
+	* gst/rtp/gstrtpopusdepay.c:
+	  rtpopusdepay: set resync flag
+	  - Set re-sync flag on output buffer when rtp had the marker flag set.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5529>
 
-2022-05-17 07:07:23 +0200  Edward Hervey <edward@centricular.com>
+2023-10-31 16:26:00 +0000  Philippe Normand <philn@igalia.com>
 
 	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: Initialize and use stream start/current position
-	  The stream start and current position would be properly set when seeking or
-	  activating a stream after playback started. But it would never be properly
-	  initialized.
-	  Set it to NONE initially to indicate to subclasses that no position has been
-	  tracked yet. This will allow them to detect initial stream usage.
-	  Futhermore, once the initial streams setup is done, make sure that it is set to
-	  a valid initial value:
-	  * The minimum stream time in live
-	  * Or else the period start
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2679>
-
-2022-05-19 15:51:03 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	  adaptivedemux2-stream: Use gst_clear_object when releasing collection
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5606>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: Fix activation of internal pool
-	  If the driver does not support VIDIOC_CREATE_BUFS ioctl, the pool
-	  configuration may get changed, which requires a validation. This would
-	  fail to activate a pool in a case it shouldn't normally fail unless we
-	  are out of memory.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2456>
+2023-10-09 09:11:47 +0000  Johan Adam Nilsson <johan.adam.nilsson@gmail.com>
 
-2022-06-28 01:29:06 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* gst/wavparse/gstwavparse.c:
+	  wavparse: fix buffer leak with adtl tag
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3020
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5595>
 
-	* ext/dv/meson.build:
-	  dv, opusparse: fix duplicate symbols in static build
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1262
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2670>
+2023-11-02 01:48:28 +0000  robert <robert.ayrapetyan@gmail.com>
 
-2022-06-25 19:50:10 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* sys/ximage/gstximagesrc.c:
+	* sys/ximage/meson.build:
+	  ximagesrc: fix xnavigation linking issue
+	  Fixes #3083
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5589>
 
-	* tests/check/meson.build:
-	  tests: skip unit tests for dependency-less elements that have been disabled
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1136
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2660>
+2023-11-02 00:03:28 +0900  Seungha Yang <seungha@centricular.com>
 
-2022-06-24 12:10:02 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* meson.build:
+	  meson: Fix MSVC build with GST_DISABLE_GST_DEBUG
+	  MSVC does not understand Wno-unused
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5585>
 
-	* tests/examples/jack/meson.build:
-	  examples: don't try and build jack examples if jack was disabled
-	  Fixes meson build ERROR: Unknown variable "libjack_dep".
-	  Fixes #1301
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2656>
+2023-11-02 13:17:15 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2022-06-17 14:02:54 +0200  Marc Leeman <m.leeman@televic.com>
+	* docs/gst_plugins_cache.json:
+	* ext/mpg123/gstmpg123audiodec.c:
+	  mpg123audiodec: Update rank from MARGINAL to PRIMARY
+	  This is our primary MP3 decoder after mad got removed.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5590>
 
-	* gst/rtpmanager/rtpsession.c:
-	  rtpsession: properly initialise favor-new property
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2625>
+2023-10-16 20:53:49 +0000  robert <robert.ayrapetyan@gmail.com>
 
-2022-05-27 11:20:06 +0200  Edward Hervey <edward@centricular.com>
+	* sys/ximage/gstximagesrc.c:
+	  ximagesrc: fix compile-time warning and XInitThreads()
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5493>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
-	  hlsdemux2: Look for mpegts synchronization point further
-	  Some mpeg-ts streams have extra data at the beginning. While it's not ideal, we
-	  should be able to cope with it.
-	  Therefore increase the initial search window for at least 4 consecutive
-	  synchronization points to 1kB.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2626>
+2023-10-24 18:20:34 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-05-18 10:23:15 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/libpng/gstpngenc.c:
+	  pngenc: mark output frames as I-frames
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5546>
 
-	* gst/matroska/matroska-demux.c:
-	  matroskademux: Avoid integer-overflow resulting in heap corruption in WavPack header handling code
-	  blocksize + WAVPACK4_HEADER_SIZE might overflow gsize, which then
-	  results in allocating a very small buffer. Into that buffer blocksize
-	  data is memcpy'd later which then causes out of bound writes and can
-	  potentially lead to anything from crashes to remote code execution.
-	  Thanks to Adam Doupe for analyzing and reporting the issue.
-	  CVE: CVE-2022-1920
-	  https://gstreamer.freedesktop.org/security/sa-2022-0004.html
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1226
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2612>
-
-2022-05-30 10:15:37 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-10-24 18:12:44 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Fix integer overflows in zlib decompression code
-	  Various variables were of smaller types than needed and there were no
-	  checks for any overflows when doing additions on the sizes. This is all
-	  checked now.
-	  In addition the size of the decompressed data is limited to 200MB now as
-	  any larger sizes are likely pathological and we can avoid out of memory
-	  situations in many cases like this.
-	  Also fix a bug where the available output size on the next iteration in
-	  the zlib decompression code was provided too large and could
-	  potentially lead to out of bound writes.
-	  Thanks to Adam Doupe for analyzing and reporting the issue.
-	  CVE: tbd
-	  https://gstreamer.freedesktop.org/security/sa-2022-0003.html
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1225
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2610>
-
-2022-05-18 11:24:37 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/libpng/gstpngenc.c:
+	* ext/libpng/gstpngenc.h:
+	  pngenc: output one frame only in snapshot mode
+	  In snapshot mode pngenc should output exactly one frame
+	  and then return FLOW_EOS to upstream. If upstream sends
+	  more input frames before shutting down, it should keep
+	  returning FLOW_EOS but not output any more encoded frames.
+	  After a flushing seek it should output frames again though.
+	  Fixes #3069.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5546>
+
+2023-05-05 17:01:50 +0800  Shengqi Yu <shengqi.yu@mediatek.com>
 
-	* gst/matroska/matroska-read-common.c:
-	  matroskademux: Fix integer overflows in zlib/bz2/etc decompression code
-	  Various variables were of smaller types than needed and there were no
-	  checks for any overflows when doing additions on the sizes. This is all
-	  checked now.
-	  In addition the size of the decompressed data is limited to 120MB now as
-	  any larger sizes are likely pathological and we can avoid out of memory
-	  situations in many cases like this.
-	  Also fix a bug where the available output size on the next iteration in
-	  the zlib/bz2 decompression code was provided too large and could
-	  potentially lead to out of bound writes.
-	  Thanks to Adam Doupe for analyzing and reporting the issue.
-	  CVE: CVE-2022-1922, CVE-2022-1923, CVE-2022-1924, CVE-2022-1925
-	  https://gstreamer.freedesktop.org/security/sa-2022-0002.html
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1225
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2610>
-
-2022-05-18 12:00:48 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2object: scale the encoded sizeimage based on maximum resolution
+	  The default 2MB ENCODED_BUFFER_SIZE can't support some 4K video playback. We now
+	  detect the driver reported maximum resolution and choose an appropriate
+	  default bitstream size accordingly. For 4K video these results in around 4MB
+	  buffer instead of 2MB.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4549>
+
+2023-10-20 08:14:40 +0200  Matthias Fuchs <matthias1.fuchs@zeiss.com>
+
+	* ext/qt6/gstqml6glsrc.cc:
+	  qml6glsrc: sync on the streaming thread
+	  After rendering a QML scene the qml6glsrc element copies the contents of
+	  the scene to a GStreamer buffer. This happens on the Qt render thread.
+	  Then it attaches a sync point to the destination buffer. This sync point
+	  must be awaited by other threads which use the buffer later on. The
+	  current implementation relies on the downstream elements to wait for the
+	  sync point. However, there are situation where this does not work. The
+	  GstBaseTransform e.g. copies the buffer metadata (which overwrites the
+	  sync point without waiting for it) *before* waiting for the sync point.
+	  This commit waits for the sync point inside the qml6glsrc element before
+	  sending it downstream. The wait command is issued on the streaming
+	  thread with the pipeline OpenGL context, i.e. it will synchronize with
+	  the GStreamer OpenGL thread.
+	  This is a port of the original fix for the qmlglsrc element.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5519>
 
-	* gst/avi/gstavidemux.c:
-	  avidemux: Fix integer overflow resulting in heap corruption in DIB buffer inversion code
-	  Check that width*bpp/8 doesn't overflow a guint and also that
-	  height*stride fits into the provided buffer without overflowing.
-	  Thanks to Adam Doupe for analyzing and reporting the issue.
-	  CVE: CVE-2022-1921
-	  See https://gstreamer.freedesktop.org/security/sa-2022-0001.html
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1224
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2608>
-
-2022-05-19 04:16:25 +0000  Adam Doupe <adamdoupe@gmail.com>
-
-	* gst/smpte/gstmask.c:
-	  smpte: Fix integer overflow with possible heap corruption in GstMask creation.
-	  Check that width*height*sizeof(guint32) doesn't overflow when
-	  allocated user_data for mask, potential for heap overwrite when
-	  inverting.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1231
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2603>
-
-2022-06-14 23:03:26 +0200  Piotr Brzeziński <piotr@centricular.com>
+2022-04-06 12:56:30 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/dash/gstmpd-prelude.h:
-	* ext/adaptivedemux2/dash/gstmpdhelper.h:
-	* ext/adaptivedemux2/dash/gstmpdnode.h:
-	* ext/adaptivedemux2/dash/gstmpdparser.h:
-	* ext/adaptivedemux2/dash/gstxmlhelper.h:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/gsthlselement.c:
-	* ext/adaptivedemux2/hls/gsthlselements.h:
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/mss/gstmss-prelude.h:
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	* ext/adaptivedemux2/mss/gstmssdemux.h:
-	* ext/adaptivedemux2/mss/gstmssfragmentparser.h:
-	* ext/adaptivedemux2/mss/gstmssmanifest.h:
-	* ext/soup/gstsouploader.c:
-	* ext/soup/gstsouploader.h:
-	* tests/check/elements/hlsdemux_m3u8.c:
-	  adaptivedemux2: Prevent duplicate symbols on static builds
-	  Uses prelude header files with #defines to rename DASH and MSS
-	  symbols duplicated in their old standalone versions.
-	  Also redefines soup-related functions when building it for
-	  adaptivedemux2 to prevent symbol conflicts there.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2534>
+	* meson.build:
+	  meson: Bump GLib requirement to >= 2.64
+	  This includes fixes to make GstBus watches non-racy.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2126>
 
-2022-05-12 02:51:00 +1000  Jan Schmidt <jan@centricular.com>
+2023-10-21 18:22:38 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-track.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	  adaptivedemux2: Improve reverse playback buffering.
-	  In reverse playback, store the lowest running time in each GOP
-	  as the input_time for buffering purposes. That means we end up
-	  storing at least a complete GOP before declaring buffering
-	  100%
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2510>
+	* docs/gst_plugins_cache.json:
+	* ext/flac/gstflacenc.c:
+	  flacenc: signal in output caps that the output is framed
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5524>
 
-2022-04-08 23:06:09 +1000  Jan Schmidt <jan@centricular.com>
+2023-10-12 17:23:00 +0100  Tim-Philipp Müller <tim@centricular.com>
 
 	* docs/gst_plugins_cache.json:
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux-track.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	  adaptivedemux2: reverse playback running times
-	  Account for running time moving non-monotonically in
-	  reverse playback by tracking the highest running time
-	  seen at each point.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2510>
+	* gst/rtp/gstrtpac3depay.c:
+	  rtpac3depay: should output audio/x-ac3 not audio/ac3
+	  audio/x-ac3 is the canonical media format in GStreamer.
+	  audio/ac3 is sometimes accepted as input (e.g. in rtpac3pay
+	  or ac3parse), but shouldn't be output.
+	  Fixes #3038.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5472>
 
-2022-04-06 12:56:30 +0100  Tim-Philipp Müller <tim@centricular.com>
-
-	* ext/adaptivedemux2/hls/m3u8.h:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	* gst/rtpmanager/gstrtpst2022-1-fecenc.c:
-	* gst/udp/gstudpelement.c:
-	* meson.build:
-	  Bump GLib requirement to >= 2.62
-	  Can't require 2.64 yet because of
-	  https://gitlab.freedesktop.org/gstreamer/cerbero/-/issues/323
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2568>
+2023-10-17 15:24:22 +0200  Matthias Fuchs <matthias1.fuchs@zeiss.com>
 
-2022-06-08 14:11:57 +0200  Marc Leeman <m.leeman@televic.com>
+	* ext/qt/gstqtsrc.cc:
+	  qmlglsrc: sync on the streaming thread
+	  After rendering a QML scene the qmlglsrc element copies the contents of
+	  the scene to a GStreamer buffer. This happens on the Qt render thread.
+	  Then it attaches a sync point to the destination buffer. This sync point
+	  must be awaited by other threads which use the buffer later on. The
+	  current implementation relies on the downstream elements to wait for the
+	  sync point. However, there are situation where this does not work. The
+	  GstBaseTransform e.g. copies the buffer metadata (which overwrites the
+	  sync point without waiting for it) *before* waiting for the sync point.
+	  This commit waits for the sync point inside the qmlglsrc element before
+	  sending it downstream. The wait command is issued on the streaming
+	  thread with the pipeline OpenGL context, i.e. it will synchronize with
+	  the GStreamer OpenGL thread.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5506>
+
+2023-09-03 18:47:24 +0000  Robert Ayrapetyan <robert.ayrapetyan@gmail.com>
 
 	* docs/gst_plugins_cache.json:
-	* gst/udp/gstmultiudpsink.c:
-	  fix trivial distination -> destination
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2573>
+	* meson_options.txt:
+	* sys/ximage/gst-ximage-navigation.c:
+	* sys/ximage/gst-ximage-navigation.h:
+	* sys/ximage/gstximagesrc.c:
+	* sys/ximage/gstximagesrc.h:
+	* sys/ximage/meson.build:
+	  ximagesrc: add navigation support
+	  Add a basic navigation support:
+	  - mouse events (buttons/move)
+	  - keyboard events (keys)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5273>
 
-2022-04-21 12:47:31 +0200  Ignacio Casal Quinteiro <qignacio@amazon.com>
+2023-09-27 13:41:33 +0200  Jordan Petridis <jordan@centricular.com>
 
-	* sys/osxaudio/gstosxcoreaudiohal.c:
-	  osxaudio: remove usage of goto
-	  It is easier to follow the code without the goto now
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2251>
+	* ext/qt6/gstqsg6material.cc:
+	  qt6: if def newer symbosl in QRhiTexture
+	  version 6.4 added QRhiTexture::RGB10A2 but we depend on an older
+	  version of qt in meson, and we can keep compiling with older Qt6
+	  versions still.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5475>
 
-2022-03-30 12:59:46 +0200  Ignacio Casal Quinteiro <qignacio@amazon.com>
+2023-10-12 16:05:18 +0200  Stéphane Cerveau <scerveau@igalia.com>
 
-	* sys/osxaudio/gstosxcoreaudiohal.c:
-	  osxaudio: support hidden devices
-	  macOS features hidden devices. These are devices that will
-	  not be shown in the macOS UIs and that cannot be retrieved
-	  without having the specific UID of the hidden device. There
-	  are cases when you might want to have a hidden device, for example
-	  when having a virtual speaker that forwards the data to a virtual
-	  hidden input device from which you can then grab the audio.
-	  The blackhole project supports these hidden devices and
-	  this patch provides a way that if the device id is a hidden
-	  device it will use it instead of check the hardware list of devices
-	  to understand if the device is valid.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2251>
-
-2022-04-20 18:12:02 +0200  Ignacio Casal Quinteiro <qignacio@amazon.com>
+	* gst/multifile/gstimagesequencesrc.c:
+	  imagesequencesrc: fix regular image deadlock
+	  With one regular image file path provided (without %05d),
+	  the element was stuck in a dead loop counting the frames:
+	  gst_image_sequence_src_count_frames
+	  This allows to display any image file out of the element
+	  for a given number of buffers.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5471>
 
-	* sys/osxaudio/gstosxcoreaudiohal.c:
-	  osx: fix indent
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2251>
+2023-10-12 00:27:11 +1100  Matthew Waters <matthew@centricular.com>
 
-2022-03-30 12:48:02 +0200  Ignacio Casal Quinteiro <qignacio@amazon.com>
+	* ext/qt6/meson.build:
+	  build/qt6: properly error/skip build if the qsb tool is not found
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3032
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5459>
 
-	* sys/osxaudio/gstosxcoreaudiohal.c:
-	  osxaudio: iterate device only if needed
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2251>
+2023-09-05 17:56:49 +0200  Michael Tretter <m.tretter@pengutronix.de>
 
-2022-03-30 09:59:59 +0200  Ignacio Casal Quinteiro <qignacio@amazon.com>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: unconditionally activate the OUTPUT pool
+	  If the v4l2videoenc receives an QUERY_ALLOCATION, it must not propose a
+	  currently used pool, because it cannot be sure that the allocation query came
+	  from exactly the same upstream element. The QUERY_ALLOCATION will not contain
+	  the internal OUTPUT pool.
+	  The upstream element (the basesrc) detects that the newly proposed pool differs
+	  from the old pool. It deactivates the old pool and switches to the new pool.
+	  If there was a format change, a new OUTPUT buffer pool will be allocated in
+	  gst_v4l2_object_set_format_full() and the CAPTURE task will be stopped to switch
+	  the format. If there hasn't been a format change,
+	  gst_v4l2_object_set_format_full() will not be called. The old pool will be kept
+	  and reused.
+	  Without a format change, the processing task continues running.
+	  This leads to the situation that the processing task is running, but the OUTPUT
+	  buffer pool (the old pool) is deactivated. Therefore, the encoder is not able to
+	  get buffers from the OUTPUT pool and encoding cannot continue.
+	  This situation can be triggered by sending a RECONFIGURE event without a format
+	  change.
+	  Resolve this situation by ensuring that the OUTPUT buffer pool is always
+	  activated when frames arrive at the encoder.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4235>
+
+2023-10-10 14:53:08 +0200  Michael Tretter <m.tretter@pengutronix.de>
 
-	* sys/osxaudio/gstosxcoreaudiohal.c:
-	  osxaudio: reduce scope of default device id variable
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2251>
-
-2022-06-07 17:31:43 +0200  Bastien Nocera <hadess@hadess.net>
-
-	* ext/gtk/gtkgstglwidget.c:
-	  gtk: Fix double-free when OpenGL can't be initialised
-	  gtk_gl_area_get_error() doesn't return a copy of the error, but just the
-	  error. If initialising OpenGL fails, then GtkGstGLWidget will consume
-	  the error, and cause GTK to try and display freed memory.
-	  ==50914== Invalid read of size 8
-	  ==50914==    at 0x4C4CB8A: gtk_gl_area_draw_error_screen (gtkglarea.c:663)
-	  ==50914==    by 0x4C4CB8A: gtk_gl_area_draw (gtkglarea.c:687)
-	  ==50914==    by 0x4E061CA: gtk_widget_draw_internal (gtkwidget.c:7084)
-	  ==50914==    by 0x4BAEFB1: gtk_container_propagate_draw (gtkcontainer.c:3854)
-	  ==50914==    by 0x4D4B6BF: gtk_stack_render (gtkstack.c:2207)
-	  ==50914==    by 0x4BB4B03: gtk_css_custom_gadget_draw (gtkcsscustomgadget.c:159)
-	  ==50914==    by 0x4BBA4C4: gtk_css_gadget_draw (gtkcssgadget.c:885)
-	  ==50914==    by 0x4D4D780: gtk_stack_draw (gtkstack.c:2119)
-	  ==50914==    by 0x4E061CA: gtk_widget_draw_internal (gtkwidget.c:7084)
-	  ==50914==    by 0x4BAEFB1: gtk_container_propagate_draw (gtkcontainer.c:3854)
-	  ==50914==    by 0x4BAF0C3: gtk_container_draw (gtkcontainer.c:3674)
-	  ==50914==    by 0x4E061CA: gtk_widget_draw_internal (gtkwidget.c:7084)
-	  ==50914==    by 0x4BAEFB1: gtk_container_propagate_draw (gtkcontainer.c:3854)
-	  ==50914==  Address 0x187a0818 is 8 bytes inside a block of size 16 free'd
-	  ==50914==    at 0x48480E4: free (vg_replace_malloc.c:872)
-	  ==50914==    by 0x49A5B8C: g_free (gmem.c:218)
-	  ==50914==    by 0x49C1013: g_slice_free1 (gslice.c:1183)
-	  ==50914==    by 0x4990DE4: g_error_free (gerror.c:870)
-	  ==50914==    by 0x4990FE9: g_clear_error (gerror.c:1052)
-	  ==50914==    by 0x1A489780: _get_gl_context (gtkgstglwidget.c:540)
-	  ==50914==    by 0x1A4863CB: gst_gtk_invoke_func (gstgtkutils.c:39)
-	  ==50914==    by 0x49A3834: g_main_context_invoke_full (gmain.c:6137)
-	  ==50914==    by 0x1A486450: gst_gtk_invoke_on_main (gstgtkutils.c:59)
-	  ==50914==    by 0x1A48A29E: gtk_gst_gl_widget_init_winsys (gtkgstglwidget.c:632)
-	  ==50914==    by 0x1A4887E7: gst_gtk_gl_sink_start (gstgtkglsink.c:267)
-	  ==50914==    by 0x6579810: gst_base_sink_change_state (gstbasesink.c:5662)
-	  ==50914==  Block was alloc'd at
-	  ==50914==    at 0x484586F: malloc (vg_replace_malloc.c:381)
-	  ==50914==    by 0x49A9278: g_malloc (gmem.c:125)
-	  ==50914==    by 0x49C1BA5: g_slice_alloc (gslice.c:1072)
-	  ==50914==    by 0x49C3BCC: g_slice_alloc0 (gslice.c:1098)
-	  ==50914==    by 0x499096B: g_error_allocate (gerror.c:708)
-	  ==50914==    by 0x4990AF1: UnknownInlinedFun (gerror.c:722)
-	  ==50914==    by 0x4990AF1: g_error_copy (gerror.c:892)
-	  ==50914==    by 0x4C4B9F9: gtk_gl_area_set_error (gtkglarea.c:1036)
-	  ==50914==    by 0x4C4BAF7: gtk_gl_area_real_create_context (gtkglarea.c:346)
-	  ==50914==    by 0x4B21B28: _gtk_marshal_OBJECT__VOIDv (gtkmarshalers.c:2730)
-	  ==50914==    by 0x4920B78: UnknownInlinedFun (gclosure.c:893)
-	  ==50914==    by 0x4920B78: g_signal_emit_valist (gsignal.c:3406)
-	  ==50914==    by 0x4920CB2: g_signal_emit (gsignal.c:3553)
-	  ==50914==    by 0x4C4B927: gtk_gl_area_realize (gtkglarea.c:308)
-	  Reproduced by running:
-	  MESA_GL_VERSION_OVERRIDE=2.7 totem
-	  See https://gitlab.gnome.org/GNOME/totem/-/issues/522
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2565>
-
-2022-06-06 12:31:52 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: fix activation of internal pool
+	  Fix the buffer pool activation if the driver does not support VIDIOC_CREATE_BUFS
+	  the same way as it was fixed for the v4l2videodec.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4235>
 
-	* gst/flv/gstflvdemux.c:
-	* gst/flv/gstflvdemux.h:
-	  flvdemux: Make use of the streams API if used in a streams-aware bin
-	  This allows adding audio/video streams after 6s.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2559>
+2023-09-05 17:54:31 +0200  Michael Tretter <m.tretter@pengutronix.de>
 
-2022-06-03 18:35:54 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: rename OUTPUT pool to opool
+	  There is a CAPTURE pool in the same function. While the CAPTURE pool is called
+	  cpool, using pool for the OUTPUT pool is confusing.
+	  Using opool for the OUTPUT pool makes it more obvious, which pool is used.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4235>
 
-	* gst/audioparsers/gstaacparse.c:
-	  aacparse: Avoid mismatch between src_caps and output_header_type
-	  If our downstream caps didn't intersect, we attempted to convert between
-	  raw and ADTS stream formats, if possible. If the caps still did not
-	  intersect, we then used the modified `src_caps` but left the
-	  `output_header_type` unmodified.
-	  This caused a mismatch between caps and actual stream format.
-	  Avoid this by first copying the `src_caps` to `convcaps` for the
-	  additional intersection tests, replacing `src_caps` if we succeed.
-	  While we're here, clean up the code a bit and remove the `codec_data`
-	  field from outgoing ADTS caps.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2550>
-
-2022-06-04 10:27:09 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-10-11 14:47:33 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-	* gst/flv/gstflvdemux.c:
-	* gst/flv/gstflvelement.c:
-	  flvdemux: Actually make use of the debug category
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2552>
+	* gst/flv/gstflvmux.c:
+	  flvmux: set the src segment position as running time
+	  We were already converting the pad last timestamp to running time but
+	  not the segment position.
+	  This segment position is used by gst_aggregator_simple_get_next_time()
+	  to compute the waiting time when aggregating.
+	  Those waiting times were wrong in my live pipeline using the system
+	  clock, resulting in the aggregator to never wait at all.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5460>
 
-2022-06-01 16:14:24 +0200  Stéphane Cerveau <scerveau@collabora.com>
+2023-10-11 09:36:11 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/soup/gstsoup.c:
-	* ext/soup/gstsoupelement.c:
-	* ext/soup/gstsouploader.c:
-	  soup: fix soup debug category
-	  Use soup debug category in souploader
-	  for soup plugin element load.
-	  Inititalize properly soup utils category.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2535>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: Fix tiled formats stride conversion
+	  While adding arbitrary tile support, a round up operation was badly
+	  converter. This caused the Y component of the stride to be 0. This
+	  eventually lead to a crash in glupoad preceded by the following
+	  assertion.
+	  gst_gl_buffer_allocation_params_new: assertion 'alloc_size > 0' failed
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5458>
 
-2022-05-29 06:05:27 +0900  Seungha Yang <seungha@centricular.com>
+2023-10-04 11:09:37 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hlsdemux2: Adjust debug log level
-	  HLS manifest might not be represented by a single common caps
-	  when different codecs are mixed in a playlist, but it
-	  does not seem to be a critical issue we need to warn.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2516>
+	* ext/adaptivedemux2/downloadhelper.c:
+	* ext/adaptivedemux2/downloadrequest.c:
+	* ext/adaptivedemux2/downloadrequest.h:
+	  adaptivedemux2: Do not submit_transfer when cancelled
+	  There is a race condition where transfer has not been submitted yet while the
+	  request is cancelled which leads to the transfer state going back to
+	  `DOWNLOAD_REQUEST_STATE_OPEN` and the user of the request to get signalled about
+	  its completion (and the task actually happening after it was cancelled) leading
+	  to assertions and misbehaviours.
+	  To ensure that this race can't happen, we start differentiating between the
+	  UNSENT and CANCELLED states as in the normal case, when entering `submit_request`
+	  the state is UNSENT and at that point we need to know that it is not because
+	  the request has been cancelled.
+	  In practice this case lead to an assertion in
+	  `gst_adaptive_demux2_stream_begin_download_uri` because in a previous call to
+	  `gst_adaptive_demux2_stream_stop_default` we cancelled the previous request and
+	  setup a new one while it had not been submitted yet and then got a `on_download_complete`
+	  callback called from that previous cancelled request and then we tried to do
+	  `download_request_set_uri` on a request that was still `in_use`, leading to
+	  something like:
+	  ```
+	  #0: 0x0000000186655ec8 g_assert (request->in_use == FALSE)assert.c:0
+	  #1: 0x00000001127236b8 libgstadaptivedemux2.dylib`download_request_set_uri(request=0x000060000017cc00, uri="https://XXX/chunk-stream1-00002.webm", range_start=0, range_end=-1) at downloadrequest.c:361
+	  #2: 0x000000011271cee8 libgstadaptivedemux2.dylib`gst_adaptive_demux2_stream_begin_download_uri(stream=0x00000001330f1800, uri="https://XXX/chunk-stream1-00002.webm", start=0, end=-1) at gstadaptivedemux-stream.c:1447
+	  #3: 0x0000000112719898 libgstadaptivedemux2.dylib`gst_adaptive_demux2_stream_load_a_fragment [inlined] gst_adaptive_demux2_stream_download_fragment(stream=0x00000001330f1800) at gstadaptivedemux-stream.c:0
+	  #4: 0x00000001127197f8 libgstadaptivedemux2.dylib`gst_adaptive_demux2_stream_load_a_fragment(stream=0x00000001330f1800) at gstadaptivedemux-stream.c:1969
+	  #5: 0x000000011271c2a4 libgstadaptivedemux2.dylib`gst_adaptive_demux2_stream_next_download(stream=0x00000001330f1800) at gstadaptivedemux-stream.c:2112
+	  ```
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5435>
 
-2022-05-02 16:55:34 +0200  Stéphane Cerveau <scerveau@collabora.com>
+2023-10-03 15:05:15 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/adaptivedemux2/dash/gstdashdemux.c:
-	* ext/adaptivedemux2/dash/gstdashdemux.h:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemuxelement.c:
-	* ext/adaptivedemux2/gstadaptivedemuxelements.h:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.h:
-	* ext/adaptivedemux2/meson.build:
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	* ext/adaptivedemux2/mss/gstmssdemux.h:
-	* ext/adaptivedemux2/plugin.c:
-	  adaptivedemux2: fix plugin/element init
-	  In case of per features registration such as the
-	  customizable gstreamer-full library, each
-	  element should check that the soup library can be loaded to
-	  facilitate the element registration.
-	  Initialize the debug categories properly
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2348>
-
-2022-05-29 20:27:38 +1000  Jan Schmidt <jan@centricular.com>
+	* docs/gst_plugins_cache.json:
+	  doc: Update plugin cache for added DMA_DRM format
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5386>
 
-	* gst/rtpmanager/gstrtpptdemux.c:
-	  rtpptdemux: Don't GST_FLOW_ERROR when ignoring invalid packets
-	  https://bugzilla.gnome.org/show_bug.cgi?id=741398 changed
-	  rtpptdemux in 2014 to not post a GST_ELEMENT_ERROR on the
-	  bus when dropping an invalid (non-RTP) packet, but still
-	  returned GST_FLOW_ERROR upstream - so the pipeline still
-	  stops, but now without a useful bus error.
-	  Return GST_FLOW_OK instead, so the pipeline keeps
-	  running. Some old telephony equipment can send invalid
-	  packets before the real RTP traffic starts.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2520>
-
-2022-05-28 16:46:04 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2023-09-27 13:55:33 +0200  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* ext/jack/gstjackaudiosrc.c:
-	  jack: Always use jack_free as specified by the docs
-	  Fixes a crash on Windows due to a CRT mismatch. The JACK installation
-	  still uses MSVCRT, and we the Universal CRT for both MinGW and MSVC.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2497>
+	* gst/deinterlace/gstdeinterlace.c:
+	  video-filters: Fix passthrough with ANY caps feature
+	  With the support for DRM modifiers, passthrough caps must now include DMA_DRM
+	  format, otherwise pipeline using thhese filters unconditionally may fail
+	  to negotiate.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5386>
 
-2022-05-26 01:21:43 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2023-09-29 15:47:48 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/jack/meson.build:
-	  jack: Add support for detecting libjack on Windows
-	  No source code changes were necessary to get the plugin working on
-	  Windows with MSVC.
-	  Run QJackCtl and audiotestsrc ! jackaudiosink just works.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2497>
+	* docs/gst_plugins_cache.json:
+	  docs: Update plugins caches
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5412>
+
+2023-09-28 18:03:31 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* ext/flac/gstflacenc.c:
+	  flacenc: Correctly handle up to 255 cue entries
+	  The counter was using a signed 8 bit integer, which was overflowing
+	  after 127 entries. That was then passed as an unsigned 32 bit integer to
+	  libflac, which caused it to be converted to a huge unsigned number.
+	  That then caused an invalid memory access inside libflac.
+	  As a bonus, signed integer overflow is undefined behaviour.
+	  Instead, use an unsigned 8 bit integer. Once this overflows the existing
+	  code already catches it and stops adding the cue. While FLAC__metadata_object_cuesheet_insert_track()
+	  takes an unsigned 32 bit integer for the track number, FLAC__StreamMetadata_CueSheet_Track is
+	  limiting it to an unsigned 8 bit integer.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2921
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5420>
+
+2023-09-28 16:59:14 +0200  Dominique Leroux <dominique@unity3d.com>
+
+	* sys/osxaudio/gstosxaudio.c:
+	* sys/osxaudio/gstosxaudiodeviceprovider.c:
+	* sys/osxaudio/gstosxaudiodeviceprovider.h:
+	* sys/osxaudio/gstosxaudiosink.c:
+	* sys/osxaudio/gstosxaudiosink.h:
+	* sys/osxaudio/gstosxaudiosrc.c:
+	* sys/osxaudio/gstosxaudiosrc.h:
+	  osxaudio: add individual elements registration for gst-full compatibility
+	  Found that osxaudiosink could not be added standalone in gst-full build
+	  using
+	  -Dgst-full-elements=osxaudio:osxaudiosink because element registration
+	  was
+	  done at the plugin level. Now src/sink elements and deviceprovider have
+	  their
+	  individual registration.
+	  Copied/adapted from the alsa plugin.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5419>
+
+2023-09-27 15:16:43 +0200  Stéphane Cerveau <scerveau@igalia.com>
 
-2022-05-24 16:07:13 +0800  Hou Qi <qi.hou@nxp.com>
+	* ext/adaptivedemux2/dash/gstmpd-prelude.h:
+	* ext/adaptivedemux2/dash/gstmpdhelper.c:
+	* ext/adaptivedemux2/dash/gstmpdhelper.h:
+	  mpdhelper: remove useless code
+	  The audio/video codec name from mime type should be retrieved from
+	  gst_codec_utils_caps_get_mime_codec instead
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5404>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2: Reset transfer in gst_v4l2_object_acquire_format()
-	  get_colorspace() checks input caps transfer when mapping V4L2_XFER_FUNC_709
-	  back to V4L2_COLORSPACE_BT2020 and GST_VIDEO_TRANSFER_BT2020_12. After
-	  receiving source change event, decoder will G_FMT and S_FMT again. So need
-	  to reset transfer when acquiring format to avoid using the old transfer.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2475>
+2023-09-27 08:46:35 -0400  Xavier Claessens <xavier.claessens@collabora.com>
 
-2022-05-25 13:00:58 +0200  Piotrek Brzeziński <piotr@centricular.com>
+	* ext/vpx/gstvp8enc.c:
+	* ext/vpx/gstvpxelement.c:
+	* gst/rtp/gstrtpvp8pay.c:
+	* tests/check/elements/rtpvp8.c:
+	* tests/check/elements/vp8enc.c:
+	  GstCustomMeta: Use simplified API where possible
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5385>
 
-	* gst/cutter/gstcutter.c:
-	* gst/cutter/gstcutter.h:
-	  cutter: Include running/stream-time in messages
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2490>
+2023-09-26 16:48:10 +0000  Florian Zwoch <fzwoch@gmail.com>
 
-2022-05-03 11:34:15 +0200  Stéphane Cerveau <scerveau@collabora.com>
+	* ext/adaptivedemux2/downloadhelper.c:
+	  adaptivedemux2: Call GTasks's return functions for blocking tasks
+	  Gio/Task states the following:
+	  If a GTask has been constructed and its callback set, it is an error to
+	  not call g_task_return_*() on it. GLib will warn at runtime if this
+	  happens (since 2.76).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5395>
+
+2023-09-27 08:48:03 +0200  Albert Sjölund <alberts@axis.com>
 
-	* ext/soup/gstsoup.c:
-	* ext/soup/gstsoupelement.c:
-	* ext/soup/gstsoupelements.h:
-	* ext/soup/gstsouphttpclientsink.c:
 	* ext/soup/gstsouphttpsrc.c:
-	  soup: Fix plugin/element init
-	  In case of per features registration such as the
-	  customizable gstreamer-full library, each
-	  element should check that the soup library can be loaded to
-	  facilitate the element registration.
-	  Initialize the debug category properly
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2349>
+	  souphttpsrc: Chain finalize call to parent
+	  GstSoupSession finalize does not chain parent finalize,
+	  causing it to leak memory, shown under g freeze notify.
+	  In finalize method, ensure all branches call to parent
+	  finalize.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5398>
 
-2022-05-23 21:24:40 -0400  Eli Schwartz <eschwartz@archlinux.org>
+2023-09-18 08:40:07 +0200  Daniel Moberg <daniemob@axis.com>
 
-	* meson.build:
-	  meson: use better zlib dependency fallback
-	  zlib is required, and if it isn't found it is checked several ways and
-	  then forced via subproject(). This code was added in commit
-	  b93e37592a3ccc0eaece1c8fef2d362b1e5fe685, to account for systems where
-	  zlib doesn't have pkg-config files installed.
-	  But Meson already does dependency fallback, and also, since 0.54.0, does
-	  the in-between checks for find_library('z') and has_header('zlib.h') via
-	  the "system" type dependency. Simplify dependency lookup by marking it
-	  as required, which also makes sure that the console log doesn't
-	  confusingly list "not found".
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2484>
-
-2022-05-19 16:33:47 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	* docs/gst_plugins_cache.json:
+	* gst/rtsp/gstrtspsrc.c:
+	* gst/rtsp/gstrtspsrc.h:
+	  rtspsrc: Property for adding custom http request headers
+	  This commit adds a property which enables adding custom http request headers to
+	  the rtspsrc element. Added headers will be appended to http requests
+	  made during http tunneling.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5268>
+
+2023-09-15 09:12:25 +0200  Stijn Last <stijn.last@barco.com>
+
+	* gst/deinterlace/tvtime/greedy.c:
+	  deinterlace: greedy, improve quality
+	  scanlines->m1 = same line of the previous field
+	  scanlines->t0 = line above of the current field
+	  scanlines->b0 = line below of the current field
+	  scanlines->mp = same line of the next field
+	  Deinterlacing a field weaved frame:
+	  When deinterlacing the top field, the next bottom field is available
+	  (part of the same frame). but when deinterlacing the bottom field,
+	  the next top field (part of the next frame) is not available and
+	  scanlines->mp equals NULL.
+	  In this case it's better to use greedy algorithm using the prevous field
+	  (twice) rather then linear interpolation of the current field.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5331>
+
+2023-09-22 16:57:28 +0900  Hou Qi <qi.hou@nxp.com>
 
 	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: Fix missing handling of resolution-change
-	  The pool process function may poll and get the resolution-change event
-	  whenever it is not possible to share our buffers. This typically happen
-	  when downstream does not support GstVideoMeta.
-	  Not handling this would cause the decoder thread to exit silently and the
-	  pipeline to stall.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2457>
+	  v4l2videodec: Correctly free caps to avoid memory leak
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5379>
 
-2022-05-19 16:39:14 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-09-21 19:50:31 +0900  Seungha Yang <seungha@centricular.com>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: Downgrade to info resolution-change trace
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2457>
+	* docs/gst_plugins_cache.json:
+	  video: Add GBR 16bits formats
+	  Adding 16bits planar RGB formats
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5375>
 
-2022-05-23 20:23:38 +0100  Tim-Philipp Müller <tim@centricular.com>
+2023-09-14 01:35:10 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* ext/shout2/gstshout2.c:
-	* ext/shout2/gstshout2.h:
-	* ext/shout2/meson.build:
-	  shout2: fix compiler warnings and bump req to libshout >= 2.4.2
-	  Fix compiler warnings with latest libshout version (2.4.6).
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2473>
+	* gst/rtpmanager/rtpsource.c:
+	  rtpsource: Don't store invalid running times and calculate with it
+	  If we end up with GST_CLOCK_TIME_NONE as running time for an RTP packet
+	  then this can't be used for bitrate estimation, and also not for
+	  constructing the next RTCP SR. Both would end up with completely wrong
+	  values, and an RTCP SR with wrong values can easily break
+	  synchronization in receivers.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5329>
 
-2022-05-14 14:58:04 +0200  László Károlyi <laszlo@karolyi.hu>
+2023-09-19 18:28:47 +0200  Piotr Brzeziński <piotr@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* ext/shout2/gstshout2.c:
-	* ext/shout2/gstshout2.h:
-	  shout2send: Adding send-title-info and user-agent options
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2420>
+	* ext/qt/qtplugin.pro:
+	  qml: Fix leftover reference to gstqsgtexture
+	  Made it impossible to build with qmake as per the readme. The file was renamed to gstqsgmaterial a while ago.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5357>
 
-2022-05-19 11:47:57 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-09-19 09:14:31 +0200  Olivier Blin <olivier.blin@softathome.com>
 
-	* gst/isomp4/qtdemux.c:
-	* gst/isomp4/qtdemux.h:
-	  qtdemux: Add support for ONVIF XML Timed MetaData
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2453>
+	* ext/pulse/pulsedeviceprovider.c:
+	  pulsedeviceprovider: fix incorrect usage of GST_ELEMENT_ERROR
+	  The provider is not a GStreamer element.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5349>
 
-2022-05-19 11:30:20 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-09-07 17:23:37 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-	* gst/isomp4/fourcc.h:
-	* gst/isomp4/qtdemux_dump.c:
-	* gst/isomp4/qtdemux_types.c:
-	  qtdemux: Add parsing/dumping of nmhd / metx boxes
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2453>
+	* gst/rtpmanager/gstrtpjitterbuffer.c:
+	  rtpjitterbuffer: Avoid integer overflow in max saveable packets calculation with negative offset
+	  The timestamp offset can be negative, and it can be a bigger negative
+	  number than the latency introduced by the rtpjitterbuffer so the overall
+	  timeout offset can be negative.
+	  Using the negative offset for calculating how many packets can still
+	  arrive in time when encountering a lost packet in an equidistant stream
+	  would then overflow and instead of considering fewer packets lost a lot
+	  more packets are considered lost.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5296>
+
+2023-09-05 16:56:44 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-05-19 11:06:31 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Handle video helper return value
+	  gst_video_info_set_interlaced_format() can return an error if the
+	  width/height causes integer overflow. Handle this case, so that we can
+	  fail cleanly. This has been experienced while testing an in-progress
+	  driver.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5286>
 
-	* gst/isomp4/fourcc.h:
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Parse styp box for informational purposes
-	  And include some more details in the debug logs for the ftyp box too.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2453>
+2023-09-05 16:51:24 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-05-10 16:20:46 +0800  Hou Qi <qi.hou@nxp.com>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Avoid warnings on empty last buffer
+	  Some drivers will push an buffer flagged LAST but empty. In decoder
+	  case, this results in an "producing too many buffer" warning, even
+	  though the result is entirely correct. Detect this case in order to
+	  signal EOS earlier and avoid this warning.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5286>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2: set default resolution if caps has no such information
-	  Output may attemp to set the width and height to zero values if
-	  caps has no such information, which will cause capture get invalid
-	  dimensions. Then decoder reports negotiation failure.
-	  So need to set default resolution if caps has no such information.
-	  Real values can be set again until source change event is signaled.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2400>
+2023-09-05 16:15:19 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-05-17 05:21:19 +1000  Jan Schmidt <jan@centricular.com>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Do not resize compressed buffer
+	  Avoid resizing compressed buffer to their maximum size. This fixes a
+	  regression that caused valid but very large streams to be generated.
+	  Fixes #2953
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5286>
 
-	* gst/multifile/gstsplitmuxpartreader.c:
-	  splitmuxsrc: Re-queue sticky events after probing.
-	  When processing the first event after probing the
-	  file and being activated, requeue sticky events
-	  as there's no requirement that demuxers send tag
-	  and other events again after a seek - that's
-	  why they're sticky.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2432>
+2023-09-10 19:13:28 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-05-16 14:14:46 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* docs/gst_plugins_cache.json:
+	  doc: Update cache after template pixel formats changes
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5304>
 
-	* gst/deinterlace/gstdeinterlace.c:
-	  deinterlace: Clean up error handling in chain and _push_history
-	  - Consistently unref the chained buffer at the end of the chain
-	  function, if we're not handing it off to `gst_pad_push`. This avoids a
-	  few buffer leaks in the error paths in `_chain` and `_push_history`.
-	  - When mapping the video frame fails, return a flow error instead of
-	  crashing.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2428>
+2023-09-06 20:28:39 +1000  Matthew Waters <matthew@centricular.com>
 
-2022-05-16 14:40:41 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* ext/qt6/gstqml6glmixer.cc:
+	* tests/examples/qt6/qmlmixer/main.cpp:
+	  qml6glmixer: add support for non-RGBA inputs
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5290>
 
-	* gst/multifile/gstsplitmuxsink.c:
-	* gst/multifile/gstsplitmuxsink.h:
-	  splitmuxsink: When flushing, exit handle_mq_input quickly
-	  If we just break the loop, we might run into the `gop != NULL` assert
-	  that follows it. Rather, exit immediately with flushing flow.
-	  Also use this flushing mechanism when we release a pad. This avoids
-	  having an extra flag.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1030>
+2023-09-06 20:28:20 +1000  Matthew Waters <matthew@centricular.com>
 
-2021-01-26 16:33:25 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* ext/qt6/gstqml6gloverlay.cc:
+	* tests/examples/qt6/qmloverlay/main.cpp:
+	  qml6glovleray: add support for non-RGBA inputs
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5290>
 
-	* gst/multifile/gstsplitmuxsink.c:
-	  splitmuxsink: Avoid deadlock on release, harder
-	  Unlock after broadcasting and wait for the pad to be free before
-	  relocking the muxer, giving the input probe a chance to react to our
-	  broadcast.
-	  Improves the fix from
-	  https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/838.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1030>
+2023-09-04 12:57:26 +1000  Matthew Waters <matthew@centricular.com>
 
-2022-05-16 19:31:18 +0900  Shingo Kitagawa <shingogo@hotmail.co.jp>
+	* ext/qt6/RGBA.frag:
+	* ext/qt6/YUV_TRIPLANAR.frag:
+	* ext/qt6/gstqml6glsink.cc:
+	* ext/qt6/gstqsg6glnode.cc:
+	* ext/qt6/gstqsg6glnode.h:
+	* ext/qt6/gstqsg6material.cc:
+	* ext/qt6/gstqsg6material.h:
+	* ext/qt6/meson.build:
+	* ext/qt6/qt6glitem.cc:
+	* ext/qt6/resources.qrc:
+	* ext/qt6/vertex.vert:
+	* tests/examples/qt6/qmlsink/main.cpp:
+	  qml6/sink: add support for non-RGBA input
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5290>
 
-	* gst/wavparse/gstwavparse.c:
-	  wavparse: fix typo in debug message
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2425>
+2023-08-24 21:29:29 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-05-13 01:42:21 +0000  Thibault Saunier <tsaunier@igalia.com>
+	* docs/gst_plugins_cache.json:
+	  video: Fix ordering of video formats in GST_VIDEO_FORMATS_ALL_STR
+	  This now follows the algorithm again that is described in the
+	  documentation and implemented in gstreamer-rs.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5243>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	  rtpbin: Avoid holding lock GST_RTP_BIN_LOCK  when emitting pad-added
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2411>
+2023-08-24 11:32:51 +1000  Matthew Waters <matthew@centricular.com>
 
-2022-05-12 17:11:38 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* docs/gst_plugins_cache.json:
+	  video: add support for A420/A422/A444 16-bit formats
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5233>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Don't use tfdt for parsing subsequent trun boxes
-	  The timestamp in the tfdt refers to the first trun box and if there are
-	  multiple trun boxes then the distance between the first timestamps will
-	  grow.
-	  At some point this distance reaches a threshold and triggers the
-	  resetting of the first sample's timestamp of this trun box to be reset
-	  to the tfdt.
-	  This threshold is implemented for files where there is a jump in the
-	  timeline between fragments and where this can be detected via a jump
-	  between the end timestamp of the previous fragment and the tfdt of the
-	  next. This behaviour is preserved.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2409>
-
-2022-05-11 16:20:42 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+2023-08-23 16:33:16 +1000  Matthew Waters <matthew@centricular.com>
 
-	* ext/vpx/gstvpxenc.c:
-	* ext/vpx/gstvpxenc.h:
-	  vpxenc: enforce strictly increasing pts
-	  From vpx_codec_encode() documentation:
-	  "The presentation time stamp (PTS) MUST be strictly increasing."
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2405>
+	* docs/gst_plugins_cache.json:
+	  video: add support for 12-bit A420/A422/A444
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5226>
 
-2022-05-11 15:37:44 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+2023-08-21 21:02:01 +1000  Matthew Waters <matthew@centricular.com>
 
-	* ext/vpx/gstvpxenc.c:
-	  vpxenc: conver input pts to running time
-	  The input pts needs to be strictly increasing, see vpx_codec_encode() doc, so convert it to
-	  running time as we don't want to reset the encoder for each segment.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2405>
+	* docs/gst_plugins_cache.json:
+	  video: add support for 8-bit A422/A444
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5213>
 
-2022-05-11 15:18:42 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+2023-08-22 01:28:47 +0000  Nicolas Dufresne <nicolas@ndufresne.ca>
 
-	* ext/vpx/gstvpxenc.c:
-	  vpxenc: fix crash if encoder produces unmatching ts
-	  If for some reason the encoder produces frames with a pts higher than
-	  the input one, we were dropping all the video encoder frames and ended
-	  up crashing when trying to access the pts of a NULL pointer returned by
-	  gst_video_encoder_get_oldest_frame().
-	  I hit this scenario by feeding a decreasing timestamp to vp8enc which
-	  seem to confuse the encoder.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2405>
+	* sys/v4l2/gstv4l2object.c:
+	  gstv4l2object: fix TODO comment about HDR configure
+	  add following todo list
+	  - Missing capture (v4l2src) HDR10 configuration and/or reporting
+	  - The API is not capable of HDR to HDR conversion as controls are
+	  not specific to queues
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4403>
 
-2022-04-28 09:19:57 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-04-12 08:22:17 +0000  HuQian <qian.hu@mediatek.com>
 
 	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2videoenc.c:
-	  v4l2videoenc: Setup crop rectangle if needed
-	  Hantro H1 and Rockchip VEPU2 drivers will pad the width/height to a
-	  multiple of 16. In order to obtain the right JPEG size, the image needs
-	  to be cropped using the S_SELECTION API. This support is added as best
-	  effort since older drivers may emulate this by looking at the capture
-	  queue width/height.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2329>
+	  gstv4l2object: passing HDR10 information
+	  when playing some codec such as matroska with vp9 codec,
+	  demuxer will save information like video_mastering_display_info
+	  and video_content_light_level in caps that decoder need,
+	  v4l2videodecoder can use it by calling V4L2_CTRL_CLASS_COLORIMETRY
+	  ioctl.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4403>
 
-2022-05-05 20:36:04 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-22 09:57:14 +0800  Ming Qian <ming.qian@nxp.com>
 
-	* gst/isomp4/gstqtmux.c:
-	  mp4mux: Disable aggregator's default negotiation
-	  mp4mux can't negotiate caps with upstream/downstream and always outputs
-	  specific caps based on the input streams. This will always happen before
-	  it produces the first buffers.
-	  By having the default aggregator negotiation enabled the same caps
-	  would be pushed twice in the beginning, and again every time a
-	  reconfigure event is received.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2372>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: clear format lists if source change event is received
+	  If decoder notify a source change event when the capture format is
+	  changed, not the resolution changed.
+	  then gst_v4l2_object_acquire_format will retuen false due to
+	  unsupported format.
+	  we need to clear the format lists in the source change flow,
+	  and reenumerate format list
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5218>
 
-2022-05-05 20:24:57 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-21 14:37:23 +0200  Jonas K Danielsson <jonas.danielsson@spiideo.com>
 
-	* gst/flv/gstflvmux.c:
-	  flvmux: Disable aggregator's default negotiation
-	  flvmux can't negotiate caps with upstream/downstream and always outputs
-	  specific caps based on the input streams. This will always happen before
-	  it produces the first buffers.
-	  By having the default aggregator negotiation enabled the same caps
-	  would be pushed twice in the beginning, and again every time a
-	  reconfigure event is received.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2372>
+	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtp.c:
+	* gst/rtp/gstrtpelements.h:
+	* gst/rtp/gstrtppassthroughpay.c:
+	* gst/rtp/gstrtppassthroughpay.h:
+	* gst/rtp/meson.build:
+	* tests/check/elements/rtppassthrough.c:
+	* tests/check/meson.build:
+	  rtp: Add rtppassthroughpay element
+	  This elements pass RTP packets along unchanged and appear as a RTP
+	  payloader element.
+	  This is useful, for example when using the gstreamer-rtsp-server
+	  library, in the case where you are receiving RTP packets from a
+	  different source and want to serve them over RTSP. Since the
+	  gst-rtsp-server library expect the element marked as payX to be a RTP
+	  payloader element and assumes certain properties are available.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5204>
+
+2023-07-25 15:14:11 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-05-03 17:27:32 +1000  Matthew Waters <matthew@centricular.com>
+	* sys/v4l2/gstv4l2allocator.c:
+	  v4l2: allocator: Don't close foreign dmabuf
+	  Imported dmabuf are not being duped, so they should never be closed. Instead,
+	  we ensure their live time by having strong reference on their original
+	  buffer. This should fix potential flickering due to dmabuf being closed
+	  too early.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5101>
 
-	* gst/wavparse/gstwavparse.c:
-	  wavparse: ensure that any pending segment is sent before an EOS event is sent
-	  Specifically fixes seqnum handling when an aggregator-based element
-	  (audiomixer et al) is downstream and a seek is performed that
-	  immediately causes an EOS from wavparse.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2356>
+2023-08-03 14:46:56 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-04-29 23:33:47 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Fix hang when splitting buffer
+	  Now that we can split GStreamer buffers over multiple v4l2 buffer, we may
+	  endup waiting for these buffers to be processed. Avoid waiting for any of
+	  the parts being processed. As a side effect, the pool will now try to
+	  grow if the number of buffers is not sufficient, and will fail
+	  otherwise.
+	  This fixes a hang if the very first frame did not fit. In this case, the
+	  driver will retrain that buffer until the capture is setup, but
+	  GStreamer won't setup the capture until process() function have
+	  returned.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5100
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5143>
+
+2023-08-16 15:37:51 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Free CNAME/SSRC mappings on finalize and PAUSED->READY
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2336>
+	* docs/gst_plugins_cache.json:
+	* gst/flv/gstflvmux.c:
+	* gst/flv/gstflvmux.h:
+	  flvmux: add 'enforce-increasing-timestamps' property
+	  The hack enforcing strictly increasing timestamps was, according to the
+	  code comments, because librtmp was confused with backwards timestamps.
+	  rtmp2sink is not using librtmp as rtmpsink did, so this is no longer
+	  required.
+	  Also changing the timestamps is causing audio glitches when streaming to
+	  Youtube.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5212>
+
+2023-07-20 19:13:36 +0200  Jan Alexander Steffens (heftig) <heftig@archlinux.org>
 
-2022-04-29 23:13:15 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/qt/meson.build:
+	  qt: Unbreak build with qt-egl enabled but viv_fb missing
+	  Avoids an error message when the feature is explicitly enabled:
+	  ERROR: Feature qt-egl cannot be enabled: gstreamer-gl-viv_fb-1.0 is required
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5083>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpmanager: Refactor RTCP packet loops to fix control flow
-	  Mixing C loops with switch statements is a bad idea as break has a
-	  different meaning in both. Breaking inside the switch statements wrongly
-	  caused further loop iterations.
-	  Instead use goto to get out of the loop and continue to do another loop
-	  iteration, and never ever use break except for the end of a case.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2336>
+2023-08-11 13:24:49 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-04-28 00:58:30 +0900  Seungha Yang <seungha@centricular.com>
+	* gst/rtp/gstrtpgstdepay.c:
+	  rtpgstpay: Enable hdrext aggregation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4979>
 
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Initialize variables
-	  Avoid use of uninitialized variable
-	  Fixing MSVC warning
-	  gstrtpjitterbuffer.c(4733) : warning C4700: uninitialized local variable 'have_sdes' used
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2315>
+2023-07-30 10:48:12 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-04-28 10:49:55 +0200  Edward Hervey <edward@centricular.com>
+	* gst/rtp/gstrtpvp8depay.c:
+	* gst/rtp/gstrtpvp9depay.c:
+	  rtp/vp8depay+vp9depay: Enable hdrext aggregation for VP8 and VP9
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4979>
 
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	  mssdemux2: Don't expose/use streams we can't handle yet
-	  Avoids issues further down
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2319>
+2023-07-30 09:35:53 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-2022-04-28 10:46:34 +0200  Edward Hervey <edward@centricular.com>
+	* gst/rtp/gstrtph264depay.c:
+	* gst/rtp/gstrtph265depay.c:
+	  rtp/h264depay+h265depay: Enable hdrext aggregation for H264 and H265
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4979>
 
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	* ext/adaptivedemux2/mss/gstmssmanifest.c:
-	* ext/adaptivedemux2/mss/gstmssmanifest.h:
-	  mssdemux2: Ensure stream/track uniqueness
-	  If there is more than one track of the same type (say audio), we would end up
-	  creating several stream/types with the same name.
-	  Instead use the MSS stream name property to make them unique
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2319>
+2023-08-11 11:59:42 +0300  Vivia Nikolaidou <vivia@ahiru.eu>
 
-2022-04-27 12:24:23 +0900  dongil.park <dongil.park@lge.com>
+	* gst/deinterlace/tvtime/vfir.c:
+	  deinterlace: Fix vfir 16-bit orc calculations
+	  memcpy works in bytes, but orc works in items, so given that the size
+	  arguments is in bytes, we need to divide by the pixel stride.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5172>
 
-	* gst/wavparse/gstwavparse.c:
-	  wavparse: Unset DISCONT buffer flag for divided into multiple buffers in push mode
-	  In push mode (streaming), if the received chunk buffer size from _chain is bigger
-	  than output buffer size, the flags of the divided-buffers are propagated to the
-	  DISCONT flag from first received chunk buffer. This unexpected buffers contained DISCONT
-	  flags are abnormally transformed when changing the sampling rate by audioresample element.
-	  So unset unnecessary DISCONT flag before pad_push().
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2305>
+2023-08-10 11:38:32 +0300  Vivia Nikolaidou <vivia@ahiru.eu>
 
-2022-04-26 22:17:51 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* gst/deinterlace/tvtime/greedyh.c:
+	  deinterlace: Fix greedyh crash for alternate-field interlacing
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2645
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5172>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: add the reference timestamp meta in more situations
-	  Previously, we only added it when actually performing synchronization
-	  based on the NTP time.
-	  The information can be useful downstream in other situations too, and
-	  we can compute a NTP time as soon as we get a sender report with the
-	  relevant information.
-	  Co-authored-by: Mathieu Duponchelle <mathieu@centricular.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2252>
+2023-08-09 10:56:59 +0200  Stéphane Cerveau <scerveau@igalia.com>
 
-2022-04-20 17:35:29 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* gst/isomp4/gstqtmux-doc.c:
+	  isomp4: update isml documentation
+	  Closing #2893
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5165>
 
-	* gst/rtp/gstrtpgstpay.c:
-	* gst/rtp/gstrtpgstpay.h:
-	  rtpgstpay: Don't push packets before the first input buffer is received
-	  It's not possible to create a valid RTP timestamp for them, which would
-	  cause a potentially very big RTP timestamp discontinuity between those
-	  first packets (created from initial events) and the packet based on the
-	  first input buffer.
-	  As a side-effect, also simplify the packet aggregation code a bit and
-	  work with only a single level of buffer lists.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1157
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2250>
-
-2022-04-22 12:19:03 +0200  Havard Graff <havard@pexip.com>
+2023-08-07 22:22:14 -0300  L. E. Segovia <amy@centricular.com>
 
-	* gst/rtpmanager/rtptwcc.c:
-	  rtptwcc: don't map the buffer twice
-	  ...and use the pt extracted rather than the one from RTPPacketInfo
-	  when logging.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2271>
+	* ext/vpx/meson.build:
+	  subprojects: Add libvpx wrap
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5105>
 
-2022-04-22 02:41:16 +0000  Thibault Saunier <tsaunier@igalia.com>
+2022-11-11 15:10:02 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* gst/rtpmanager/rtpsession.c:
-	  rtpsession: Emit "notify::stats" when we update stats from RR or SR
-	  Sensibily optimizing caching the pspecs and using them directly
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2266>
+	* docs/gst_plugins_cache.json:
+	  video: Add Mediatek 10bit formats
+	  These 10bit formats are identical to NV12_16L32S, but 64bytes of data is being
+	  prefixed with 16bytes data with four pixels of lower 2bits per byte. For
+	  MT2110T, the lower two bits set so each bytes contains a column of 4 pixels,
+	  also describe as tiled lower 2 bits. MT2110T has been chosen as a name to match
+	  the vendor chosen name. This format is unlikely to exist for other vendors.
+	  For MT2110R, the 2 low bits are in raster order.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3444>
+
+2023-08-03 17:46:57 +1000  Jan Schmidt <jan@centricular.com>
+
+	* sys/osxaudio/gstosxaudiosink.c:
+	* sys/osxaudio/gstosxaudiosrc.c:
+	* sys/osxaudio/gstosxcoreaudio.c:
+	* sys/osxaudio/gstosxcoreaudio.h:
+	  osxaudio: Interpolate clock by counting elapsed time since render calls
+	  When advancing the ringbuffer, store the processed CoreAudio sample
+	  time, then interpolate the clock in the _get_delay() calls to smooth
+	  the clock. CoreAudio's "latency" report is always a constant and
+	  otherwise leads to the clock generating a latency-time staircase.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5140>
 
-2022-04-23 01:57:53 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+2023-07-26 00:21:24 +1000  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtp/gstrtpredenc.c:
-	* gst/rtp/gstrtpredenc.h:
-	  rtpredenc: quieten warning about ignoring header extensions
-	  Turn it into a FIXME, and only log once
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2279>
+	* sys/osxaudio/gstosxcoreaudio.c:
+	* sys/osxaudio/gstosxcoreaudiocommon.c:
+	* sys/osxaudio/gstosxcoreaudiohal.c:
+	  osxaudio: Share debug category in the internal coreaudio object
+	  Make the internal coreaudio object output debug to the same
+	  debug category by making it shared between code units.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5140>
 
-2021-02-01 10:36:42 +0100  Havard Graff <havard.graff@gmail.com>
+2023-07-26 00:21:18 +1000  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxsend.c:
-	* tests/check/elements/rtprtx.c:
-	  rtprtxsend: mark RTX buffers with GST_RTP_BUFFER_FLAG_RETRANSMISSION
-	  It is useful for elements downstream from rtxsend to know if the RTP
-	  buffer they are dealing with is an RTX buffer or not.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2272>
+	* sys/osxaudio/gstosxaudioringbuffer.c:
+	* sys/osxaudio/gstosxcoreaudio.c:
+	* sys/osxaudio/gstosxcoreaudio.h:
+	* sys/osxaudio/gstosxcoreaudiohal.c:
+	  osxaudio: Attempt to configure the segment size in CoreAudio
+	  Set the BufferFrame size in CoreAudio so it will deliver data
+	  in ringbuffer segment units when recording. Otherwise, CoreAudio
+	  will provide data in whatever granularity it wants, with no
+	  relationship to the requested latency-time.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5140>
 
-2022-04-19 18:40:31 -0400  Tristan Matthews <tmatth@videolan.org>
+2023-07-26 00:21:08 +1000  Jan Schmidt <jan@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/isomp4/gstqtmux.c:
-	  mp4mux: fix spelling
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2241>
+	* sys/osxaudio/gstosxaudiosrc.c:
+	  osxaudiosrc: Set sample timestamps
+	  Set the timestamp on output buffers based on the incoming sample
+	  times from Core Audio
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5140>
 
-2022-01-21 14:21:18 +0100  Jonas Bonn <jonas@norrbonn.se>
+2023-08-05 17:45:17 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/udp/gstmultiudpsink.c:
-	  multiudpsink: allow binding to IPv6 address
-	  When the sink is configured to create sockets with an explicit bind
-	  address, then the created socket gets set to the udp_socket field
-	  irregardless of whether the bind address indicated that the socket
-	  family should be IPv4 or IPv6.  When binding to an IPv6 address, this
-	  results in the following error:
-	  gstmultiudpsink.c:1285:gst_multiudpsink_configure_client:<rtcpsink>
-	  error: Invalid address family (got 10)
-	  This patch adds a check of the address family being bound to and sets
-	  the created socket to used_socket or used_socket_v6, accordingly.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1551>
-
-2022-04-18 18:20:00 +0900  Camilo Celis Guzman <camilo@pexip.com>
-
-	* tests/check/elements/rtphdrextsdes.c:
-	  rtphdrextsdes: fixup test trying to g_free a local variable
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2235>
-
-2022-04-21 11:47:55 +0200  Edward Hervey <edward@centricular.com>
+	* ext/soup/gstsouphttpsrc.c:
+	  soup: use GST_PARAM_DOC_SHOW_DEFAULT for libsoup2-specific properties
+	  Otherwise the value in the gst_plugins_cache.json will vary depending
+	  on the libsoup version picked up at runtime.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5090>
 
-	* ext/adaptivedemux2/hls/m3u8.c:
-	  hls/m3u8: Fix starting segment for live playlist
-	  RFC 8216 6.3.3 "Playing the Media Playlist File" : states that for live media
-	  playlists "the client SHOULD NOT choose a segment that starts less than three
-	  target durations from the end of the Playlist file"
-	  This is an off-by-one error. Since we are looking for the "index" of the
-	  segment, we need to subtract 1 from the searched position.
-	  Ex: For a playlist with 12 entries, we want to start playback on the 9th segment
-	  ... which is at index 8.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2259>
-
-2022-04-20 14:50:35 +0200  Edward Hervey <edward@centricular.com>
+2023-08-05 17:27:31 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
-	  hls: Relax webvtt checks
-	  If no hour field is present (which is allowed), the remaining data can be less
-	  than 15 character.
-	  Fix time translation failures if the hour field wasn't present
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2248>
+	* docs/gst_plugins_cache.json:
+	* gst/flv/gstflvmux.c:
+	  flvmux: use version template in metadata creator properties
+	  Avoids documentation churn when the version changes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5090>
 
-2022-04-20 10:53:16 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-05 17:27:31 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpfunnel.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	* gst/rtpmanager/gstrtpsession.c:
-	* gst/rtpmanager/gstrtputils.c:
-	* gst/rtpmanager/gstrtputils.h:
-	* gst/rtpmanager/meson.build:
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtptwcc.c:
-	  rtpmanager: Move some duplicated constant and helper function to a single place
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* docs/gst_plugins_cache.json:
+	* ext/soup/gstsouphttpsrc.c:
+	  souphttpsrc: use version template in user-agent property
+	  Avoids documentation churn when the version changes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5090>
 
-2022-04-18 16:22:50 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-05 17:27:31 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpbin/rtpjitterbuffer: Don't parse RTCP SRs twice unless needed
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* docs/gst_plugins_cache.json:
+	* ext/shout2/gstshout2.c:
+	  shout2send: use version template in user-agent property
+	  Avoids documentation churn when the version changes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5090>
 
-2022-04-18 11:50:48 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-05 17:27:31 +0100  Tim-Philipp Müller <tim@centricular.com>
 
 	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpjitterbuffer: Add property to throttle handling of RTCP SR / NTP-64 syncing
-	  This proxies the "rtcp-sync-interval" property of rtpbin.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* gst-libs/gst/glib-compat-private.h:
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: use version template in user-agent property
+	  Avoids documentation churn when the version changes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5090>
 
-2022-04-11 19:14:43 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-07 14:18:21 +0800  Wang Chuan <ouchuanm@outlook.com>
 
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtpsession.h:
-	  rtpsession: Handle RTCP-SR-REQ (RFC6051) RTCP feedback message
-	  This causes an RTCP SR to be sent at the earliest possible time.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  gstadaptivedemux: fix memory leak
+	  GstQuery leaks when using invalid url
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5154>
 
-2022-04-11 19:25:43 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-04 18:26:35 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpbin/rtpjitterbuffer: Allow syncing to an SR without CNAME if the CNAME is already known
-	  The RTCP SR packet might be without SDES in case of a reduced-size RTCP
-	  packet. For syncing purposes the CNAME is needed but it might be known
-	  already from an earlier RTCP packet or out of band, via the SDP for
-	  example.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* ext/qt/gstqsgmaterial.cc:
+	* ext/qt/gstqsgmaterial.h:
+	  qmlgl: Can't use frame info if we don't have a frame
+	  Fixes segfault accessing vinfo in: GST_VIDEO_FRAME_N_PLANES (&this->v_frame)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5148>
 
-2022-04-07 18:59:07 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-04 15:11:05 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	  rtpbin/jitterbuffer: Use inband 64-bit NTP timestamps according to RFC6051 for faster synchronization
-	  When signalled via the caps that the header extension is used, it will
-	  be read and used in the same way as the RTP/NTP time mapping from RTCP
-	  SRs.
-	  If the CNAME of the stream's SSRC is provided out of band via e.g. the
-	  SDP then this allows streams to be synchronized immediately on the first
-	  packet instead of having to wait for the first RTCP SR to arrive.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/383
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
-
-2022-04-09 11:00:52 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/qt/meson.build:
+	* ext/qt6/meson.build:
+	  meson: Fix searching of qt5/qt6 tools with qmake
+	  If the pkg-config files are broken, we want to ensure that qmake is
+	  used. This can easily happen on macOS with the official Qt binaries.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5147>
 
-	* gst/rtpmanager/gstrtpsession.c:
-	  rtpsession: Only add send latency to the running time if it is actually known
-	  Otherwise we can't know the running time yet if rtcp-sync-send-time is
-	  set, and have to wait until the latency is known later.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+2023-08-04 13:02:21 +1000  Matthew Waters <matthew@centricular.com>
 
-2022-04-06 15:39:14 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* ext/qt/gstqsgmaterial.cc:
+	* ext/qt/gstqsgmaterial.h:
+	* ext/qt/qtitem.cc:
+	  qml: handle multiple items with different input formats
+	  Issue was that Qt was caching multiple different shadersbased on a static
+	  variable location.  This static variable location was the same no matter
+	  the input video format and so if an item had an input video format of
+	  RGB and another of RGBA, they would eventually end up using the same
+	  GL shader leading to incorrect colours.
+	  Fix by providing different static variable locations for each of the
+	  different shaders that will be produced.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5145>
+
+2023-07-20 17:04:22 -0400  Charlie Blevins <charlie.blevins@flocksafety.com>
 
-	* gst/rtpmanager/gstrtpsession.c:
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtpsession.h:
-	* gst/rtpmanager/rtpstats.h:
-	  rtpsession: Update 64-bit NTP header extensions with the actual NTP time in senders
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* gst/rtpmanager/gstrtpjitterbuffer.c:
+	* tests/check/elements/rtpjitterbuffer.c:
+	  rtpjitterbuffer: Allow earlier reference-timestamp-meta
+	  Allow reference-timestamp-meta to be added earlier if an RTCP sender
+	  report is sent before the first RTP packet.
+	  Fixes #2843
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5084>
 
-2022-04-05 20:05:57 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-08-02 21:17:26 +1000  Matthew Waters <matthew@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtphdrext-ntp.c:
-	* gst/rtpmanager/gstrtphdrext-ntp.h:
-	* gst/rtpmanager/gstrtpmanager.c:
-	* gst/rtpmanager/meson.build:
-	  rtpmanager: Add header extension implementation for the 64-bit RFC6051 NTP header extension
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2132>
+	* ext/qt/gstqsgmaterial.cc:
+	  qml/gl: fix array definition
+	  Some implementations require the [N] to suffixed to the variable name.
+	  Error message example: 'syntax error: Array size must appear after
+	  variable name'
+	  Follow up with https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5123
+	  of https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5119
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5137>
 
-2022-03-25 10:18:34 -0400  Xavier Claessens <xavier.claessens@collabora.com>
+2023-07-25 12:19:22 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* meson.build:
-	  Always define ENABLE_NLS
-	  GLib guarantees libintl API is always available, provided by
-	  proxy-libintl as last resort. GLib itself unconditionally define
-	  ENABLE_NLS.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2028>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Keep processing bitstream buffer
+	  Bitstream buffers may no fit a single v4l2 buffer, following spec
+	  recommendation, keep processing the buffer until all the data has been
+	  queued.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5100>
 
-2022-03-25 10:20:24 -0400  Xavier Claessens <xavier.claessens@collabora.com>
+2023-07-25 12:19:22 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* gst-libs/gst/gettext.h:
-	* gst-libs/gst/gst-i18n-plugin.h:
-	  Delete unused i18n headers
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2028>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Fix buffer resize asserstion
+	  When we fill a bitstream buffer the buffer might be too small to hold
+	  the entire frame. Only resize to the filled size, preventing the
+	  following assertion to happen.
+	  gst_buffer_resize_range: assertion 'bufmax >= bufoffs + offset + size' failed
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5100>
 
-2022-03-25 09:59:23 -0400  Xavier Claessens <xavier.claessens@collabora.com>
+2023-07-20 21:12:55 +1000  Matthew Waters <matthew@centricular.com>
 
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* docs/gst_plugins_cache.json:
+	* ext/qt/gstqsgmaterial.cc:
+	* ext/qt/gstqsgmaterial.h:
+	* ext/qt/gstqsgtexture.cc:
+	* ext/qt/gstqtoverlay.cc:
+	* ext/qt/gstqtsink.cc:
+	* ext/qt/meson.build:
+	* ext/qt/qtitem.cc:
+	* ext/qt/qtwindow.cc:
+	* tests/examples/qt/qmloverlay/main.cpp:
+	* tests/examples/qt/qmlsink/main.cpp:
+	  qml: add support for non-RGBA formats as input format
+	  Currently supported are RGBA, BGRA and YV12
+	  Output is still RGBA textures
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5119>
+
+2023-07-27 12:05:07 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
 	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	* ext/flac/gstflacdec.c:
-	* ext/flac/gstflacelement.c:
-	* ext/jack/gstjackaudiosink.c:
-	* ext/jack/gstjackaudiosrc.c:
-	* ext/jpeg/gstjpegdec.c:
-	* ext/lame/gstlamemp3enc.c:
-	* ext/lame/plugin.c:
-	* ext/libpng/gstpngdec.c:
-	* ext/pulse/gstpulseelement.c:
-	* ext/pulse/pulsesink.c:
-	* ext/shout2/gstshout2.c:
-	* ext/soup/gstsoup.c:
-	* ext/soup/gstsoupelement.c:
-	* ext/soup/gstsouphttpsrc.c:
-	* ext/twolame/gsttwolamemp2enc.c:
-	* ext/wavpack/gstwavpackelement.c:
-	* gst/apetag/gstapedemux.c:
-	* gst/avi/gstavidemux.c:
-	* gst/avi/gstavielement.c:
-	* gst/avi/gstavimux.c:
-	* gst/icydemux/gsticydemux.c:
-	* gst/id3demux/gstid3demux.c:
-	* gst/isomp4/gstisomp4element.c:
-	* gst/isomp4/isomp4-plugin.c:
+	  adaptivedemux2: Remove API lock
+	  The various fields this was protecting were for the legacy design.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5113>
+
+2023-07-12 12:37:34 +0200  Alicia Boya García <aboya@igalia.com>
+
 	* gst/isomp4/qtdemux.c:
-	* gst/multifile/gstsplitmuxsrc.c:
-	* gst/rtsp/gstrtspelement.c:
-	* gst/rtsp/gstrtspsrc.c:
-	* gst/wavparse/gstwavparse.c:
-	* sys/oss/gstossaudio.c:
-	* sys/oss/gstossaudioelement.c:
-	* sys/oss/gstosshelper.c:
-	* sys/oss/gstosssink.c:
-	* sys/oss/gstosssrc.c:
-	* sys/oss4/oss4-audio.c:
-	* sys/oss4/oss4-sink.c:
-	* sys/oss4/oss4-source.c:
-	* sys/osxaudio/gstosxaudioringbuffer.c:
-	* sys/rpicamsrc/gstrpicamsrcdeviceprovider.c:
-	* sys/v4l2/gstv4l2.c:
-	* sys/v4l2/gstv4l2bufferpool.c:
-	* sys/v4l2/gstv4l2element.c:
-	* sys/v4l2/gstv4l2fwhtenc.c:
-	* sys/v4l2/gstv4l2h263enc.c:
-	* sys/v4l2/gstv4l2h264enc.c:
-	* sys/v4l2/gstv4l2h265enc.c:
-	* sys/v4l2/gstv4l2jpegenc.c:
-	* sys/v4l2/gstv4l2mpeg4enc.c:
-	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2radio.c:
-	* sys/v4l2/gstv4l2sink.c:
-	* sys/v4l2/gstv4l2src.c:
-	* sys/v4l2/gstv4l2transform.c:
-	* sys/v4l2/gstv4l2videodec.c:
-	* sys/v4l2/gstv4l2videoenc.c:
-	* sys/v4l2/gstv4l2vp8enc.c:
-	* sys/v4l2/gstv4l2vp9enc.c:
-	* sys/v4l2/v4l2_calls.c:
-	* sys/ximage/gstximagesrc.c:
-	  Replace gst-i18n-*.h with gi18n-lib.h
-	  GLib guarantees libintl is always present, using proxy-libintl as
-	  last resort. There is no need to mock gettex API any more.
-	  This fix static build on Windows because G_INTL_STATIC_COMPILATION must
-	  be defined before including libintl.h, and glib does it for us as part
-	  as including glib.h.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2028>
+	  qtdemux: Fix premature EOS when some files are played in push mode
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2771
+	  This EOS branch exists so that if a seek with a stop is made, qtdemux
+	  stops accepting bytes from the sink after the entire requested playback
+	  range is demuxed, as otherwise we could keep download content that is
+	  not being used.
+	  This patch fixes two flaws that were present in that EOS check:
+	  1) A comparison was made between track time and movie time without conversion.
+	  This made the check trigger early in files with edit lists. This patch fixes
+	  this by converting the track PTS to movie PTS (stream time) for the check.
+	  2) To avoid sending a EOS prematurely when the segment stop is within a GOP and
+	  B-frames are present, the check for EOS should only be done for keyframes. I
+	  gather this was already the intention with the existing code, but because it
+	  used `stream->on_keyframe` instead of the local variable `keyframe` the old
+	  code was checking if the *previous* frame was a keyframe.
+	  It's interesting to note that these two flaws in the old code mask each other
+	  in most cases: the track PTS will have reached the movie end PTS, but EOS would
+	  only be sent if the previous frame was a keyframe. A simple case where they
+	  wouldn't mask each other, reproducing the bug, is a sequence of 3 frame GOPs
+	  with structure I-B-P.
+	  The following validateflow tests have been added to future-proof the
+	  fix:
+	  * validate.test.mp4.qtdemux_ibpibp_non_frag_pull.default
+	  * validate.test.mp4.qtdemux_ibpibp_non_frag_push.default
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5021>
+
+2023-07-25 15:15:58 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-2022-04-19 09:52:51 +0100  Tim-Philipp Müller <tim@centricular.com>
+	* gst/videofilter/gstvideoflip.c:
+	  videoflip: fix concurrent access when modifying the tag list
+	  We were checking if the tag list is writable, but it may actually be
+	  shared through the same event (tee upstream or multiple consumers).
+	  Fix a bug where multiple branches have a videoflip element checking the
+	  taglist. The first one was changing the orientation back to rotate-0
+	  which was resetting the other instances.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5097>
 
-	* tests/check/elements/dash_mpd.c:
-	  tests: dash_mpd: fix linker issues with non-optimizing compilers
-	  undefined reference to `download_request_take_buffer'
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2117#note_1344646
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2228>
+2023-07-21 12:48:08 +0200  Xabier Rodriguez Calvar <calvaris@igalia.com>
 
-2021-11-12 20:13:10 +0100  Ruben Gonzalez <rgonzalez@fluendo.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: attach cbcs crypt info at the right moment
+	  Before it was always added but that can cause issues when the stream begins
+	  unencrypted.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5085>
 
-	* docs/gst_plugins_cache.json:
-	  gst_plugin_load_file: force plugin reload if diff filename
-	  If a file includes a new version of a plugin that exits in the
-	  registry, the output of gst-inspect is incorrect. The output has the
-	  correct version but incorrect filename, and element description.
-	  This seems to have also fixed some documentation issues.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1344>
+2023-07-11 17:00:57 +0200  Bastien Nocera <hadess@hadess.net>
 
-2022-03-11 17:11:50 +0100  Edward Hervey <edward@centricular.com>
+	* ext/gtk/gtkgstbasewidget.c:
+	  gtk: Fix critical caused by pointer movement when stream is getting ready
+	  This check fixes a critical warning that can happen when a pointer motion
+	  happens and the video doesn't have its width/height information available.
+	  GStreamer-Video-CRITICAL **: gst_video_center_rect: assertion 'src->h != 0' failed
+	  #0  g_logv (log_domain=0x7ffff705e176 "GStreamer-Video", log_level=G_LOG_LEVEL_CRITICAL, format=<optimized out>, args=<optimized out>) at ../../../../Projects/jhbuild/glib/glib/gmessages.c:1422
+	  #1  0x00007ffff7e1a81d in g_log (log_domain=<optimized out>, log_level=log_level@entry=G_LOG_LEVEL_CRITICAL, format=format@entry=0x7ffff7e77a9d "%s: assertion '%s' failed") at ../../../../Projects/jhbuild/glib/glib/gmessages.c:1460
+	  #2  0x00007ffff7e1b749 in g_return_if_fail_warning (log_domain=<optimized out>, pretty_function=<optimized out>, expression=<optimized out>) at ../../../../Projects/jhbuild/glib/glib/gmessages.c:2930
+	  #3  0x00007ffff701d90b in gst_video_sink_center_rect (src=..., dst=..., result=result@entry=0x7fffffffc6d0, scaling=scaling@entry=1) at ../../../../Projects/jhbuild/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/gstvideosink.c:105
+	  #4  0x00007fffe5652dbb in _fit_stream_to_allocated_size (result=0x7fffffffc6d0, allocation=0x7fffffffc6c0, base_widget=0x9396f0) at ../../../../Projects/jhbuild/gstreamer/subprojects/gst-plugins-good/ext/gtk/gtkgstbasewidget.c:326
+	  #5  gtk_gst_base_widget_display_size_to_stream_size (base_widget=base_widget@entry=0x9396f0, x=1207.7109375, y=811.84765625, stream_x=stream_x@entry=0x7fffffffc720, stream_y=stream_y@entry=0x7fffffffc728) at ../../../../Projects/jhbuild/gstreamer/subprojects/gst-plugins-good/ext/gtk/gtkgstbasewidget.c:344
+	  #6  0x00007fffe5651a4b in gst_gtk_base_sink_navigation_send_event (navigation=0x5ff990, event=0x178a730) at ../../../../Projects/jhbuild/gstreamer/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.c:340
+	  #7  0x00007fffe5652432 in gtk_gst_base_widget_motion_event (widget=<optimized out>, event=event@entry=0x1f14b60) at ../../../../Projects/jhbuild/gstreamer/subprojects/gst-plugins-good/ext/gtk/gtkgstbasewidget.c:404
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5049>
+
+2023-07-05 18:46:25 -0600  Olivier Crête <olivier.crete@collabora.com>
 
-	* docs/gst_plugins_cache.json:
-	* docs/meson.build:
-	* ext/adaptivedemux2/dash/gstdash_debug.h:
-	* ext/adaptivedemux2/dash/gstdashdemux.c:
-	* ext/adaptivedemux2/dash/gstdashdemux.h:
-	* ext/adaptivedemux2/dash/gstmpdadaptationsetnode.c:
-	* ext/adaptivedemux2/dash/gstmpdadaptationsetnode.h:
-	* ext/adaptivedemux2/dash/gstmpdbaseurlnode.c:
-	* ext/adaptivedemux2/dash/gstmpdbaseurlnode.h:
-	* ext/adaptivedemux2/dash/gstmpdclient.c:
-	* ext/adaptivedemux2/dash/gstmpdclient.h:
-	* ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.c:
-	* ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.h:
-	* ext/adaptivedemux2/dash/gstmpddescriptortypenode.c:
-	* ext/adaptivedemux2/dash/gstmpddescriptortypenode.h:
-	* ext/adaptivedemux2/dash/gstmpdhelper.c:
-	* ext/adaptivedemux2/dash/gstmpdhelper.h:
-	* ext/adaptivedemux2/dash/gstmpdlocationnode.c:
-	* ext/adaptivedemux2/dash/gstmpdlocationnode.h:
-	* ext/adaptivedemux2/dash/gstmpdmetricsnode.c:
-	* ext/adaptivedemux2/dash/gstmpdmetricsnode.h:
-	* ext/adaptivedemux2/dash/gstmpdmetricsrangenode.c:
-	* ext/adaptivedemux2/dash/gstmpdmetricsrangenode.h:
-	* ext/adaptivedemux2/dash/gstmpdmultsegmentbasenode.c:
-	* ext/adaptivedemux2/dash/gstmpdmultsegmentbasenode.h:
-	* ext/adaptivedemux2/dash/gstmpdnode.c:
-	* ext/adaptivedemux2/dash/gstmpdnode.h:
-	* ext/adaptivedemux2/dash/gstmpdparser.c:
-	* ext/adaptivedemux2/dash/gstmpdparser.h:
-	* ext/adaptivedemux2/dash/gstmpdperiodnode.c:
-	* ext/adaptivedemux2/dash/gstmpdperiodnode.h:
-	* ext/adaptivedemux2/dash/gstmpdprograminformationnode.c:
-	* ext/adaptivedemux2/dash/gstmpdprograminformationnode.h:
-	* ext/adaptivedemux2/dash/gstmpdreportingnode.c:
-	* ext/adaptivedemux2/dash/gstmpdreportingnode.h:
-	* ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.c:
-	* ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.h:
-	* ext/adaptivedemux2/dash/gstmpdrepresentationnode.c:
-	* ext/adaptivedemux2/dash/gstmpdrepresentationnode.h:
-	* ext/adaptivedemux2/dash/gstmpdrootnode.c:
-	* ext/adaptivedemux2/dash/gstmpdrootnode.h:
-	* ext/adaptivedemux2/dash/gstmpdsegmentbasenode.c:
-	* ext/adaptivedemux2/dash/gstmpdsegmentbasenode.h:
-	* ext/adaptivedemux2/dash/gstmpdsegmentlistnode.c:
-	* ext/adaptivedemux2/dash/gstmpdsegmentlistnode.h:
-	* ext/adaptivedemux2/dash/gstmpdsegmenttemplatenode.c:
-	* ext/adaptivedemux2/dash/gstmpdsegmenttemplatenode.h:
-	* ext/adaptivedemux2/dash/gstmpdsegmenttimelinenode.c:
-	* ext/adaptivedemux2/dash/gstmpdsegmenttimelinenode.h:
-	* ext/adaptivedemux2/dash/gstmpdsegmenturlnode.c:
-	* ext/adaptivedemux2/dash/gstmpdsegmenturlnode.h:
-	* ext/adaptivedemux2/dash/gstmpdsnode.c:
-	* ext/adaptivedemux2/dash/gstmpdsnode.h:
-	* ext/adaptivedemux2/dash/gstmpdsubrepresentationnode.c:
-	* ext/adaptivedemux2/dash/gstmpdsubrepresentationnode.h:
-	* ext/adaptivedemux2/dash/gstmpdsubsetnode.c:
-	* ext/adaptivedemux2/dash/gstmpdsubsetnode.h:
-	* ext/adaptivedemux2/dash/gstmpdurltypenode.c:
-	* ext/adaptivedemux2/dash/gstmpdurltypenode.h:
-	* ext/adaptivedemux2/dash/gstmpdutctimingnode.c:
-	* ext/adaptivedemux2/dash/gstmpdutctimingnode.h:
-	* ext/adaptivedemux2/dash/gstxmlhelper.c:
-	* ext/adaptivedemux2/dash/gstxmlhelper.h:
-	* ext/adaptivedemux2/downloadhelper.c:
-	* ext/adaptivedemux2/downloadhelper.h:
-	* ext/adaptivedemux2/downloadrequest.c:
-	* ext/adaptivedemux2/downloadrequest.h:
-	* ext/adaptivedemux2/gstadaptivedemux-period.c:
-	* ext/adaptivedemux2/gstadaptivedemux-private.h:
-	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
-	* ext/adaptivedemux2/gstadaptivedemux-track.c:
-	* ext/adaptivedemux2/gstadaptivedemux.c:
-	* ext/adaptivedemux2/gstadaptivedemux.h:
-	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
-	* ext/adaptivedemux2/gstadaptivedemuxutils.h:
-	* ext/adaptivedemux2/gstisoff.c:
-	* ext/adaptivedemux2/gstisoff.h:
-	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.c:
-	* ext/adaptivedemux2/hls/gsthlsdemux.h:
-	* ext/adaptivedemux2/hls/gsthlselement.c:
-	* ext/adaptivedemux2/hls/gsthlselements.h:
-	* ext/adaptivedemux2/hls/m3u8.c:
-	* ext/adaptivedemux2/hls/m3u8.h:
-	* ext/adaptivedemux2/hls/meson.build:
-	* ext/adaptivedemux2/meson.build:
-	* ext/adaptivedemux2/mss/gstmssdemux.c:
-	* ext/adaptivedemux2/mss/gstmssdemux.h:
-	* ext/adaptivedemux2/mss/gstmssfragmentparser.c:
-	* ext/adaptivedemux2/mss/gstmssfragmentparser.h:
-	* ext/adaptivedemux2/mss/gstmssmanifest.c:
-	* ext/adaptivedemux2/mss/gstmssmanifest.h:
-	* ext/adaptivedemux2/plugin.c:
-	* ext/meson.build:
-	* ext/soup/gstsouploader.c:
-	* ext/soup/gstsouploader.h:
+	* scripts/gen-changelog.py:
+	  gst-omx: Retire the whole package
+	  The OpenMAX standard is long dead and even the Raspberry Pi OS
+	  no longer supports it.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4976>
+
+2023-07-14 08:28:10 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+
+	* ext/soup/meson.build:
 	* meson_options.txt:
-	* tests/check/elements/dash_mpd.c:
-	* tests/check/elements/hlsdemux_m3u8.c:
-	* tests/check/meson.build:
-	  New HLS, DASH and MSS adaptive demuxer elements
-	  This provides new HLS, DASH and MSS adaptive demuxer elements as a single plugin.
-	  These elements offer many improvements over the legacy elements. They will only
-	  work within a streams-aware context (`urisourcebin`, `uridecodebin3`,
-	  `decodebin3`, `playbin3`, ...).
-	  Stream selection and buffering is handled internally, this allows them to
-	  directly manage the elementary streams and stream selection.
-	  Authors:
-	  * Edward Hervey <edward@centricular.com>
-	  * Jan Schmidt <jan@centricular.com>
-	  * Piotrek Brzeziński <piotr@centricular.com>
-	  * Tim-Philipp Müller <tim@centricular.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2117>
-
-2022-04-15 09:53:19 +0800  Hou Qi <qi.hou@nxp.com>
+	  meson: Ensure that soup plugin is built on Windows
+	  The libpsl subproject wasn't building successfully and CI didn't
+	  notice because:
+	  1. The plugin wasn't explicitly enabled
+	  2. Even when the plugin is explicitly enabled, the dep is not required
+	  at build time when not building a static plugin
+	  So fix all of these issues.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5038>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: copy colorimetry values to output_state caps
-	  This is to avoid transcoding negotiation fail between v4l2h265dec
-	  and v4l2h264enc caused by colorimetry mismatch.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2192>
-
-2022-04-14 20:10:46 +1000  Brad Hards <bradh@frogmouth.net>
-
-	* tests/interactive/equalizer-test.c:
-	* tests/interactive/gdkpixbufoverlay-test.c:
-	* tests/interactive/gdkpixbufsink-test.c:
-	* tests/interactive/meson.build:
-	* tests/interactive/test-accurate-seek.c:
-	* tests/interactive/test-oss4.c:
-	* tests/interactive/test-segment-seeks.c:
-	* tests/interactive/videobox-test.c:
-	* tests/interactive/videocrop-test.c:
-	* tests/interactive/videocrop2-test.c:
-	* tests/interactive/ximagesrc-test.c:
-	* tests/meson.build:
-	  tests: rename 'icles' subdir to be more descriptive
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2178>
+2023-07-13 16:27:05 +0200  Michael Tretter <m.tretter@pengutronix.de>
 
-2022-04-07 11:12:47 +1000  Havard Graff <havard.graff@gmail.com>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: remove empty sink_query
+	  The sink_query() function simply calls the sink_query() function of the parent
+	  videoencoder class. Remove the override to simply directly call the parent's
+	  function.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5034>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: fix leak of channel_mapping
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2179>
+2023-07-13 16:16:44 +0200  Michael Tretter <m.tretter@pengutronix.de>
 
-2022-04-13 10:17:15 +0800  Ming Qian <ming.qian@nxp.com>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: replace custom QUERY_CAPS handling with getcaps callback
+	  The videoencoder base class uses getcaps() to ask a subclass for the caps in its
+	  sink_query_default() implementation.
+	  Replace the custom handling of the QUERY_CAPS in the v4l2videoenc with an
+	  implementation of getcaps() that returns the caps that are supported by the
+	  v4l2videoenc to return these caps in the query.
+	  This getcaps() implementation also calls the provided proxy_getcaps(), which
+	  sends a caps query to downstream. This fixes the v4l2videoenc element to respect
+	  limits of downstream elements in a sink query.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5034>
+
+2023-07-12 09:27:22 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	  doc: Update cache after NV12_8L128 and NV12_10BE_8L128 addition
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2158>
+	* tests/check/meson.build:
+	  meson: Always use forward slashes in defines with paths
+	  Fixes the following build failure on MSYS2:
+	  ```
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c: In function 'test_seeking':
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c:107:53: error: incomplete universal character name \U
+	  107 |   g_object_set (G_OBJECT (src), "location", TESTFILE, NULL);
+	  |                                                     ^
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c:107:53: warning: unknown escape sequence: '\A'
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c:107:53: warning: unknown escape sequence: '\g'
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c:107:53: warning: unknown escape sequence: '\s'
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c:107:53: warning: unknown escape sequence: '\g'
+	  ../subprojects/gstreamer/tests/check/elements/filesrc.c:107:53: warning: unknown escape sequence: '\c'
+	  ```
+	  Due to: `-DTESTFILE=\"C:\\Users\\Administrator\[...]`
+	  https://gitlab.freedesktop.org/nirbheek/gstreamer/-/jobs/45317733
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5018>
 
-2022-04-12 14:15:01 +0800  Ming Qian <ming.qian@nxp.com>
+2023-01-31 15:52:42 +0100  Michael Tretter <m.tretter@pengutronix.de>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2: Add NV12_8L128 in gst_v4l2_object_get_caps_info
-	  It should be included in
-	  <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1379>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2158>
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: always allocate CAPTURE buffer from our pool
+	  The videoencoder base class always uses the negotiated allocator for allocating
+	  coded buffers and ignores the negotiated buffer pool. Therefore, the
+	  v4l2videoenc always has to copy buffers from the pool into the allocated
+	  output buffers.
+	  This breaks downstream elements that want to import the CAPTURE buffers of the
+	  v4l2videoenc, since the v4l2videoenc copies the exported CAPTURE buffers and
+	  sends the copies downstream.
+	  Always use the CAPTURE buffer pool for acquiring CAPTURE buffers instead of
+	  allocating the buffers in the base class.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4230>
+
+2023-07-11 21:41:46 +0200  Carlos Rafael Giani <crg7475@mailbox.org>
 
-2022-04-12 10:35:26 +0800  Ming Qian <ming.qian@nxp.com>
+	* meson.build:
+	  gl: Take into account viv-fb vs. viv_fb naming in meson scripts
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5020>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2: Add a missed break
-	  Fix a typo that miss a break in the switch statement
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2158>
+2023-05-16 21:24:44 +1000  Matthew Waters <matthew@centricular.com>
 
-2022-04-11 13:40:56 +0200  Robert Rosengren <robertr@axis.com>
+	* ext/qt/gstqtglutility.cc:
+	* ext/qt/meson.build:
+	* meson.build:
+	  gl: provide a pkg-config/gir file for the viv-fb backend
+	  Required to be able to generate coherent bindings for window system
+	  specific APIs due to limitations in gobject-introspection.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5020>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	  rtpbin: Fix division by zero when using ts-offset-smoothing-factor
-	  avg_ts_offset may cause division by zero when calculating potential
-	  overflow protection. This fix will avoid the division.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2151>
+2023-07-10 22:45:49 +0900  Seungha Yang <seungha@centricular.com>
 
-2022-04-06 09:46:30 -0400  Tristan Matthews <tmatth@videolan.org>
+	* ext/qt6/qt6glitem.cc:
+	  qt6: Set sampler filtering method
+	  QQuickItem::smooth property doesn't seem to be propagated to
+	  newly created QSGSimpleTextureNode automatically.
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2793
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5004>
 
-	* gst/rtp/gstrtpopusdepay.c:
-	  rtpopusdepay: assume 2 channels if sprop-stereo is missing
-	  Fixes #1064
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2125>
+2023-07-09 17:44:03 +0200  David Craven <david@craven.ch>
 
-2022-03-11 15:13:21 +0100  Matthias Fuchs <matthias1.fuchs@zeiss.com>
+	* gst/matroska/matroska-read-common.c:
+	  matroska: demux: Strip signal byte from encrypted blocks
+	  Removes the signal byte when the frame is unencrypted to
+	  be consistent with when the frame is encrypted.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4997>
 
-	* ext/qt/gstqtsrc.cc:
-	* ext/qt/qtwindow.cc:
-	* ext/qt/qtwindow.h:
-	  qmlglsrc: Fix deadlock when stopping
-	  This fix makes sure that streaming thread stops waiting when the
-	  qmlglsrc element transitions from playing to paused.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2115>
+2023-06-26 17:10:33 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-03-14 17:20:38 +0100  Matthias Fuchs <matthias1.fuchs@zeiss.com>
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Don't wait for src_ch if active
+	  If the capture pool is already active, like when handling gaps at the
+	  start of a stream, do not setup the decoder to wait for src_ch event.
+	  Otherwise the decoder will endup waiting for that at the wrong moment
+	  and exit the decoding thread unexpectedly.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4590>
 
-	* ext/qt/qtwindow.cc:
-	  qmlglsrc: Fix missing depth & stencil buffer
-	  Qt Quick primitives which have some kind of alpha blending
-	  (transparency, rounded corners) are z-sorted by Qt and rendered in the
-	  correct order. For opaque primitives Qt relies on the OpenGL depth
-	  buffer to correctly determine the visibility of stacked elements.
-	  This change enables the depth buffer to make sure that opaque primitives
-	  are correctly z-stacked.
-	  https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph-renderer.html#opaque-primitives
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2114>
+2023-06-26 17:08:57 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-2022-04-06 10:14:19 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Move pool setup inside negotiate()
+	  Move all the pool configuration inside the negotiate() virtual function.
+	  This allow settting up a pool with default format whenever the base
+	  class wants to start without input data, like gaps.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4590>
 
-	* gst/rtpmanager/rtpstats.h:
-	  rtpstats: Remove non-existing twcc field docs from RTPPacketInfo and add missing field docs
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2121>
+2023-07-06 17:44:48 +0800  Hou Qi <qi.hou@nxp.com>
 
-2022-04-05 20:28:36 +0300  Sebastian Dröge <sebastian@centricular.com>
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2videodec: correctly register v4l2mpeg2dec
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4977>
 
-	* gst/rtpmanager/rtpsession.h:
-	  rtpsession: Remove unused twcc fields from the struct
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2121>
+2023-07-07 12:33:37 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-2022-03-30 11:06:02 -0400  Xavier Claessens <xavier.claessens@collabora.com>
+	* gst/videofilter/gstvideoflip.c:
+	  videoflip: fix critical when tag list is not writable
+	  Fix this pipeline where the tag list is not writable:
+	  gst-launch-1.0 videotestsrc ! taginject tags="image-orientation=rotate-90" ! videoflip video-direction=auto \
+	  ! autovideosink
+	  GStreamer-CRITICAL **: 12:34:36.310: gst_tag_list_add: assertion 'gst_tag_list_is_writable (list)' failed
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4987>
 
-	* meson.build:
-	  Use gmodule-no-export-2.0
-	  We don't need `-Wl,--export-dynamic`, that's used only for executables
-	  that needs to export an API to be used by plugins they load.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2031>
+2023-07-06 14:27:42 +0200  Michael Olbrich <m.olbrich@pengutronix.de>
 
-2022-03-25 15:00:20 -0400  Xavier Claessens <xavier.claessens@collabora.com>
+	* sys/v4l2/gstv4l2src.c:
+	  v4l2src: handle resolution change when buffers are copied
+	  When buffers are copied then GST_V4L2_FLOW_RESOLUTION_CHANGE is returned by
+	  gst_v4l2_buffer_pool_process() so do renegotiation here as well.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4980>
 
-	* ext/qt/meson.build:
-	* ext/soup/meson.build:
-	* gst/imagefreeze/meson.build:
-	* gst/rtsp/meson.build:
-	* gst/shapewipe/meson.build:
-	* meson.build:
-	* tests/check/meson.build:
-	  Remove glib and gobject dependencies everywhere
-	  They are part of gst_dep already and we have to make sure to always have
-	  gst_dep. The order in dependencies matters, because it is also the order
-	  in which Meson will set -I args. We want gstreamer's config.h to take
-	  precedence over glib's private config.h when it's a subproject.
-	  While at it, remove useless fallback args for gmodule/gio dependencies,
-	  only gstreamer core needs it.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2031>
+2023-07-05 19:55:17 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2022-03-31 23:41:41 +0200  Thibault Saunier <tsaunier@igalia.com>
+	* docs/gst_plugins_cache.json:
+	  video: Move NV12_10LE40_4L4 before the BE variant on LE platforms
+	  This keeps the sorting rules for the format list intact.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4974>
 
-	* gst/debugutils/gstnavigationtest.c:
-	  navigation: Rename parse_state to parse_modifier_state
-	  `parse_state` sounds a bit weird and `parse_modifier_state` is clearer.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2087>
+2021-05-26 12:37:21 +0200  Philipp Zabel <p.zabel@pengutronix.de>
 
-2022-03-26 01:02:02 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+	* ext/qt/qtglrenderer.cc:
+	* ext/qt6/qt6glrenderer.cc:
+	  qtglrenderer.cc: Add missing QCoreApplication and QEventLoop includes
+	  This fixes a build error if Qt was build without accessibility support:
+	  ../../../../../gstreamer/subprojects/gst-plugins-good/ext/qt/qtglrenderer.cc:
+	  In member function 'bool GstQuickRenderer::init(GstGLContext*, GError**)':
+	  ../../../../../gstreamer/subprojects/gst-plugins-good/ext/qt/qtglrenderer.cc:341:13:
+	  error: 'QCoreApplication' was not declared in this scope; did you mean 'QApplication'?
+	  ../../../../../gstreamer/subprojects/gst-plugins-good/ext/qt/qtglrenderer.cc:341:31:
+	  error: 'app' was not declared in this scope
+	  ../../../../../gstreamer/subprojects/gst-plugins-good/ext/qt/qtglrenderer.cc:341:37:
+	  error: 'QCoreApplication' is not a class, namespace, or enumeration
+	  [...]
+	  ../../../../../gstreamer/subprojects/gst-plugins-good/ext/qt/qtglrenderer.cc:458:5:
+	  error: 'QEventLoop' was not declared in this scope; did you mean 'QEvent'?
+	  ../../../../../gstreamer/subprojects/gst-plugins-good/ext/qt/qtglrenderer.cc:459:9:
+	  error: 'loop' was not declared in this scope
+	  If accessibility is enabled, the includes for QCoreApplication and QEventLoop
+	  are indirectly pulled via QWidget.
+	  Add the required headers as documented in [1] and [2].
+	  [1] https://doc.qt.io/qt-5/qcoreapplication.html
+	  [2] https://doc.qt.io/qt-5/qeventloop.html
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4815>
+
+2023-06-28 15:51:15 +0300  Jordan Petridis <jordan@centricular.com>
 
-	* ext/soup/meson.build:
-	  meson: Add some messages when selecting libsoup
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2032>
+	* ext/libpng/gstpngenc.c:
+	* ext/libpng/gstpngenc.h:
+	  pngenc: Allocate a single GstMemory per frame
+	  Previously, we would create a new GstMemory per write operation
+	  and then append them to the GstBuffer. This would cause a reallocation
+	  every 16 Memories which is an issue since the png encoder will usually
+	  do write in a pattern of 4, 8 and 8k bytes repeating until the frame
+	  is done.
+	  Instead allocate a single GstMemory and keep writting it into it
+	  with a manual index. Much like the jpegenc does.
+	  Doing some basic testing With a testsrc snow pattern at 4k and 8k
+	  the same pipeline would take ~3.30s to encode a 4k frame and ~23s
+	  for an 8k. At 4k 0.70s/33% is taken by memory allocations, while at
+	  8k its ~10.5s/45%.
+	  With this patch, at 4k the pipeline takes ~2.40s and at 8k only 9.60s
+	  making this 28% and 58% faster accordingly on my laptop, and
+	  allocation runtime is dropped to subsecond times.
+	  Here's the test pipeline used, increase num-buffers in image freeze
+	  to gather more samples.
+	  ```
+	  gst-launch-1.0 videotestsrc num-buffers=1 pattern=snow ! imagefreeze num-buffers=1 ! \
+	  video/x-raw,width=7680,height=4320 ! pngenc ! fakesink
+	  ```
+	  Close #2717
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4944>
 
-2022-03-26 00:59:12 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2023-07-04 23:11:53 +0900  Seungha Yang <seungha@centricular.com>
 
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Fix crash when is-live=false
+	  The pad's parent (i.e., rtspsrc) can be nullptr since we add pads
+	  later.
+	  Co-authored-by: Jan Schmidt <jan@centricular.com>
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2751
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4965>
+
+2023-07-04 10:57:01 +0200  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
+	  hlsdemux2: Ensure processed webvtt ends with empty new line
+	  Parsers downstream will use empty new lines to detect where an entry
+	  ends. Failure to have a newline would cause the entry to be either
+	  discarded or (wrongly) concatenated with the next entry
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2752
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4963>
+
+2023-06-23 13:11:49 +0700  Arnaud Rebillout <elboulangero@gmail.com>
+
+	  examples: gtk: Add example to illustrate usage of accept-certificate with souphttpsrc
+	  The aim of this example is to show how to make use of the accept-certificate
+	  signal from a GTK GUI, and prompt user in case of invalid certificate.
+	  There are two subtleties to be aware of:
+	  1. the signal is emitted from the GStreamer streaming thread, therefore the
+	  caller can't modify the GUI straight away, instead they must do it from the
+	  main thread (eg. by using g_idle_add())
+	  2. in case of a redirection, then a TLS failure, the caller won't know
+	  about the redirection. Actually, it's possible to be notified of the
+	  redirection by watching "message:element" and inspecting http-headers,
+	  but even in that case, the signal will be received *after* the signal
+	  "accept-certificate" (even though the redirection happened *before*).
+	  This second point is tricky. It's not uncommon to have servers that redirect
+	  http requests to https. So errors of the type "HTTP -> HTTPS -> TLS error"
+	  happen, and if the caller doesn't care about redirection, they might prompt
+	  users with a message such as "TLS error for URL http://...", which wouldn't make
+	  much sense.
+	  This example shows how to handle that right, by connecting to the signal
+	  "message:element", inspecting the http-headers, and in case of redirection,
+	  updating the TLS error dialog to indicate that the request was redirected.
+	  Here are a few examples of streams that exhibit TLS failure (at the time of
+	  this commit, of course):
+	  * https://radiolive.sanjavier.es:8443/stream: unknown-ca
+	  * https://am981.ddns.net:9005/stream.ogg: unknown-ca
+	  * http://stream.diazol.hu:7092/zene.mp3: redir then bad-identity
+	  * https://streaming.fabrik.fm/izwi/echocast/audio/index.m3u8: unknown-ca
+	  (this one is a HLS stream)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4925>
+
+2023-03-16 22:32:11 +0700  Arnaud Rebillout <elboulangero@gmail.com>
+
+	* docs/gst_plugins_cache.json:
 	* ext/soup/gstsouphttpsrc.c:
-	  soup: Fix usage of symbols / defines that are gone in libsoup3
-	  I am not sure about the SOUP_MESSAGE_OVERWRITE_CHUNKS change, but it
-	  was definitely already broken when using libsoup-3.0 in a shared
-	  build. souphttpsrc probably needs to be ported from SoupMessage to
-	  SoupServerMessage when using libsoup-3.0.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1111
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2032>
+	  souphttpsrc: forward accept-certificate signal from libsoup-3
+	  With libsoup 2.x, it was possible to know when there was a TLS failure, as
+	  libsoup provided the "special http status code" SOUP_STATUS_SSL_FAILED.
+	  However these special codes were dropped with libsoup 3.x: now libsoup emits
+	  the accept-certificate signal when there's a TLS failure.
+	  This commit adds a signal "accept-certificate" to SoupHttpSrc, which is in fact
+	  just about forwarding the signal from SoupMessage (which is, itself, forwarded
+	  from GTlsConnection). Note that, in case of libsoup 2.x, the signal is never
+	  emitted.
+	  Fixes: #2379
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4925>
+
+2023-06-27 10:42:21 +0200  Peter Stensson <petest@axis.com>
 
-2022-03-26 00:56:04 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+	* gst/rtp/gstrtpvp9pay.c:
+	* tests/check/elements/rtpvp9.c:
+	  rtpvp9pay: Only mark first outgoing packet as non delta-unit
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4937>
 
-	* ext/soup/gstsouploader.c:
-	* ext/soup/gstsouploader.h:
-	  soup: Fix pre-processor macros in souploader for libsoup-3.0
-	  Some of the preprocessor conditionals in the loader were very broken
-	  with libsoup-3.0 + --default-library=static
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1111
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2032>
+2023-06-27 10:40:47 +0200  Peter Stensson <petest@axis.com>
+
+	* gst/rtp/gstrtpvp8pay.c:
+	* tests/check/elements/rtpvp8.c:
+	  rtpvp8pay: Only mark first outgoing packet as non delta-unit
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4937>
 
-2022-03-28 18:12:03 +1100  Matthew Waters <matthew@centricular.com>
+2023-06-22 11:17:48 +0200  Peter Stensson <petest@axis.com>
 
-	* sys/osxaudio/gstosxcoreaudio.c:
-	  osxcoreaudio: fix unused-but-set warning
-	  ../sys/osxaudio/gstosxcoreaudio.c:480:18: error: variable 'interleaved' set but not used [-Werror,-Wunused-but-set-variable]
-	  gboolean sign, interleaved;
-	  ^
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2046>
+	* tests/check/elements/rtph264.c:
+	  rtph264pay: Add unit tests verifying delta-unit flag
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4937>
+
+2023-06-22 11:06:02 +0200  Peter Stensson <petest@axis.com>
+
+	* gst/rtp/gstrtph265pay.c:
+	* tests/check/elements/rtph265.c:
+	  rtph265pay: Only mark first NAL as non delta-unit
+	  When the input buffer contained multiple NAL's the second one would keep
+	  the non delta-unit flag for a key frame.
+	  The delta-unit flag will now be set per NAL when preparing the buffer
+	  list to payload.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4937>
 
-2022-03-28 10:10:45 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-06-16 21:53:11 +0200  Mathieu Duponchelle <mathieu@centricular.com>
 
 	* docs/gst_plugins_cache.json:
-	  video-format: Move NV12_8L128 into the correct position in GST_VIDEO_FORMATS_ALL
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2045>
+	* gst/rtpmanager/gstrtpbin.c:
+	* gst/rtpmanager/gstrtpbin.h:
+	* gst/rtpmanager/gstrtpsession.c:
+	* gst/rtpmanager/rtpsession.c:
+	* gst/rtpmanager/rtpsession.h:
+	  rtpsession: expose timeout-inactive-sources property
+	  In some situations it is not desirable to time out when no data is
+	  received any longer, users can opt in to this behavior via a new
+	  property.
+	  The property is also exposed on rtpbin and sdpdemux
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4880>
 
-2022-03-28 13:35:17 +1100  Matthew Waters <matthew@centricular.com>
+2023-06-22 10:10:43 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
-	* gst/deinterlace/tvtime/tomsmocomp/SearchLoopTop.inc:
-	  deinterlace: silence unused-but-set werror from imported code
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2042>
+	* sys/v4l2/gstv4l2bufferpool.c:
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2: Fix support for left and top padding
+	  In the current implementation, we support for most pixel format left
+	  and top padding by changing the offset in the video meta. Though, to
+	  align driver bytesused to the offset, we recalculate the offset, which
+	  removed the modification we did before.
+	  Instead, save the plane size, and truncate the driver reported bytesused
+	  to the expected size, which ensures that the offsets still match. This
+	  should also fix issues were the buffer size ended up bigger then the
+	  pool size due to driver introduced padding.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4920>
+
+2023-05-25 20:39:03 +0200  Matthieu Volat <mazhe@alkumuna.eu>
 
-2022-03-28 09:50:38 +1100  Matthew Waters <matthew@centricular.com>
+	* sys/oss/gstossaudio.c:
+	* sys/oss/gstossaudioelement.c:
+	* sys/oss/gstossaudioelements.h:
+	* sys/oss/gstossdeviceprovider.c:
+	* sys/oss/gstossdeviceprovider.h:
+	* sys/oss/meson.build:
+	  oss: add a GstDeviceProvider plugin
+	  Based on Alsa's GstDeviceProvider structure, relies on sndstat
+	  file for OSS device enumeration but uses already existing utils
+	  to query caps and names.
+	  Reviewed and thanks to @slomo
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4879>
+
+2023-05-25 14:41:58 +0800  Elliot Chen <elliot.chen@nxp.com>
 
-	* sys/osxvideo/osxvideosink.m:
-	  osxvideosink: fix unused-but-set-variable warning
-	  ../sys/osxvideo/osxvideosink.m:859:11: error: variable 'data' set but not used [-Werror,-Wunused-but-set-variable]
-	  guint8 *data, *readp, *writep;
-	  ^
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2040>
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	  dashdemux2: fix some mpeg-ts issue with no audio output
+	  For dashdemux2, one stream will create one track.
+	  Maybe there are multiple tracks in one stream such as
+	  some mpeg-ts streams, need add the function to check
+	  and create the other tracks if needed.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4706>
 
-2022-03-25 11:42:03 -0300  Thibault Saunier <tsaunier@igalia.com>
+2023-06-21 17:05:47 +0200  Edward Hervey <edward@centricular.com>
 
-	* gst/debugutils/gstnavigationtest.c:
-	* gst/debugutils/gstnavigationtest.h:
-	  navigationtest: Add some support for modifiers
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2010>
+	* gst/matroska/matroska-demux.c:
+	  matroska-demux: Properly handle early time-based segments
+	  Refusing an incoming segment in < GST_MATROSKA_READ_STATE_DATA should only be
+	  done if the incoming segment is not in GST_FORMAT_TIME.
+	  In GST_FORMAT_TIME, we are just storing the values and returning, so we can
+	  invert the order of the checks.
+	  Fixes proper segment propagation in matroska/webm DASH use-cases
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-2022-03-18 16:59:32 +0000  Thibault Saunier <tsaunier@igalia.com>
+2023-02-10 15:26:20 +0100  Edward Hervey <edward@centricular.com>
 
-	* ext/gtk/gtkgstbasewidget.c:
-	* ext/qt/qtitem.cc:
-	  navigation: Add support for key Modifiers in all relevant events
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2010>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Handle early SEEKING query
+	  No pads are present yet, but we can still answer the query
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-2021-11-17 17:27:13 +1100  Matthew Waters <matthew@centricular.com>
+2023-02-09 17:22:34 +0100  Edward Hervey <edward@centricular.com>
 
-	* gst/rtpmanager/gstrtpptdemux.c:
-	  rtpptdemux: fix leak of caps when ignoring a pt
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2025>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Fix non-accurate seeking
+	  If no accurate positioning was required, default to snap to the previous segment
+	  for improved responsiveness
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-2022-02-23 12:53:04 +0100  Vivienne Watermeier <vwatermeier@igalia.com>
+2023-02-09 14:54:27 +0100  Edward Hervey <edward@centricular.com>
 
-	* ext/qt/qtitem.cc:
-	* ext/qt/qtitem.h:
-	  qt: Add touch event support
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1633>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Handle return in seek handling
+	  Various code path were repeating the same logic, and risk forgetting a lock
+	  release.
+	  Unify all of them
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-2022-02-21 20:16:06 +0100  Vivienne Watermeier <vwatermeier@igalia.com>
+2023-02-09 14:45:01 +0100  Edward Hervey <edward@centricular.com>
 
-	* ext/gtk/gtkgstbasewidget.c:
-	  gtk: Add touch event support
-	  Add a handler for touch events to gtkbasewidget.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1633>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Move API lock usage
+	  It is not needed so early
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-2022-02-02 15:46:57 +0100  Vivienne Watermeier <vwatermeier@igalia.com>
+2023-02-08 17:24:18 +0100  Edward Hervey <edward@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/debugutils/gstnavigationtest.c:
-	* gst/debugutils/gstnavigationtest.h:
-	  navigationtest: Display touchscreen events, log all events
-	  Represents touchscreen events as a trail of black squares, one for each
-	  reported position. Additionally, this adds the `display-mouse` and
-	  `display-touch` properties to toggle visibility of mouse/touchscreen
-	  events, since touchscreens often emulate mouse events, as well as
-	  logging for all received navigation events.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1633>
-
-2022-02-14 16:08:23 +0100  Vivienne Watermeier <vwatermeier@igalia.com>
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Handle early key-unit seek
+	  Is a seek is done on stream-collection post, there are no selected streams
+	  yet. Therefore none would be chosen to adjust the key-unit seek.
+	  If no streams are selected, fallback to a default stream (i.e. one which has
+	  track(s) with GST_STREAM_FLAG_SELECT).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-	* ext/gtk/gstgtkbasesink.c:
-	* ext/gtk/gtkgstbasewidget.c:
-	* ext/qt/gstqtsink.cc:
-	* ext/qt/qtitem.cc:
-	* ext/qt/qtitem.h:
-	* gst/debugutils/gstnavigationtest.c:
-	* gst/debugutils/gstnavseek.c:
-	* gst/videobox/gstvideobox.c:
-	* gst/videocrop/gstvideocrop.c:
-	* gst/videofilter/gstvideoflip.c:
-	  all: Use new navigation interface and API
-	  Use and implement the new navigation interface in all relevant sink elements,
-	  and use API functions everywhere instead of directy accessing the event structure.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1633>
+2023-02-08 12:02:45 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	  adaptivedemux2: Fix early seeking
+	  When seeking is handled by the collection posting thread, there is a possibility
+	  that some leftover data will be pushed by the stream thread.
+	  Properly detect and reject those early segments (and buffers) by comparing it to
+	  the main segment seqnum
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3914>
 
-2022-03-18 15:20:49 +0100  Stéphane Cerveau <scerveau@collabora.com>
+2023-06-20 14:56:44 +0200  François Laignel <francois@centricular.com>
 
-	* gst/wavparse/gstwavparse.c:
-	* tests/check/elements/wavparse.c:
-	  wavparse: handle query in any parse state
-	  In order to create the stream_id, we need to
-	  pass the query to the default query handler.
-	  If the parse state is different from GST_WAVPARSE_DATA
-	  the query should be passed to the default query
-	  handler.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1987>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: opus: set entry as sampled
+	  ... otherwise streams with constant size samples defined with a single
+	  `sample_size` for all samples in the `stsz` box fall in the category
+	  `chunks_are_samples` in `qtdemux_stbl_init`, overriding the actual
+	  sample count.
+	  `FOURCC_soun` would set this automatically for `compression_id == 0xfffe`,
+	  however `compression_id` is read from the Audio Sample Entry box at an offset
+	  marked as "pre-defined" in some version of the spec and set to 0 both by
+	  GStreamer and FFmpeg for opus streams.
+	  Considering the stream `sampled` flag is set explicitely by other fourcc
+	  variants, doing so for opus seems consistent.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4903>
+
+2023-06-13 13:20:16 +0300  Sebastian Dröge <sebastian@centricular.com>
 
-2020-09-13 02:17:59 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* gst/audioparsers/gstflacparse.c:
+	  flacparse: Avoid integer overflow in available data check for image tags
+	  If the image length as stored in the file is some bogus integer then
+	  adding it to the current byte readers position can overflow and wrongly
+	  have the check for enough available data succeed.
+	  This then later can cause NULL pointer dereferences or out of bounds
+	  reads/writes when actually reading the image data.
+	  Fixes ZDI-CAN-20775
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2661
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4894>
+
+2023-06-19 15:11:30 +0200  François Laignel <francois@centricular.com>
 
-	* gst/flv/gstflvmux.c:
-	  flvmux: Clean up aggregate's control flow
-	  This unifies exits to go through a single out label. It mostly
-	  simplifies how EOS is handled.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1035>
+	* gst/isomp4/qtdemux.c:
+	* gst/isomp4/qtdemux_dump.c:
+	* gst/isomp4/qtdemux_dump.h:
+	* gst/isomp4/qtdemux_types.c:
+	  qtdemux: parse Opus and dOps as qtdemux nodes and add size checks
+	  This allows checking the nodes conformity and dumping parsed values.
+	  Note: Audio Sample Entry version parsing and offset handling is handled as part
+	  of `FOURCC_soun` common processing and in `qtdemux_parse_node`.
+	  Also, only read `stream_count` and `coupled_count` when
+	  `channel_mapping_family` != 0. See:
+	  https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4875>
+
+2023-06-16 10:29:28 +0200  François Laignel <francois@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: fix byte order for opus extension and version field type
+	  The "Encapsulation of Opus in ISO Base Media File Format" [1] specifications,
+	  § 4.3.2 Opus Specific Box, indicates that data must be stored as big-endian.
+	  [1] https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4875>
+
+2023-06-16 10:02:16 +0200  François Laignel <francois@centricular.com>
+
+	* gst/isomp4/atoms.c:
+	  qtmux: fix byte order for opus extension
+	  The "Encapsulation of Opus in ISO Base Media File Format" [1] specifications,
+	  § 4.3.2 Opus Specific Box, indicates that data must be stored as big-endian.
+	  In `build_opus_extension`, `gst_byte_writer_put*_le ()` variants were used,
+	  causing audio streams conversion to Opus in mp4 to offset samples due to the
+	  PreSkip field incorrect value (29ms early in our test cases).
+	  [1] https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4875>
+
+2023-02-05 21:55:52 -0500  Daniel Morin <daniel.morin@collabora.com>
+
+	* docs/gst_plugins_cache.json:
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2src: adding support for bayer 10,12,14,16
+	  - Adding bayer 10,12,14,16 bits components with 16 bits storage. These
+	  changes only adds capabilities. Capability format string is a complete
+	  description of the frame and pixels layout. Only mapping LE bayer
+	  formats as v4l2 only define LE bayer formats.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4852>
 
-2022-03-18 16:34:38 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-06-15 10:41:26 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
 	* docs/gst_plugins_cache.json:
-	  doc: Update cache after NV12_8L128 addition
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1379>
+	  doc: Update plugin cache
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3447>
 
-2021-10-19 14:41:04 +0800  Ming Qian <ming.qian@nxp.com>
+2023-06-14 17:18:04 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
 
+	* sys/v4l2/ext/v4l2-common.h:
+	* sys/v4l2/ext/v4l2-controls.h:
 	* sys/v4l2/ext/videodev2.h:
+	  v4l2: Sync headers to current media_stage
+	  commit d78b9d6671decdaedb539635b1d0a34f8f5934f8
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3447>
+
+2023-04-20 17:31:32 -0400  Daniel Morin <daniel.morin@collabora.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
 	* sys/v4l2/gstv4l2object.c:
-	  v4l2: Add NV12_8L128 and NV12_10BE_8L128
-	  These formats are used by i.MX 8QXP/8QM VPU.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1379>
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2src: fix support for bayer format
+	  - Define a new function that identify if the v4l2object is raw based
+	  on pixel format
+	  - Only consider setting delta flag on buffer if the video is not raw.
+	  Sponsored by Living Optics Ltd.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4848>
 
-2021-09-09 23:43:33 +1000  Matthew Waters <matthew@centricular.com>
+2023-04-04 14:58:33 +0100  Mark Hymers <mark@livingoptics.com>
 
 	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtprtxreceive.c:
-	* gst/rtpmanager/gstrtprtxreceive.h:
-	* gst/rtpmanager/gstrtprtxsend.c:
-	* gst/rtpmanager/gstrtprtxsend.h:
-	* tests/check/elements/rtprtx.c:
-	  rtpmanager/rtx: implement initial support for reading/writing rid extensions
-	  Two RTP Header extensions are very relevant for rtprtxsend/receive.
-	  1. "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id": will always be removed
-	  2. "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id": will be written
-	  instead of the "rtp-stream-id" header extension.
-	  Currently it's only a simple replacement of one header extension for
-	  another however a future change would only add the relevant extension
-	  based on some heuristics (like, video frames only on one of the rtp key
-	  frame buffers, or only until the rtx ssrc has been validated by the peer)
-	  in order to reduce the required bandwidth.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1759>
-
-2021-08-25 16:59:40 +1000  Matthew Waters <matthew@centricular.com>
-
-	* tests/check/elements/rtphdrextsdes.c:
-	* tests/check/meson.build:
-	  test: add tests for sdes-based RTP header extensions
-	  mid, stream id and repaired stream id.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1759>
+	* gst/matroska/matroska-demux.c:
+	* gst/matroska/matroska-mux.c:
+	  matroska: Add support for more pixel formats
+	  - Add support for GRAY16_LE (using ffmpeg fourcc mapping)
+	  - Update documentation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4824>
 
-2021-08-25 16:58:16 +1000  Matthew Waters <matthew@centricular.com>
+2023-02-28 15:45:30 -0500  Daniel Morin <daniel.morin@collabora.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtphdrext-repairedstreamid.c:
-	* gst/rtpmanager/gstrtphdrext-repairedstreamid.h:
-	* gst/rtpmanager/gstrtphdrext-streamid.c:
-	* gst/rtpmanager/gstrtphdrext-streamid.h:
-	* gst/rtpmanager/gstrtpmanager.c:
-	* gst/rtpmanager/meson.build:
-	  rtpmanager: add support for RFC8852 (rid) RTP header extensions
-	  Both for regular RID and for adding on a repaired (RTX) etc stream.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1759>
-
-2021-08-25 16:38:44 +1000  Matthew Waters <matthew@centricular.com>
+	* gst/matroska/matroska-demux.c:
+	* gst/matroska/matroska-mux.c:
+	  matroska: Add new pixels format support
+	  - Add support for GRAY10_BE32
+	  - Add support for RGBA64_LE and BGRA64_LE
+	  Sponsored by Living Optics
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4824>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtphdrext-mid.c:
-	* gst/rtpmanager/gstrtphdrext-mid.h:
-	* gst/rtpmanager/gstrtpmanager.c:
-	* gst/rtpmanager/meson.build:
-	  rtpmanager: add support for writing RFC8843 (BUNDLE mid) RTP header extension
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1759>
+2023-06-12 19:24:15 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-03-18 19:33:00 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* tests/check/elements/rtpbin_buffer_list.c:
+	  tests: rtpbin_buffer_list: fix possible unaligned read on 32-bit ARM
+	  Fixes #2666
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4842>
 
-	* docs/gst_plugins_cache.json:
-	* gst/videocrop/gstvideocrop-private.h:
-	* gst/videocrop/gstvideocrop.c:
-	* gst/videocrop/gstvideocrop.h:
-	  videocrop: Add support for v210
-	  Like UYVY and similar formats this is rounding down to the start of the
-	  previous macro-pixel to not mix up the different components.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1988>
+2023-04-29 16:20:13 +0100  Tim-Philipp Müller <tim@centricular.com>
 
-2022-03-18 19:06:05 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* gst/matroska/matroska-demux.c:
+	* tests/check/elements/matroskademux.c:
+	  matroska-demux: fix accumulated base offset in segment seeks
+	  When doing a segment seek, the base offset in the new segment
+	  would be increased by segment.position which is basically the
+	  timestamp of the last packet. This does not include the duration
+	  of the last packet though, so might be slightly shorter than the
+	  actual duration of the clip or the requested segment.
+	  Increase the base offset by the segment duration instead when
+	  accumulating segments, which is more correct as it doesn't cut
+	  off the last frame and makes the effective loop segment duration
+	  consistent with the actual duration returned from a duration
+	  query.
+	  In case a segment stop was specified it's also possible that
+	  some data was sent beyond the stop that's necessary for decoding
+	  so the base offset increment should be based on that then and
+	  not on the timestamp of the last buffer pushed out.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4604>
+
+2023-05-30 17:52:34 +0900  ekwange <ekwange@gmail.com>
 
-	* gst/videocrop/gstvideocrop.c:
-	  videocrop: Use GST_ROUND_DOWN_2 instead of re-defining a local version
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1988>
+	* sys/v4l2/v4l2_calls.c:
+	  v4l2: Change to query only up to V4L2_CID_PRIVATE_BASE+V4L2_CID_MAX_CTRLS
+	  Fix to prevent infinite querying.
+	  There are devices that exceed V4L2_CID_PRIVATE_BASE+V4L2_CID_MAX_CTRLS
+	  but do not return EINVAL.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4790>
 
-2022-03-18 19:03:57 +0200  Sebastian Dröge <sebastian@centricular.com>
+2023-06-08 19:12:54 +0200  Jonas Kvinge <jonas@jkvinge.net>
 
-	* gst/videocrop/gstvideocrop.c:
-	* gst/videocrop/gstvideocrop.h:
-	  videocrop: Rename PACKED_COMPLEX to PACKED_YVYU
-	  It's not handling any kind of complex packed format, only formats that
-	  are like YVYU.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1988>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Allow data dash+xml manifest for uri
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4811>
 
-2022-03-18 13:42:27 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2023-06-05 07:29:57 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
 
-	* meson.build:
-	  meson: Bump all meson requirements to 0.60
-	  Lots of new warnings ever since
-	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1934
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1977>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Cleanup code for next pending command
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4792>
+
+2023-06-05 06:50:55 +0200  Jochen Henneberg <jh@henneberg-systemdesign.com>
+
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Do not try send dropped get/set parameter
+	  If the set_get_param_q has been emptied we have to reset the cached
+	  pending command to CMD_LOOP as we will not have the request parameters
+	  anymore.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4792>
+
+2023-05-08 16:55:26 +0200  Xabier Rodriguez Calvar <calvaris@igalia.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix critical message on cenc sample grouping parsing
+	  Inside qtdemux_parse_sbgp there is already a check to ensure the fragment group
+	  properties are not null but it is being hit in some examples and it is better to
+	  directly avoid the critical.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4576>
+
+2023-06-06 11:34:03 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/videofilter/gstvideoflip.c:
+	  videoflip: update orientation tag in auto mode
+	  The frames are flipped according to the tag orientation so it's no longer accurate.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4778>
+
+2023-06-06 09:24:37 +0800  Hou Qi <qi.hou@nxp.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2videodec: treat MPEG 1 format as MPEG 2
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4770>
+
+2023-05-30 15:31:47 +1000  Matthew Waters <matthew@centricular.com>
+
+	* ext/qt/meson.build:
+	  build/android: remove all references to gnustl
+	  Not needed anymore with NDK R25.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4747>
+
+2023-05-30 15:10:11 +0200  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+
+	* gst/isomp4/gstqtmux.c:
+	* gst/isomp4/qtdemux.c:
+	  isomp4: Fix (E)AC-3 channel count handling
+	  The muxer used a fixed value of 2 channels because the TR 102 366 spec
+	  says they're to be ignored. However, the demuxer still trusted them,
+	  resulting in bad caps.
+	  Make the muxer fill in the correct channel count anyway (FFmpeg already
+	  does) and make the demuxer ignore the value.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4739>
+
+2023-05-18 14:23:49 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+
+	* ext/qt6/gstqt6glutility.cc:
+	* ext/qt6/meson.build:
+	  meson: Support building qml6glsink on win32
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4742>
+
+2023-06-01 16:21:47 +0200  Piotr Brzeziński <piotr@centricular.com>
+
+	* ext/libpng/gstpngdec.c:
+	  pngdec: Fix 16bit RGB images display
+	  Due to the alpha value being inserted with _BEFORE, we were ending up
+	  with ARGB instead of RGBA, thus displaying completely wrong colours.
+	  According to libpng's manual, "to add an opaque alpha channel, use filler=0xff
+	  or 0xffff and PNG_FILLER_AFTER which will generate RGBA pixels".
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4756>
+
+2023-03-07 14:05:54 +0100  Stéphane Cerveau <scerveau@igalia.com>
 
-2022-03-15 15:44:17 +0900  Sangchul Lee <sc11.lee@samsung.com>
+	* gst/multifile/meson.build:
+	* tests/meson.build:
+	  gstreamer-full: add full static support
+	  Allow a project to use gstreamer-full as a static library
+	  and link to create a binary without dependencies.
+	  Introduce the option 'gst-full-target-type' to
+	  select the build type, dynamic(default) or static.
+	  In gstreamer-full/static build configuration gstreamer (gst.c)
+	  needs the symbol gst_init_static_plugins which is defined
+	  in gstreamer-full.
+	  All the tests and examples are linking with gstreamer but the
+	  symbol gst_init_static_plugins is only defined in the gstreamer-full
+	  library. gstreamer-full can not be built first as it needs to know what plugins
+	  will be built.
+	  One option would be to build all the examples and tests after
+	  gstreamer-full as the tools.
+	  Disable tools build in subprojects too as it will be built at the end of
+	  build process.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4128>
+
+2023-05-29 17:02:29 +1000  Matthew Waters <matthew@centricular.com>
+
+	* ext/qt6/qt6glrenderer.cc:
+	  qt6/glrenderer: don't attempt to use QWindow from non-Qt main thread
+	  Use QObject::deleteLater() to schedule deletion in the main thread.
+	  Remove the moveToThread of the QWindow.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4728>
+
+2023-05-29 17:01:01 +1000  Matthew Waters <matthew@centricular.com>
+
+	* ext/qt/qtglrenderer.cc:
+	  qt/glrenderer: don't attempt to use QWindow from non-Qt main thread
+	  Use QObject::deleteLater() to schedule deletion in the main thread.
+	  Remove the moveToThread of the QWindow.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4728>
+
+2023-05-18 10:25:44 +0900  Hyung Song <hyung.song@lge.com>
+
+	* gst/audioparsers/gstaacparse.c:
+	  aacparse: parse GASpecificConfig for channels
+	  Some media have valid channel information in GASpecificConfig which is
+	  not yet implemented in gstaacparse. Parse data according to ISO/IEC
+	  14496-3 just enough to get channels.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4720>
+
+2023-05-29 15:26:35 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/flv/gstflvmux.c:
+	* tests/check/elements/flvmux.c:
+	  flvmux: push metadata on caps change
+	  The metdata contains tags but also caps dependent info such as the
+	  resolution and the framerate.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4730>
+
+2023-05-29 15:23:11 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/flv/gstflvmux.c:
+	* gst/flv/gstflvmux.h:
+	  flvmux: rename 'new_tags' to 'new_metadata'
+	  The metadata contains more than just tags: resolution, framerate, etc.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4730>
+
+2023-05-29 15:21:27 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/flv/gstflvmux.c:
+	  flvmux: add some logs when input is changing
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4730>
+
+2023-05-24 16:17:46 +0200  Michael Olbrich <m.olbrich@pengutronix.de>
+
+	* gst/flv/gstflvmux.c:
+	  flvmux: use the correct timestamp to calculate wait times
+	  Since c0bf793c05cf793aa18a8548cda702625e388115 ("flvmux: Set PTS based on
+	  running time") the timestamp of the output buffer is already in running
+	  time. So using that for 'srcpad->segment.position' does not work correctly
+	  because gst_aggregator_simple_get_next_time() will convert it again with
+	  gst_segment_to_running_time().
+	  This means that the timestamp returned by
+	  gst_aggregator_simple_get_next_time() may be incorrect. For example, if
+	  flvmux is added to a already runinng pipeline then the timestamp is too
+	  small and gst_aggregator_wait_and_check() returns immediately. As a result,
+	  buffers may be muxed in the wrong order.
+	  To fix this, use the PTS of the incoming buffer instead of the outgoing
+	  buffer. Also add the duration as get_next_time() is supposed to return the
+	  timestamp of the next buffer, not the current one.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4701>
+
+2020-08-31 16:38:48 +0200  Michael Olbrich <m.olbrich@pengutronix.de>
+
+	* ext/jpeg/gstjpegdec.c:
+	  jpegdec: be stricter when detecting interlaced video
+	  There are broken(?) mjpeg videos that are incorrectly detected as
+	  interlaced. This happens because 'info.height > height' (e.g. 1088 > 1080).
+	  In the interlaced case info.height is approximately 'height * 2' but not
+	  exactly because height is a multiple of DCTSIZE. Make the check more
+	  restrictive but take the rounding effect into account.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4696>
+
+2020-08-31 16:12:33 +0200  Michael Olbrich <m.olbrich@pengutronix.de>
+
+	* ext/jpeg/gstjpegdec.c:
+	  jpegdec: decode the correct number of lines for interlaced frames
+	  For interlaced jpeg, gst_jpeg_dec_decode_direct() is called twice, once for each
+	  field. In this case, stride[n] is plane_stride[n] * 2 to ensure that only every
+	  other line is written. So the loop must stop at height / num_fields.
+	  If the frame is really interlaced then continuing beyound this, is not harmful,
+	  because jpeg_read_raw_data() will do nothing and return 0, so am info message is
+	  printed.
+	  However, if the frame is not actually interlaced, just misdetected as interlaced
+	  then there is still data available from the second half of the frame. Now
+	  line[0][j] is set to the scratch buffer. If the scratch buffer is not allocated
+	  (because the height is a multiple of v_samp[0] * DCTSIZE) then the result is a
+	  segfault due to a null-pointer dereference.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4696>
+
+2023-05-05 15:12:46 +0800  YURI FEDOSEEV <yuri.fedoseev@helixleisure.com>
+
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: support force keyframe event in v4l2 encoder
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4663>
+
+2023-05-19 15:50:11 +0200  Ruben Gonzalez <rgonzalez@fluendo.com>
+
+	* docs/gst_plugins_cache.json:
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* gst/alpha/gstalpha.c:
+	* gst/multifile/gstimagesequencesrc.c:
+	  doc: Fix newline char between authors
+	  Found running `gst-inspect-1.0 -a |& grep -v ":" | grep @`
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4682>
+
+2023-05-18 11:58:51 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2allocator.c:
+	* sys/v4l2/gstv4l2bufferpool.c:
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Fix stalls on empty buffer
+	  Drivers may signal end of sequence using an empty buffer and LAST buffer
+	  set, or just an empty buffer on certain legacy implementation. When this
+	  occured, we'd send GST_V4L2_FLOW_LAST_BUFFER were the code expected
+	  GST_FLOW_EOS. Stop abusing GST_FLOW_EOS and port all the code to the new
+	  GST_V4L2_FLOW_LAST_BUFFER.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4669>
+
+2023-02-16 16:16:47 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/fourcc.h:
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Add support for SpeedHQ video codec
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3982>
+
+2023-05-12 13:49:20 +1000  Matthew Waters <matthew@centricular.com>
+
+	* ext/qt6/gstplugin.cc:
+	* ext/qt6/gstqml6glmixer.cc:
+	* ext/qt6/gstqml6glmixer.h:
+	* ext/qt6/gstqml6gloverlay.cc:
+	* ext/qt6/gstqml6glsink.cc:
+	* ext/qt6/gstqt6elements.h:
+	* ext/qt6/meson.build:
+	* tests/examples/qt6/meson.build:
+	* tests/examples/qt6/qmlmixer/main.cpp:
+	* tests/examples/qt6/qmlmixer/main.qml:
+	* tests/examples/qt6/qmlmixer/meson.build:
+	* tests/examples/qt6/qmlmixer/mixer.qml:
+	* tests/examples/qt6/qmlmixer/qmlmixer.qrc:
+	  qml6: add a mixer element
+	  Can take multiple input streams and a qml scene and layout the input
+	  videos inside the qml scene.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4609>
+
+2023-05-06 11:17:43 +0800  Shengqi Yu <shengqi.yu@mediatek.com>
+
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: fix some errors in probe_caps_for_fromat
+	  1, there is a mistake when print stepwise.max_height, fix it
+	  2, modify the calculation of width and height under the step wise
+	  condition
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4562>
+
+2023-05-17 22:58:46 +0200  Ruben Gonzalez <rgonzalez@fluendo.com>
+
+	* README.md:
+	  README.md: fix current version
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4662>
+
+2023-04-27 13:54:58 +0800  Hou Qi <qi.hou@nxp.com>
+
+	* sys/v4l2/gstv4l2videoenc.c:
+	  v4l2videoenc: fix set format failure when needs reset encoder
+	  In cases that encoder needs to reset format, there is race while draining.
+	  v4l2videoenc finish() sends CMD_STOP command to driver, and desire to return
+	  GST_FLOW_OK. But at this time, encoder CAPTURE may have dequeued the last
+	  buffer and got eos. finish() return value changes to be GST_FLOW_EOS which
+	  causes set format fail. So there is no need to check return value for finish()
+	  when set format.
+	  Also need to flush encoder after draining to make sure flush is finished.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4495>
+
+2023-05-15 11:45:12 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/gstqtmux.c:
+	  qtmux: Fix extraction of CEA608 data from S334-1A packets
+	  The index is already incremented by 3 every iteration so multiplying it
+	  by 3 additionally on each array access is doing it twice and does not
+	  work.
+	  This caused invalid files to be created if there's more than one CEA608
+	  triplet in a buffer, and out of bounds memory reads.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4634>
+
+2023-05-15 19:10:39 +1000  Jan Schmidt <jan@centricular.com>
+
+	* gst/multifile/gstsplitmuxpartreader.c:
+	  splitmuxsrc: Make PTS contiguous by preference
+	  Make splitmuxsrc deal better with stream reordering by
+	  making the largest observed PTS contiguous in the
+	  next fragment. Previously, it selected DTS, but then
+	  aligned that with the segment start of the next fragment,
+	  which holds PTS values - leading to glitches in
+	  streams that don't have PTS = DTS at the start.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4637>
+
+2023-05-12 13:27:19 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* ext/pulse/pulsesink.c:
+	* ext/pulse/pulsesink.h:
+	* ext/pulse/pulsesrc.h:
+	  pulse: Change bitfield booleans to normal gbooleans
+	  Assigning TRUE (1) to a signed 1 bit integer will cause truncation
+	  from 1 to -1 because the only non-zero value that can be stored is -1
+	  due to how two's-complement works.
+	  As this is a proper GObject let's not bother with all this and simply
+	  use a normal gboolean instead.
+	  ../subprojects/gst-plugins-good/ext/pulse/pulsesink.c:1490:19: warning: implicit truncation from 'int' to a one-bit
+	  wide bit-field changes value from 1 to -1 [-Wsingle-bit-bitfield-constant-conversion]
+	  pbuf->in_commit = TRUE;
+	  ^ ~~~~
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4617>
+
+2023-05-12 13:12:04 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	  rtpjitterbuffer: Fix uninitialized variable compiler warning
+	  It could indeed be used uninitialized, but only if one of the
+	  g_return_val_if_fail() caused an early return.
+	  ../subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c: In function ‘rtp_jitter_buffer_append_query’:
+	  ../subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c:1234:10: warning: ‘head’ may be used uninitialized
+	  [-Wmaybe-uninitialized]
+	  1234 |   return head;
+	  |          ^~~~
+	  ../subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c:1232:12: note: ‘head’ was declared here
+	  1232 |   gboolean head;
+	  |            ^~~~
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4616>
+
+2023-05-11 20:01:45 +0200  Piotr Brzeziński <piotr@centricular.com>
+
+	* sys/osxvideo/osxvideosink.m:
+	  macos: Set activation policy in osxvideosink and glimagesink
+	  Upon creating a window, glimagesink and osxvideosink now set the policy to
+	  NSApplicationActivationPolicyRegular, which lets us show an icon in the Dock
+	  for convenience and appear in the top menu bar like other apps.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4573>
+
+2023-05-10 22:35:27 +0200  Piotr Brzeziński <piotr@centricular.com>
+
+	* sys/osxvideo/cocoawindow.h:
+	* sys/osxvideo/cocoawindow.m:
+	* sys/osxvideo/osxvideosink.h:
+	* sys/osxvideo/osxvideosink.m:
+	  macos: Remove old NSApp workaround related code
+	  This is no longer needed since the introduction of `gst_macos_main()` in 1.22.
+	  Before that existed, we had a patch for GLib in Cerbero, which did work but made it
+	  impossible to update GLib at all. The code being removed was a fail-safe in case of
+	  running without said patch being applied. It's no longer needed, since for macOS
+	  we just wrap our GStreamer with an NSApplication using `gst_macos_main()`.
+	  Warnings will be displayed if no NSApp/NSRunLoop is found wherever needed,
+	  pointing the user towards using the new API.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4366>
+
+2023-05-11 16:25:11 +0100  Tim-Philipp Müller <tim@centricular.com>
+
+	* tests/check/elements/qtdemux.c:
+	* tests/files/editlists.mp4.gz.gz:
+	  qtdemux: add unit test for edit list regression
+	  File is the mp4 file from #2549 with the mdat atom
+	  zeroed out and compressed. We compress twice because
+	  apparently compressing 5MB of zeroes effectively in
+	  one run is too difficult for gzip.
+	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2549
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4560>
+
+2023-05-05 19:41:34 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  Revert "qtdemux: fix conditions for end of segment in reverse playback"
+	  This reverts commit 9deb3c27acd4161f810cd782f03bcdaccf2643c7.
+	  The test case that was described in the associated MR
+	  (https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/262)
+	  remains adequately fixed by a related MR that was merged later
+	  (https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/275).
+	  It introduced incorrect logic that broke edit lists as described in
+	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2549
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2549
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4560>
+
+2023-05-05 18:27:14 +0200  Piotr Brzeziński <piotr@centricular.com>
+
+	* sys/osxvideo/osxvideosink.m:
+	  osxvideosink: fix deadlock upon closing output window
+	  Invoking gst_osx_video_sink_osxwindow_destroy() can currently cause a deadlock
+	  because showFrame() keeps trying to get the same lock as well. Moving the lock
+	  closer to where it's actually needed seems to be enough to fix the issue for now.
+	  Reported-by: Alexande B <abobrikovich@gmail.com>
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4559>
+
+2023-05-09 17:28:49 +0200  François Laignel <francois@centricular.com>
+
+	  rtpmanager/rtsession: data race leading to critical warnings
+	  This is a fix for a data race leading to:
+	  > GLib-CRITICAL: g_hash_table_foreach:
+	  >   assertion 'version == hash_table->version' failed
+	  Identified sequence:
+	  * `rtp_session_on_timeout` acquires the lock on `session` and proceeds with its
+	  processing.
+	  * `rtp_session_process_rtcp` is called (debug log : received RTCP packet) and
+	  attempts to acquire the lock on `session`, which is still held by
+	  `rtp_session_on_timeout`.
+	  * as part of an hash table iterator, `rtp_session_on_timeout` transitively
+	  invokes `source_caps` which releases the lock on `session` so as to call
+	  `session->callbacks.caps`.
+	  * Since `rtp_session_process_rtcp` was waiting for the lock to be released, it
+	  succeeds in acquiring it and proceeds with `rtp_session_process_rr` which
+	  transitively calls `g_hash_table_insert` via `add_source`.
+	  * After `source_caps` re-acquires the lock and gives the control flow back to
+	  `rtp_session_on_timeout`, the hash table iterator is changed, resulting in the
+	  assertion failure.
+	  This commits copies `sess->ssrcs[sess->mask_idx]` and iterates on the copy so
+	  the iterator is not affected by a concurrent change due to the lock being
+	  released in the `source_caps` callback.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4555>
+
+2023-05-09 14:37:25 +0100  Philippe Normand <philn@igalia.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/dtmf/gstrtpdtmfdepay.c:
+	  rtpdtmfdepay: Classify as RTP element
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4582>
+
+2023-05-09 14:36:56 +0100  Philippe Normand <philn@igalia.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/dtmf/gstrtpdtmfsrc.c:
+	  rtpdtmfsrc: Classify as RTP source
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4582>
+
+2023-05-03 21:05:54 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+
+	* ext/qt/meson.build:
+	* ext/qt6/meson.build:
+	* meson_options.txt:
+	  meson: Add more qt options and eliminate all automagic
+	  The qt5 and qt6 plugins will now correctly error out if you enable the
+	  option, and you can also now explicitly ensure that wayland, x11,
+	  eglfs support is actually functional by enabling the options. It was
+	  too easy to build non-functional support for these.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4537>
+
+2023-05-04 18:58:13 +0100  Tim-Philipp Müller <tim@centricular.com>
+
+	* ext/jack/gstjackaudioclient.c:
+	  jack: tone down log ERRORs in case no JACK server is running
+	  jackaudiosink and jackaudiosrc have a rank and might be plugged
+	  as part of auto-plugging inside playbin and playsink or the
+	  autoaudiosink/autoaudiosrc elements, so we don't really want to
+	  spew ERROR log messages in that case, which is consistent with
+	  what alsasink and pulseaudiosink do.
+	  This is less noticable on Linux because pulseaudiosink has a
+	  higher and alsasink which has the same rank comes before jack
+	  in the alphabet.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4545>
+
+2023-05-03 15:42:01 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/videofilter/gstvideoflip.c:
+	  videoflip: fix setting of method property at construction time
+	  Since c2f890ab, element properties are gathered from the parse-launch
+	  line and passed at object construction.
+	  This caused the following issue to happen in videoflip:
+	  * videoflip installed a CONSTRUCT property named method, now deprecated
+	  * videoflip now also overrides that property with a video-direction
+	  property
+	  GObject construction causes method to be set first at construct time,
+	  with the user-provided value, then video-direction with the default
+	  value.
+	  The user-provided value was thus overridden, causing a regression.
+	  Fix by not installing the properties as CONSTRUCT, and explicitly
+	  implementing constructed() instead in order to ensure that we do still
+	  call gst_video_flip_set_method() at least once during construction.
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2529
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4536>
+
+2023-05-04 02:41:09 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* gst/rtp/gstrtpvp8pay.c:
+	* gst/rtp/gstrtpvp8pay.h:
+	* gst/rtp/gstrtpvp9pay.c:
+	* gst/rtp/gstrtpvp9pay.h:
+	  rtpvp8pay: rtpvp9pay: access picture_id property atomically
+	  Atomically set and get the picture_id. This changeset only atomically gets
+	  the picture-id when such property is queried on the element, on every other
+	  place where it is accessed internally it is accessed directly.
+	  This is because there is no MT scenario where we would be modifying this value
+	  and reading it internally in parallel.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-05-03 20:53:41 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* gst/rtp/gstrtpvp8pay.c:
+	* gst/rtp/gstrtpvp9pay.c:
+	* tests/check/elements/rtpvp8.c:
+	  rtpvp8pay, rtpvp9pay: increment PictureID on FLUSH_START
+	  In recent versions of Chrome (M106) a change on their jitter buffer means that
+	  they are very susceptible to PictureID discontinuities.
+	  Then avoid at all cost resetting the PictureID. Moreover, according to
+	  the RFCs for VP8 and VP9 payloads; the PictureID can start off at any
+	  random value. So there is no logical problem of incrementing it here
+	  rather than resetting it, as long as it is a different PictureID.
+	  WebRTC's recent corruption issue:
+	  https://bugs.chromium.org/p/webrtc/issues/detail?id=15101
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-05-03 00:34:55 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtpvp8pay.c:
+	* gst/rtp/gstrtpvp9pay.c:
+	  rtpvp8pay, rtpvp9pay: expose picture-id as a property
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-05-02 22:21:34 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* tests/check/elements/rtpvp9.c:
+	  rtpvp9pay: tests: remove unused struct and argument on test
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-05-02 21:45:48 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/rtp/gstrtpvp9pay.c:
+	* gst/rtp/gstrtpvp9pay.h:
+	  rtpvp9pay: add picture-id-offset property
+	  Bring the VP9 payloader in sync in this regard to the VP8 payloader
+	  Allowing setting the picture id to a known value is useful when testing.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-04-25 15:36:45 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* gst/rtp/gstrtpvp9pay.c:
+	  rtpvp9pay: minor refactor of PictureID logic
+	  This brings the logic inline with the vp8pay
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-04-25 15:25:57 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* gst/rtp/gstrtpvp8pay.c:
+	  rtpvp8pay: avoid reseting PictureID if NO_PICTURE_ID mode is set
+	  There is no logical change here, as `& (1 << nbits) - 1` would produce also 0
+	  when NO_PICTURE_ID mode is choosen. However, this avoid computing a random
+	  integer that is actually unused.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-04-25 15:11:10 +0900  Camilo Celis Guzman <camilo@pexip.com>
+
+	* gst/rtp/gstrtpvp8pay.c:
+	* gst/rtp/gstrtpvp8pay.h:
+	* gst/rtp/gstrtpvp9pay.h:
+	  rtpvp8pay, rtpvp9pay: use GType like name for PictureIDMode
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4530>
+
+2023-05-03 13:21:23 +0200  Xabier Rodriguez Calvar <calvaris@igalia.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: emit no-more-pads after pruning old pads
+	  If we don't do that, clients can rely on this signal to see the final pad
+	  topology but it won't be the real one as some of them will disappear after
+	  emitting that signal. This can happen after injecting a different init segment.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4535>
+
+2023-05-01 14:01:02 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2deviceprovider.c:
+	  v4l2: device provider: Fix GMainLoop leak
+	  On very quick start/stop, the mainloop may never be run. As a side
+	  effect, our idle stop function is not really being ran, so we can't rely
+	  on that to free the main loop. Simply unref the mainloop when the
+	  thread have completely stop.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4521>
+
+2023-03-29 15:34:36 +0200  Carlos Rafael Giani <crg7475@mailbox.org>
+
+	* tests/check/elements/qtdemux.c:
+	  qtdemux: Only set appsink sync property and check for async state changes
+	  By keeping async to TRUE, a deadlock is avoided where the appsink is
+	  filled with data after a flushing seek but before its PAUSED->PLAYING
+	  state change finishes. If that happens, the appsink is stuck, because
+	  its internal condition variable waits for the appsink to have more room
+	  for data. The basesink's preroll lock is held during this, and it also
+	  tries to acquire that lock during the state change -> deadlock.
+	  By keeping async to TRUE, this flood of data does not happen.
+	  Also, setting the max-buffers property to 1 is unnecessary - the test
+	  runner will anyway detect excess memory usage if it happens.
+	  Other property adjustments turned out to just be redundant.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4200>
+
+2023-03-03 12:10:38 +0100  Carlos Rafael Giani <crg7475@mailbox.org>
+
+	* gst/isomp4/qtdemux.c:
+	* gst/isomp4/qtdemux.h:
+	* gst/isomp4/qtdemux_tags.c:
+	* tests/check/elements/qtdemux.c:
+	* tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-itunes.m4a:
+	* tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-with-itunsmpb.m4a:
+	* tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-without-itunsmpb.m4a:
+	  qtdemux: Add audio clipping meta when playing gapless m4a content
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4200>
+
+2023-02-27 20:36:20 +0100  Carlos Rafael Giani <crg7475@mailbox.org>
+
+	* gst/isomp4/qtdemux_tags.c:
+	  qtdemux: use qtdemux debug category instead of default in qtdemux_tags.c
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4200>
+
+2023-04-28 16:06:24 +0100  Tim-Philipp Müller <tim@centricular.com>
+
+	* REQUIREMENTS:
+	* docs/gst_plugins_cache.json:
+	* ext/amrnb/GstAmrnbEnc.prs:
+	* ext/amrnb/README:
+	* ext/amrnb/amrnb.c:
+	* ext/amrnb/amrnbdec.c:
+	* ext/amrnb/amrnbdec.h:
+	* ext/amrnb/amrnbenc.c:
+	* ext/amrnb/amrnbenc.h:
+	* ext/amrnb/meson.build:
+	* ext/amrwbdec/README:
+	* ext/amrwbdec/amrwb.c:
+	* ext/amrwbdec/amrwbdec.c:
+	* ext/amrwbdec/amrwbdec.h:
+	* ext/amrwbdec/meson.build:
+	* ext/meson.build:
+	* meson_options.txt:
+	* tests/check/elements/amrnbenc.c:
+	* tests/check/meson.build:
+	  amrnb, amrwbdec: move AMR-NB and AMR-WB plugins to -good
+	  Fedora ships these libraries as part of the main distribution now,
+	  and they are decades old anyway so don't implement any of the newer
+	  features.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4512>
+
+2023-05-01 14:14:25 +0200  François Laignel <francois@centricular.com>
+
+	  rtpmanager/rtsession: race conditions leading to critical warnings
+	  While testing the [implementation for insertable streams] in `webrtcsink` &
+	  `webrtcsrc`, I encountered critical warnings, which turned out to result from
+	  two race conditions in `rtpsession`. Both race conditions produce:
+	  > GLib-CRITICAL: g_hash_table_foreach:
+	  >   assertion 'version == hash_table->version' failed
+	  This commit fixes one of the race conditions observed.
+	  In its simplest form, the test consists in 2 pipelines and a Signalling server:
+	  * pipelines_sink: audiotestsrc ! webrtcsink
+	  * pipelines_src: webrtcsrc ! appsrc
+	  1. Set `pipelines_sink` to `Playing`.
+	  2. The Signalling server delivers the `producer_id`.
+	  3. Initialize `pipelines_src` to establish a session with `producer_id`.
+	  4. Set `pipelines_src` to `Playing`.
+	  5. Wait for a buffer to be received by the `appsrc`.
+	  6. Set `pipelines_src` to `Null`.
+	  7. Set `pipelines_sink` to `Null`.
+	  The race condition happens in the following sequence:
+	  * `webrtcsink` runs a task to periodically retrieve statistics from `webrtcbin`.
+	  This transitively ends up executing `rtp_session_create_stats`.
+	  * `pipelines_sink` is set to `Null`.
+	  * In `Paused` to `Ready`, `gst_rtp_session_change_state()` calls
+	  `rtp_session_reset()`.
+	  * The assertion failure occurs when `rtp_session_reset` is called while
+	  `rtp_session_create_stats` is executing.
+	  This is because `rtp_session_create_stats` acquires the lock on `session` prior
+	  to calling `g_hash_table_foreach`, but `rtp_session_reset` doesn't acquire the
+	  lock before calling `g_hash_table_remove_all`.
+	  Acquiring the lock in `rtp_session_reset` fixes the issue.
+	  [implementing insertable streams support]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1176
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4528>
+
+2023-05-02 11:32:01 +0200  Xabier Rodriguez Calvar <calvaris@igalia.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix segfault in cenc sample grouping
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4523>
+
+2023-04-26 15:58:23 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
+	* sys/v4l2/gstv4l2bufferpool.h:
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: pool: Flush events on capture queue
+	  Unfortunately streamoff does not flush the events, and this can cause all
+	  sort of issues. Flush events on capture queue. We also return
+	  GST_V4L2_FLOW_RESOLUTION_CHANGE in case a resolution change was seen.
+	  This allow skipping streamon(capture) on flush, which could lead to a
+	  configuration miss-match, or failure if the buffers aren't of the right
+	  size.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-21 13:33:11 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Detect flushes while setting up the capture
+	  As we missed the fact we were flushing, we could create and activate
+	  that buffer pool, and wait on it, causing a hang. We detect that we
+	  are flushing by checking the related pad state.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-21 13:30:43 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Don't copy buffer when flushing
+	  Threshold handling can race with flushing operation. This can lead to
+	  avoidable buffer copies. Simply check and return the flushing status.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-19 14:19:13 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	* sys/v4l2/gstv4l2videodec.h:
+	  v4l2: videodec: Don't forcibly drain on resolution changes
+	  Let the driver detects the change and reconfigure the capture side
+	  transparently from there. This avoid reallocation of the output buffers,
+	  and eliminates the need to stop and restart the capture task. This is
+	  only happening if the driver have support for this, otherwise the old
+	  behaviour is maintained.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-18 11:42:06 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Remove the spurious srccaps probe
+	  We don't need to probe the srccaps in set_format() anymore, this
+	  handled already in the capture thread while setting up the capture
+	  queue.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-17 16:10:34 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Improve few logs
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-17 16:07:21 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Only warn of incomplete drain on success
+	  We may have hit an error, or just flushing in order to stop the thread,
+	  in which case, not having drain everything is expected and not a
+	  driver bug.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-17 16:05:35 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2: bufferpool: Don't assert when orphaning is not needed
+	  This may happen when shutting down and should not cause
+	  any harm. This removes the associated assert when shutting
+	  down the pipeline, notably with CTRL+C.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-14 18:54:22 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	* sys/v4l2/gstv4l2videodec.h:
+	  v4l2: videodec: Wait for source change event
+	  Stop doing capture buffer allocation based on guesses
+	  and wait for the source change event when available.
+	  Unlike stateless decoder, the stateful decoder is not aware of
+	  the coded resolution, and this may lead to the wrong result
+	  even when using TRY_FMT.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-14 22:22:06 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
+	* sys/v4l2/gstv4l2bufferpool.h:
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2: object: Move the GstPoll into v4l2object
+	  Moves the GstPoll from the buffer pool into v4l2object. This will be
+	  needed to poll for events before the pool has been created.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-14 23:38:34 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2: object: Fix bogus debug objects pointers
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-14 09:53:15 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	* sys/v4l2/gstv4l2videodec.h:
+	  v4l2: videodec: Move the capture setup into the processing loop
+	  In previous implementation that job was split between handle_frame and
+	  the processing loop and it wasn't clear if this mechanism was race
+	  free. The capture setup would also be tried for every buffer, which was
+	  not necessary.
+	  This also simplify the handling of SRC_CH event, dropping the unneeded
+	  atomic boolean.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-14 09:51:39 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Ensure object is inactive on failure
+	  Sprinkle stop() calls in error case to guaranty that the capture object
+	  is inactive on failure. Not doing so could allow some code to be called
+	  in unexpected (and possibly undefined) conditions.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
+
+2023-04-21 09:58:36 +0800  jeri.li <jeri.li@mediatek.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
+	  v4l2bufferpool: add lock as atomic operation for seek
+	  When seek flush, gst v4l2 buffer pool flush is not atomic which will
+	  lead double enqueue buffer (qbuf) issue, and v4l2 buffer pool qbuf is
+	  also not atomic which will lead no free buffer found in the pool.
+	  1. add lock for calculate enqueue number in streamon function
+	  2. add lock for v4l2 capture end streamoff in pool flush function
+	  3. lock the whole funciton of v4l2 buffer pool qbuf, then the buffer
+	  pool index and qbuf operation are atomic
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4465>
+
+2023-04-27 22:12:15 +0800  Haihua Hu <jared.hu@nxp.com>
+
+	* sys/v4l2/gstv4l2src.c:
+	  v4l2src: fix cannot reuse current caps when fixate caps in negotiation
+	  when regotiation happens, v4l2src will check if it can reuse current caps,
+	  but we need check if current caps is subset of all query caps from downstream
+	  instead of check it with query caps one by one.
+	  Assuming that the current caps is not the subset of first caps from query caps,
+	  it will go to try fmt. when try fmt success, v4l2src will make pending_set_fmt
+	  to TRUE and going to reset.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4500>
+
+2023-04-28 12:51:08 +0300  Jordan Petridis <jordan@centricular.com>
+
+	* ext/jack/gstjack.c:
+	  jack: return TRUE during init when failing to dlopen
+	  If we return FALSE, that means the plugin won't be tried again,
+	  even if jack is available afterwards.
+	  Followup to 689dbd1fbe43f293d7a46d5fa0fe1ddcfbea50d6
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4507>
+
+2023-04-27 16:33:25 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/multifile/gstsplitmuxsink.c:
+	  Revert "splitmuxsink: Avoid assertion when WAITING_GOP_COLLECT on reference context"
+	  This reverts commit f29c19be5821d26a6b7682d0c69ff31d0815e072. If this is
+	  called for the reference context then we would run into an infinite
+	  loop, which is not really better than an assertion.
+	  By fixing up DTS to never be ahead of the PTS in the previous commit
+	  this situation should be impossible to hit now.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4498>
+
+2023-04-27 16:29:53 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/multifile/gstsplitmuxsink.c:
+	  splitmuxsink: Catch invalid DTS to avoid running into problems later
+	  DTS > PTS makes no sense, so we clamp DTS to the PTS. Also if there's a
+	  PTS but no DTS, then assume that PTS=DTS to make sure we're not working
+	  with a much older DTS.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4498>
+
+2023-04-27 13:00:42 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Fix handling of `*` control path
+	  Regression introduced by 7f9d689572843ff9e0b8a92128034a8fc4a14d96.
+	  Thanks to Tristan Matthews for reporting this.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4497>
+
+2022-12-09 11:16:21 +0100  Sebastian Szczepaniak <s.szczepaniak@metrological.com>
+
+	* gst/isomp4/fourcc.h:
+	* gst/isomp4/qtdemux.c:
+	* gst/isomp4/qtdemux_types.c:
+	  qtdemux: Add support for cenc sample grouping
+	  Co-authored-by: Xabier Rodriguez Calvar <calvaris@igalia.com>
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3551>
+
+2023-03-19 15:35:29 -0300  Thibault Saunier <tsaunier@igalia.com>
+
+	* docs/meson.build:
+	  doc: Avoid shelling out to hotdoc to generate plugins config files
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4479>
+
+2023-04-20 16:31:04 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/videofilter/gstvideoflip.c:
+	* gst/videofilter/gstvideoflip.h:
+	  videoflip: check that stream actually changed when resetting
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4377>
+
+2023-04-13 14:19:29 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/videofilter/gstvideoflip.c:
+	* gst/videofilter/gstvideoflip.h:
+	* tests/check/elements/videoflip.c:
+	  videoflip: reset orientation if not present in a tag update
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4377>
+
+2023-04-13 14:06:19 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/videofilter/gstvideoflip.c:
+	* gst/videofilter/gstvideoflip.h:
+	* tests/check/elements/videoflip.c:
+	  videoflip: handle tag list scopes
+	  STREAM taglist can now overrides the orientation from the GLOBAL
+	  taglist, but not the other way around.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4377>
+
+2023-04-10 13:01:14 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* gst/videofilter/gstvideoflip.c:
+	* tests/check/elements/videoflip.c:
+	  videoflip: reset orientation on new stream
+	  Fix the following use:
+	  - upstream sends a video with a rotation tag, say 90°
+	  - upstream switches to another video without rotation
+	  - the second video was still rotated by videoflip
+	  Fix this by resetting the orientation when receiving STREAM_START.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4377>
+
+2023-04-13 11:37:13 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* tests/check/elements/videoflip.c:
+	  videoflip: add test rotating from tags
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4377>
+
+2023-03-29 19:50:19 +0300  Jordan Petridis <jordan@centricular.com>
+
+	* ext/jack/gstjack.c:
+	* ext/jack/gstjack.h:
+	* ext/jack/gstjackaudioclient.c:
+	* ext/jack/gstjackaudioclient.h:
+	* ext/jack/gstjackaudiosink.c:
+	* ext/jack/gstjackaudiosink.h:
+	* ext/jack/gstjackaudiosrc.c:
+	* ext/jack/gstjackaudiosrc.h:
+	* ext/jack/gstjackloader.c:
+	* ext/jack/gstjackloader.h:
+	* ext/jack/meson.build:
+	* tests/examples/jack/meson.build:
+	  jack: Dynamically load libjack at runtime instead of linking
+	  In order to provide build and provide the jack plugin with the prebuilt
+	  binaries of gstreamer we distribute with releases, we can not depend
+	  on an external dependency nor can we ship plugins linking to libraries
+	  we don't provide.
+	  We can also not provide jack ourselves, as it would likely cause a
+	  mismatch with the jack daemon on the host.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4350>
+
+2023-04-18 15:28:30 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/meson.build:
+	  meson: Add a wrap file for libgudev
+	  And allow fallback to it.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4447>
+
+2023-04-17 11:36:55 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* ext/adaptivedemux2/dash/gstmpdclient.c:
+	  dash: mpdclient: fix divide by 0 if segment has no duration
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4436>
+
+2021-12-23 18:23:57 +0900  Seungha Yang <seungha@centricular.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/deinterlace/gstdeinterlace.c:
+	* gst/deinterlace/gstdeinterlacemethod.c:
+	* gst/deinterlace/gstdeinterlacemethod.h:
+	* gst/deinterlace/tvtime-dist.c:
+	* gst/deinterlace/tvtime-dist.h:
+	* gst/deinterlace/tvtime.orc:
+	* gst/deinterlace/tvtime/linear.c:
+	* gst/deinterlace/tvtime/vfir.c:
+	* gst/deinterlace/tvtime/weave.c:
+	* gst/deinterlace/tvtime/weavebff.c:
+	* gst/deinterlace/tvtime/weavetff.c:
+	* gst/deinterlace/yadif.c:
+	  deinterlace: Add support for high bitdepth planar YUV formats
+	  Add C implementation for high bitdepth planar YUV formats
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1476>
+
+2021-12-22 19:49:35 +0900  Seungha Yang <seungha@centricular.com>
+
+	* gst/deinterlace/yadif.c:
+	  deinterlace: yadif: Prettify indentation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1476>
+
+2023-04-17 09:28:43 +0200  Edward Hervey <edward@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix av1C parsing
+	  This is a regression introduced by
+	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3882
+	  The av1c codec configuration parsing would always fail due to an off-by-one
+	  error, the content of an atom starting at offset 8 (i.e. the 9th byte) and not
+	  9 (the 10th byte).
+	  Also introduce a break in order to not get stray warnings
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4433>
+
+2023-04-13 00:55:46 +0200  Mathieu Duponchelle <mathieu@centricular.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/rtpmanager/gstrtpmux.c:
+	  docs: mark GstRTPMux as plugin API
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4408>
+
+2023-04-12 17:18:13 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2: Fix use after free of fmtdesc part 2
+	  Add missing code in merge commit e890e6e8d8bd
+	  ("v4l2: Fix use after free of fmtdesc"). The v4l2object code was
+	  missing.
+	  Related to https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4317
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4411>
+
+2023-04-12 17:18:13 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: Fix use after free of fmtdesc
+	  The decoder needs to force another enumeration of the format. For
+	  this it was clearing the v4l2object insternal list, leaving a fmtdesc
+	  pointer pointing to freed memory. This patch clears the fmtdesc pointer
+	  that has just been free. It also makes sure the probe function does not
+	  use the cached formats list. The probe function will restore the current
+	  fmtdesc pointer based on the currently configured pixelformat.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4317>
+
+2023-03-31 10:32:54 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2bufferpool.c:
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Prefer acquired caps over anything downstream
+	  As we don't have anything smart in the fixation process, we may endup with
+	  a format that has a lower bitdepth, even if downstream can handle higher
+	  depth. it is notably the case when negotiating with deinterlace, which places
+	  is non-passthrough caps before its passthrough one. This makes the generic
+	  fixation prefer the formats natively supported by deinterlace element over
+	  the HW 10bit format. As some HW can downscale 10bit to 8bit, this can break
+	  10bit decoding.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4317>
+
+2023-03-31 10:31:07 -0400  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+
+	* sys/v4l2/gstv4l2videodec.c:
+	  v4l2: videodec: Remove leading space in comment
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4317>
+
+2023-03-03 18:40:22 +0000  Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+
+	* docs/gst_plugins_cache.json:
+	* gst/multifile/gstimagesequencesrc.c:
+	  imagesequencesrc: Properly set default location
+	  Noticed this because the generic_states test kept segfaulting at random.
+	  GLibC 2.37 can crash when NULL is supplied as a format string.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4109>
+
+2023-04-12 12:47:02 +0100  Tim-Philipp Müller <tim@centricular.com>
+
+	* gst/multifile/gstimagesequencesrc.c:
+	* gst/multifile/gstmultifilesrc.c:
+	  multifile: error out if no filename was set
+	  Fixes #2483
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4404>
+
+2022-11-22 23:06:02 +0900  Seungha Yang <seungha@centricular.com>
+
+	* docs/gst_plugins_cache.json:
+	* gst/udp/gstudp.c:
+	* gst/udp/gstudpnetutils.c:
+	* gst/udp/gstudpnetutils.h:
+	* gst/udp/gstudpsink.c:
+	* gst/udp/gstudpsrc.c:
+	* gst/udp/gstudpsrc.h:
+	* tests/check/elements/udpsrc.c:
+	  udpsrc: Add support for IGMPv3 SSM
+	  Adding "multicast-source" property to support Source Specific Muliticast
+	  RFC 4604. The source can be multiple address with '+' (for positive
+	  filter) or '-' (negative filter) prefix, or URI query can be used.
+	  Note that negative filter is not implemented yet and it will be
+	  ignored
+	  Example:
+	  gst-launch-1.0 uridecodebin \
+	  uri=udp://{ADDRESS}:PORT?multicast-source=+SOURCE0+SOURCE1
+	  Inspired by:
+	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2620
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3485>
+
+2023-04-11 17:54:23 +0200  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: fix critical when using an unsupported URI
+	  adaptivedemux2 only supports http(s), trying to use it with, say,
+	  file:// was raising a CRITICAL in libsoup.
+	  Fix #2476
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4396>
+
+2023-04-06 08:55:46 +0200  Matthias Fuchs <matthias1.fuchs@zeiss.com>
+
+	* ext/qt/qtwindow.cc:
+	  qtwindow: unref caps in destructor
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4393>
+
+2023-04-04 09:21:47 +0200  Edward Hervey <edward@centricular.com>
+
+	* gst/rtpmanager/rtptwcc.c:
+	* tests/check/elements/rtpsession.c:
+	  twcc: Better handle duplicate packets
+	  The previous code would only check if two packets in a row were duplicates. If
+	  not (i.e. a packet is a duplicate of a packet received slightly before) the code
+	  would generate completely bogus FCI because it assumes there were no duplicates
+	  present in the array.
+	  In order to be efficient, just store all received packets and remove the
+	  duplicates just before the FCI is generated once the array of observations have
+	  been sorted by seqnum.
+	  Fixes TWCC usage with moderate to high packet duplication.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4328>
+
+2023-04-05 19:36:15 +0300  Jordan Petridis <jordan@centricular.com>
+
+	* ext/jack/gstjackaudiosink.c:
+	* ext/jack/gstjackaudiosrc.c:
+	* ext/jack/meson.build:
+	  jack: remove version guards from the code
+	  We already require >= 1.9.7 in meson and thus we can remove
+	  the older codepath.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4348>
+
+2022-11-04 22:04:21 +0100  Alexande B <abobrikovich@gmail.com>
+
+	* sys/osxvideo/cocoawindow.m:
+	  osxvideosink: fix broken aspect ration and frame drawing region
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3336>
+
+2023-04-04 19:23:14 +0300  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Skip PTs with caps incompatible to the global caps
+	  Otherwise empty caps are created while all following code assumes that
+	  the caps will have exactly one structure, and then run into assertions.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4339>
+
+2023-04-04 23:58:36 +1000  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	  adaptivedemux: Don't parse URI unnecessarily
+	  Short-circuit parsing and recreating the playlist URI if
+	  no HLS directives are going to be applied to it.
+	  Fixes problems playing some streams (YouTube) that have
+	  unneeded escaped characters in the URI and then complain
+	  when GStreamer removes the escaping
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4335>
+
+2023-03-28 15:29:46 +0800  Shengqi Yu <shengqi.yu@mediatek.com>
+
+	* docs/gst_plugins_cache.json:
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: Add support for YVU420M format
+	  This is a multi-planar format with planes non contiguous in memory. It
+	  is intended to be used only in drivers and applications that support the
+	  multi-planar API.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4287>
+
+2023-03-23 16:40:54 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* gst/rtp/gstrtpjpegdepay.c:
+	  rtpjpegdepay: fix logic error when checking if an EOI is present
+	  We wouldn't add the missing EOI marker if the frame ended with
+	  either 0xFF NN or 0xNN D9.
+	  Fixes #2407
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4256>
+
+2023-03-20 16:35:45 +0100  Piotr Brzeziński <piotr@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix seek adjustment with SNAP_AFTER flag
+	  With GST_SEEK_FLAG_SNAP_AFTER present, the previous version would
+	  adjust seek time based on the keyframe farthest away from desired_time.
+	  This was incorrect, because we always want the *earliest* suitable keyframe
+	  to seek to, not the last one.
+	  With this fix, in case of the SNAP_AFTER, we now look for the closest keyframe
+	  that can be found after desired_time. Behaviour for SNAP_BEFORE should remain
+	  unchanged.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4183>
+
+2023-01-31 16:02:03 +0100  Michael Tretter <m.tretter@pengutronix.de>
+
+	* docs/gst_plugins_cache.json:
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2object: mark jpeg as parsed
+	  Assuming that V4L2 CAPTURE devices always use one buffer per JPEG image, we can
+	  always mark JPEGs provided by a V4L2 element as parsed.
+	  The V4L2 elements require that JPEG images sent to V4L2 OUTPUT devices must
+	  always be parsed.
+	  This is necessary to link a V4L2 CAPTURE device with a V4L2 OUTPUT device
+	  without explicitly marking the stream as parsed or adding a jpegparse into the
+	  pipeline.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4229>
+
+2023-03-20 11:20:30 +0100  Edward Hervey <edward@centricular.com>
+
+	* gst/rtp/gstrtph264pay.c:
+	* gst/rtp/gstrtph265pay.c:
+	  plugins: Fix wrong enum usage
+	  gcc 13 now detects conflicting enum usages. Fix the various cases where it was wrong
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4225>
+
+2023-02-15 18:06:36 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	  adaptivedemux2: Don't blindly set the main manifest URI as referer
+	  There's no guarantee it will *actually* be the URI which refered to what we are
+	  downloading. It could be a stream URI or anything else.
+	  Instead of putting something wrong, put no (specific) referer as a better choice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3972>
+
+2023-02-15 17:32:39 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	  hlsdemux2: Don't set a referer when updating playlists
+	  In the same way we don't for regular playlists in the base class.
+	  If there is a referer specified by the app/user, the downloadhelper will set it
+	  accordingly.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3972>
+
+2023-03-17 17:13:39 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/matroska/matroska-read-common.c:
+	  matroskademux: Make gst_byte_reader_get_data() usage less confusing
+	  This is effectively the same behaviour but retrieving 0 bytes of data is
+	  confusing to read.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4210>
+
+2023-03-17 16:48:51 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* ext/flac/gstflacenc.c:
+	  flacenc: Fix mapping of GStreamer image tag type to FLAC image tag type
+	  These enums are not compatible so just casting them does not work.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4210>
+
+2023-03-17 16:32:45 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* sys/oss/gstossaudio.c:
+	* sys/oss/gstosshelper.c:
+	  plugins: Fix various trivial clang compiler warnings
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4210>
+
+2023-03-16 20:54:14 +0100  Enrique Ocaña González <eocanha@igalia.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix crash on MSE-style flush
+	  The flowcombiner and active_streams shouldn't be cleared in the
+	  mse-bytestream variant, only in the mss-fragmented one. Otherwise the
+	  soft reset leaves qtdemux in a state where it still believes that it has
+	  streams, but they've been cleared. In that case, a null pointer
+	  dereference happens and the app crashes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4199>
+
+2023-03-15 18:52:48 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* ext/flac/gstflacdec.c:
+	* ext/gtk/gstgtkbasesink.c:
+	* ext/jpeg/gstjpegdec.c:
+	* ext/pulse/pulsesink.c:
+	* ext/pulse/pulsesrc.c:
+	* ext/pulse/pulseutil.c:
+	* ext/wavpack/gstwavpackcommon.c:
+	* gst/alpha/gstalpha.c:
+	* gst/apetag/gstapedemux.c:
+	* gst/audiofx/audioamplify.c:
+	* gst/audiofx/gstscaletempo.c:
+	* gst/audioparsers/gstac3parse.c:
+	* gst/audioparsers/gstwavpackparse.c:
+	* gst/avi/gstavidemux.c:
+	* gst/avi/gstavimux.c:
+	* gst/deinterlace/gstdeinterlace.c:
+	* gst/flv/gstflvdemux.c:
+	* gst/goom/mmx.c:
+	* gst/imagefreeze/gstimagefreeze.c:
+	* gst/isomp4/qtdemux.c:
+	* gst/isomp4/qtdemux_tags.c:
+	* gst/matroska/matroska-demux.c:
+	* gst/matroska/matroska-mux.c:
+	* gst/rtp/dboolhuff.c:
+	* gst/rtp/gstrtph263depay.c:
+	* gst/rtp/gstrtpmparobustdepay.c:
+	* gst/rtp/gstrtpsv3vdepay.c:
+	* gst/rtsp/gstrtspsrc.c:
+	* gst/videofilter/gstvideotemplate.c:
+	* gst/wavenc/gstwavenc.c:
+	* sys/oss4/oss4-sink.c:
+	* sys/v4l2/gstv4l2object.c:
+	* sys/v4l2/gstv4l2transform.c:
+	* sys/v4l2/v4l2_calls.c:
+	* tests/check/elements/dtmf.c:
+	* tests/check/elements/rtpvp8.c:
+	* tests/check/elements/rtpvp9.c:
+	* tests/check/elements/videocrop.c:
+	* tests/check/elements/videofilter.c:
+	* tests/check/elements/vp8enc.c:
+	  gst-plugins-good: re-indent with GNU indent 2.2.12
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4182>
+
+2023-03-10 13:10:16 -0500  Arun Raghavan <arun@asymptotic.io>
+
+	* docs/gst_plugins_cache.json:
+	* gst/matroska/matroska-mux.c:
+	  matroskamux: Set rate/channels in Opus template caps
+	  For some reason these were missed, and if caps didn't have them, we would emit
+	  an invalid Matroska file with a 0 value for Sampling Frequency or channels.
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2354
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4151>
+
+2023-03-10 12:06:08 -0500  Arun Raghavan <arun@asymptotic.io>
+
+	* gst/rtp/gstrtpopusdepay.c:
+	  rtpopusdepay: Assume 48 kHz if sprop-maxcapturerate is missing
+	  This matches 7587, section 6.1:
+	  >   sprop-maxcapturerate:  a hint about the maximum input sampling rate
+	  >      [...]
+	  >      bandwidths (Table 1).  By default, the sender is assumed to have
+	  >      no limitations, i.e., 48000.
+	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2354
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4151>
+
+2023-03-09 15:08:19 +0200  Itamar Marom <imarom@trigoretail.com>
+
+	* gst/multifile/gstsplitmuxsink.c:
+	  splitmuxsink: Fix docs support version
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4138>
+
+2023-03-06 19:56:08 +0000  Matt Feury <mattfeury@gmail.com>
+
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Consider "451: Parameter Not Understood" when handling broken control urls
+	  similar to https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3854
+	  it seems that some implementations return this when
+	  the server does not implement URL handling correctly
+	  this fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2334
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4123>
+
+2023-03-03 23:15:42 +0900  Seungha Yang <seungha@centricular.com>
+
+	* ext/adaptivedemux2/downloadrequest.c:
+	  adaptivedemux2: Fix MSVC build error
+	  downloadrequest.c(497): error C4013: 'atoi' undefined; assuming extern returning int
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4107>
+
+2023-03-02 18:05:08 +0100  Alicia Boya García <aboya@igalia.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Add MSE-style flush
+	  The abort() method of SourceBuffer in Media Source Extensions is
+	  expected to flush the demuxer and discard the current fragment,
+	  if any. The configuration of tracks, if any, should be preserved.
+	  qtdemux has different behavior for flush events depending on the
+	  context.
+	  This patch activates the intended behaviour only for streams of the
+	  VARIANT_MSE_BYTESTREAM type, conformant to the ISO BMFF Bytestream
+	  specification[1]. This flush behaviour is the same as the one
+	  already in use for adaptivedemux sources.
+	  [1] https://www.w3.org/TR/mse-byte-stream-format-isobmff/
+	  https://bugzilla.gnome.org/show_bug.cgi?id=795424
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4101>
+
+2023-02-22 17:18:11 +0800  Shengqi Yu <shengqi.yu@mediatek.com>
+
+	* gst/matroska/matroska-demux.c:
+	* gst/matroska/matroska-parse.c:
+	  matroskademux: Consider TrackUID==0 a warning and not handle it as error
+	  some special files whose trackUID is 0 can be played on the other
+	  player. But it cannot be played in GStreamer, because trackUID 0 will be
+	  treated as an error in matroskademux.
+	  So, it makes sense to only consider trackUID==0 a warning and not handle
+	  it as error
+	  https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1821
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4036>
+
+2021-09-15 10:21:19 -0400  Scott Kanowitz <skanowitz@echo360.com>
+
+	* gst/rtpmanager/gstrtpsession.c:
+	  rtpsession: fix a race condition during the EOS event in gstrtpsession.c
+	  This patch prevents a possible race condition from taking place between the EOS event handling and rtcp send
+	  function/thread.
+	  The condition starts by getting the GST_EVENT_EOS event on the send_rtp_sink pad, which causes two core things
+	  to happen -- the event gets pushed down to the send_rtp_src pad and all sessions get marked "bye" prior to
+	  completion of the event handler. In another thread the rtp_session_on_timeout function gets called after an
+	  expiration of gst_clock_id_wait in the rtcp_thread function. This results in a call to the
+	  ess->callbacks.send_rtcp(), which is configured as a function pointer to gst_rtp_session_send_rtcp via the
+	  RTPSessionCallbacks structure passed to rtp_session_set_callbacks in the gst_rtp_session_init function.
+	  In the race condition, the call to gst_rtp_session_send_rtcp can have the all_sources_bye boolean set to true
+	  while GST_PAD_IS_EOS(rtpsession->send_rtp_sink) evaluates to false. This is the result of gst_rtp_session_send_rtcp
+	  running before the send_rtp_sink's GST_EVENT_EOS handler completes. The exact point at which this condition occurs
+	  is if there's a context switch to the rtcp_thread right after the call to rtp_session_mark_all_bye in the
+	  GET_EVENT_EOS handler, but before the handler returns.
+	  Normally, this would not be an issue because the rtcp_thread continues to run and indirectly call
+	  gst_rtp_session_send_rtcp. However, the call to rtp_source_reset sets the sent_bye boolean to false, which ends up
+	  causing rtp_session_are_all_sources_bye to return false. This gets passed to gst_rtp_session_send_rtcp and the EOS
+	  event never gets sent.
+	  The race condition results in the EOS event never getting passed to the rtcp_src pad, which prevents the bin and
+	  pipeline from ever completing with EOS.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3798>
+
+2023-02-23 11:18:44 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Use the correct vfunc for the `push-backchannel-sample` action signal
+	  Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/446
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4050>
+
+2023-02-22 22:18:48 +0900  Seungha Yang <seungha@centricular.com>
+
+	* gst/isomp4/gstqtmux.c:
+	  qtmux: Fix assertion on caps update
+	  GstQTMuxPad.configured_caps should be protected since it's
+	  updated from streaming thread and accessed in aggregate thread
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4042>
+
+2023-02-22 11:52:21 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* po/af.po:
+	* po/az.po:
+	* po/bg.po:
+	* po/ca.po:
+	* po/cs.po:
+	* po/da.po:
+	* po/de.po:
+	* po/el.po:
+	* po/en_GB.po:
+	* po/eo.po:
+	* po/es.po:
+	* po/eu.po:
+	* po/fi.po:
+	* po/fr.po:
+	* po/fur.po:
+	* po/gl.po:
+	* po/hr.po:
+	* po/hu.po:
+	* po/id.po:
+	* po/it.po:
+	* po/ja.po:
+	* po/ka.po:
+	* po/ky.po:
+	* po/lt.po:
+	* po/lv.po:
+	* po/mt.po:
+	* po/nb.po:
+	* po/nl.po:
+	* po/or.po:
+	* po/pl.po:
+	* po/pt_BR.po:
+	* po/ro.po:
+	* po/ru.po:
+	* po/sk.po:
+	* po/sl.po:
+	* po/sq.po:
+	* po/sr.po:
+	* po/sv.po:
+	* po/tr.po:
+	* po/uk.po:
+	* po/vi.po:
+	* po/zh_CN.po:
+	* po/zh_HK.po:
+	* po/zh_TW.po:
+	  gst-plugins-good: update translations
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4040>
+
+2023-02-16 21:31:59 +0100  Rafał Dzięgiel <rafostar.github@gmail.com>
+
+	* ext/adaptivedemux2/dash/gstmpdclient.c:
+	  dashdemux2: mpdclient: Debug all restrictions when selecting rep
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3894>
+
+2023-01-29 20:00:45 +0100  Rafał Dzięgiel <rafostar.github@gmail.com>
+
+	* docs/gst_plugins_cache.json:
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/dash/gstdashdemux.h:
+	  dashdemux2: Add start-bitrate property
+	  Similarly to hlsdemux2 that has this property, also add it to dashdemux2
+	  so users can use it to choose first alternate.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3894>
+
+2022-11-13 18:12:51 +0100  Rafał Dzięgiel <rafostar.github@gmail.com>
+
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/dash/gstmpdclient.c:
+	* ext/adaptivedemux2/dash/gstmpdclient.h:
+	* tests/check/elements/dash_mpd.c:
+	  dashdemux2: Improve initial representation selection
+	  Do not always start with lowest quality possible. Use properties set
+	  by user to select best allowed initial representation at startup too.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3894>
+
+2023-02-05 16:21:57 +0100  Rafał Dzięgiel <rafostar.github@gmail.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Make start-bitrate property work without connection-speed
+	  Makes "start-bitrate" work without setting "connection-speed" property. Having
+	  another property set as a requirement for this one to work is unexpected.
+	  This commit allows to request some initial bitrate for first segment, then
+	  go into adaptive streaming for the rest of media playback.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3895>
+
+2020-06-16 15:18:37 +0900  Hosang Lee <hosang10.lee@lge.com>
+
+	* tests/check/elements/qtdemux.c:
+	* tests/files/mss-fragment.m4f:
+	  tests: qtdemux: add test for MSS fragment wrong data offset compensation
+	  A data offset with an offset smaller than the moof length is wrong
+	  in smooth streaming streams. The samples will not be located and
+	  eventually playback will error out. So compensate assuming data
+	  is in mdat following moof.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3840>
+
+2023-01-27 02:17:47 +0000  Tim-Philipp Müller <tim@centricular.com>
+
+	* tests/check/elements/qtdemux.c:
+	* tests/check/elements/qtdemux.h:
+	* tests/files/qtdemux-test-BBB_32k_1.mp4:
+	* tests/files/qtdemux-test-BBB_32k_init.mp4:
+	* tests/files/qtdemux-test-audio-init.mp4:
+	* tests/files/qtdemux-test-audio-seg1.m4f:
+	  tests: qtdemux: use binary files for samples
+	  Instead of hexdumping it in a 360k header file.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3840>
+
+2020-06-16 15:18:37 +0900  Hosang Lee <hosang10.lee@lge.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: compensate wrong data offset for MSS fragments
+	  A data offset with an offset smaller than the moof length is wrong
+	  in smooth streaming streams.
+	  The samples will not be located and eventually playback will
+	  error out. So compensate assuming data is in mdat following moof.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3840>
+
+2022-12-13 22:04:58 +0900  Seungha Yang <seungha@centricular.com>
+
+	* gst/multifile/gstsplitmuxsrc.c:
+	  splitmuxsrc: Proxy latency query to part reader
+	  splitmuxsrc can respond to the latency query
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3566>
+
+2023-02-13 12:47:31 -0800  Khem Raj <raj.khem@gmail.com>
+
+	* sys/v4l2/gstv4l2object.h:
+	  v4l2: Define ioctl_req_t for posix/linux case
+	  this is an issue seen with musl based linux distros e.g. alpine [1]
+	  musl is not going to change this since it breaks ABI/API interfaces
+	  Newer compilers are stringent ( e.g. clang16 ) which can now detect
+	  signature mismatches in function pointers too, existing code warned but
+	  did not error with older clang
+	  Fixes
+	  gstv4l2object.c:544:23: error: incompatible function pointer types assigning to 'gint (*)(gint, ioctl_req_t, ...)' (aka 'int (*)(int, unsigned long, ...)') from 'int (int, int, ...)' [-Wincompatible-function-pointer-types]
+	  v4l2object->ioctl = ioctl;
+	  ^ ~~~~~
+	  [1] https://gitlab.alpinelinux.org/alpine/aports/-/issues/7580
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3950>
+
+2023-02-10 15:43:45 +0200  Vivia Nikolaidou <vivia@ahiru.eu>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Handle moov atom length=0 case by reading until the end
+	  Previously it would fail to demux the file by trying to read G_MAXUINT64
+	  bytes.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3934>
+
+2023-02-10 15:35:15 +0200  Vivia Nikolaidou <vivia@ahiru.eu>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Fix guint vs gsize type confusion
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3934>
+
+2023-02-03 11:25:29 +0100  Edward Hervey <bilboed@bilboed.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Use track ID for debugging
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3890>
+
+2023-02-03 11:23:50 +0100  Edward Hervey <bilboed@bilboed.com>
+
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	* ext/adaptivedemux2/gstadaptivedemux.h:
+	  adaptivedemux2: Split track id from event stream-id
+	  The id is used for naming of the various objects and debugging. We don't
+	  want/need it to be obfuscated with the massive upstream id.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3890>
+
+2023-02-02 16:48:05 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/gstqtmux.c:
+	  qtmux: Implement writing of `av1C` version 1 box
+	  Version 0 is ancient and not specified in any documents. Take it
+	  directly from the `codec_data` if presents or otherwise try to construct
+	  a reasonably looking `av1C` box.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3882>
+
+2023-02-02 16:28:47 +0200  Sebastian Dröge <sebastian@centricular.com>
+
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Drop av1C version 0 parsing and implement version 1 parsing
+	  The av1C box is optional so dropping parsing does not break anything
+	  fundamentally, and there seems to be no historical record how version 0
+	  even looks like while the comments and the parsing disagreed with each
+	  other.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3882>
+
+2022-11-14 10:49:02 +0100  Patricia Muscalu <patricia@axis.com>
+
+	* gst/rtp/gstrtph264pay.c:
+	* tests/check/elements/rtph264.c:
+	  rtph264pay: Don't insert SPS/PPS before the second image slice
+	  Only the first slice, for which fist_mb_in_slice is set to 0,
+	  should trigger insertion of SPS and PPS buffers.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3402>
+
+2023-02-01 12:09:52 +0100  Enrique Ocaña González <eocanha@igalia.com>
+
+	* gst/isomp4/qtdemux.c:
+	* gst/isomp4/qtdemux.h:
+	  qtdemux: Don't emit GstSegment correcting start time when in MSE mode
+	  When using qtdemux in a pipeline that should only work as a pure demuxer (not
+	  for actual playback), qtdemux shouldn't emit new GstSegments to correct
+	  the start time (jump to the future) to ensure that the user experiences no
+	  playback delay. By doing so, it's generating the wrong segments when an append
+	  of data from the past happens. When that happens, downstream elements such as
+	  parsers (eg: aacparse) may clip those buffers laying before the GstSegment and
+	  create problems on the GStreamer client app (eg: WebKit).
+	  Getting buffers clipped out because of the wrong GstSegments started becoming
+	  a problen when this commit was introduced:
+	  ab6e49e9cc audioparsers: add back segment clipping to parsers that have lost it
+	  This clipping makes test DASH shaka 35 from MVT tests[1] to fail in
+	  WebKitGTK/WPE (at least) and can potentially cause a number of other problems
+	  in the WebKit Media Source Extensions (MSE) code.
+	  Note that this new behaviour of not emitting new GstSegments only makes sense
+	  when qtdemux is being used as a pure demuxer and not as part of a regular
+	  pipeline. This is why the variant field has been added. When equal to
+	  VARIANT_MSE_BYTESTREAM, it will make qtdemux behave differently in push mode,
+	  taking decisions that meet the expectations for an MSE-like processing mode.
+	  This kind of tweaks have been done in the past for MSS streams, for instance.
+	  That code has been refactored to use VARIANT_MSS_FRAGMENTED now, instead of
+	  its own dedicated boolean flag.
+	  Co-authored by: Alicia Boya García <ntrrgc@gmail.com>
+	  ...who suggested to use "variant: mse-bytestream" in the caps to identify that
+	  mode, as proposed in her unmerged patch:
+	  https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/467
+	  [1] https://github.com/rdkcentral/mvt
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3867>
+
+2023-01-25 11:07:43 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Remove enable-llhls property
+	  This was only used for testing purposes
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-25 10:52:10 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Don't leak PDT datetime
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-25 10:08:50 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	* ext/adaptivedemux2/mss/gstmssdemux.c:
+	  adaptivedemux2: Don't leak taglist
+	  Clarify the ownership in the documentation
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-25 07:46:22 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	  adaptivedemux2: Don't leak track tags
+	  The tags are fully transfered to this function
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-06 00:14:27 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/downloadhelper.c:
+	  adaptivedemux2: Log request duration in debug output
+	  When completing, log how long a HTTP request took into the debug output.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-04 11:49:14 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Improve live playlist update intervals
+	  The live playlists should be updated at a defined interval. The problem is that
+	  this interval was used *after* the playlist was finally received and processed,
+	  which resulted in a gradual shift happening in playlist updates.
+	  Instead store and use the time at which playlists were requested to determine
+	  when the next one should be downloaded.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-04 11:45:30 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	  hlsdemux2: Fix playlist reload interval when unchanged
+	  When falling back to using the regular last segment, use that duration as the
+	  identical-playlist reload interval (and not the playlist target duration which
+	  could be much larger)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-04 11:40:40 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Fix position searching
+	  The scanning is done in a reverse order, the proper full checks to do are
+	  therefore:
+	  * If the position is beyond half a "segment duration", it's in the following
+	  segment
+	  * If the position is within the first half of a segment, it's in that one
+	  * If the segment is the first one and the position is within half a duration
+	  backwards, we consider the position as being within that first segment
+	  Also handle the case where a "partial only" segment doesn't have a reliable
+	  duration, and therefore use the playlist target duration instead.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-04 11:17:42 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Handle all cases for starting segment calculation
+	  The implementation wouldn't work with regular HLS streams (i.e. the final
+	  fallback).
+	  Now that the implementation uses time to search for the starting
+	  segment (instead of just the n-th from the end), we can specify the correct
+	  hold_back fallback value from the RFC
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-04 11:16:14 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Fix buffering threshold calculation and handling
+	  * The checks for smaller values were wrong
+	  * Properly initialize the stream default recommended buffering threshold so that
+	  a default (10s) value is used until the subclass can provide a proper value
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-04 10:28:40 +0100  Edward Hervey <edward@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Make sure simple media playlist is properly primed
+	  By setting/propagating stream time initially
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-30 23:37:23 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	* ext/adaptivedemux2/gstadaptivedemux.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	  adaptivedemux2: Fix manifest access during seeking query
+	  Avoid a deadlock if a downstream seeking query happens while the scheduler
+	  thread is holding the manifest lock (for example during a seek back to live).
+	  Instead, do a more elaborate fix where the external calls that need access to a
+	  'manifest' access a copy that's updated during a manually triggered manifest
+	  update callback.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-03 21:15:21 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Symbol hygiene cleanup
+	  Rename track_dequeue_data_locked() to
+	  gst_adaptive_demux_track_dequeue_data_locked(), since it's non-static.
+	  Make find_stream_for_track_locked() static since it's only used in the main
+	  gstadaptivedemux.c file.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-03 21:03:35 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
+	  adaptivedemux2: Fix download error handling more
+	  gst_adaptive_demux2_stream_finish_download() will already schedule another
+	  fragment download if it can so don't fall through to the retry code that will
+	  also try and schedule a download (triggering an assert).
+	  Fix the logic in general to retry advancing into the live seek range once.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-02 22:32:13 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	  hlsdemux2: Immediately request playlist after URI changes
+	  When the stream switches to a new playlist / variant while the loader is waiting
+	  on a timer to refresh the old playlist, cancel the timer and submit the request
+	  for the new URI.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-31 22:13:05 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Re-add support for fallback variant URLs
+	  fallback variant URLs get accumulated into a list in the variant now. If there's
+	  one available, switch to it after a variant update failure (failure to load the
+	  variant 3 times)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-31 00:51:55 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Demote log message
+	  Don't complain loudly about replacing the current pending playlist, just log it
+	  at debug level
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-31 00:51:02 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Wait for playlist load after a switch
+	  Check in update_fragment_info() if the playlist we want has actually been loaded
+	  yet, and return BUSY if not.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-30 23:39:46 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Handle async playlist loading failures
+	  Add failed variant playlists to a list and failover to other variants until
+	  there is none left
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-28 04:40:38 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Wait for playlist switch during seek.
+	  When switching to/from an iframe variant to do seeking, wait for the target
+	  playlist to load before handling the seek.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-28 02:01:57 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	  hlsdemux2/playlist-loader: Implement more features
+	  Implement limited retries on download errors before reporting it, and remember
+	  permanent redirects, with LL-HLS directives removed.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-28 00:45:15 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemuxdemux2: Consider the hold-back when calculating seek range
+	  When calculating the seek range for a live stream, use the same hold-back logic
+	  as when choosing a starting segment, including low-latency segments if
+	  enabled. Permits seeking closer to the live edge when re-synching or catching
+	  up.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-27 03:04:36 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	  hlsdemux2: Continue reworking code for async playlist updates
+	  Everything is working again now except for corner cases:
+	  - Failing over to another playlist after a load failure
+	  - Remembering playlist redirects and using that URI
+	  directly next time.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-31 23:26:03 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Handle more async stream cases
+	  Handle BUSY flow returns when making calls from external threads, and inhibit
+	  fragment downloads during stream prepare
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-24 01:37:40 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Add llhls-enabled property to streams
+	  Tidying: Make the llhls-enabled setting configurable through a stream property
+	  instead of set manually after construction.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-24 01:36:52 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Add gst_hls_demux_stream_set_playlist_uri
+	  Add a method that configures the new playlist URI for a stream.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2023-01-24 17:03:21 +0100  Edward Hervey <bilboed@bilboed.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/meson.build:
+	  hlsdemux2: Add HLS playlist loader
+	  Add a helper that asynchronously loads and refreshes the playlist for HLS
+	  streams.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-31 00:33:51 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	  adaptivedemux2: Fix for failed download handling
+	  When playing at the live edge of a live playlist, and a download fails, we don't
+	  expect there to be a next fragment. That case is handled lower down anyway, so
+	  don't retry infinitely on spurious http errors at the live edge.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-28 00:41:44 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Drop segment lock on stream_seek error.
+	  If stream_seek() fails, make sure to drop the segment lock before bailing out.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-31 23:22:29 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Add gst_adaptive_demux2_stream_wait_prepared()
+	  Add a method that waits for a stream to signal the prepare_cond after it returns
+	  a BUSY flow return.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-27 03:46:47 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Remove gst_adaptive_demux2_stream_has_selected_tracks
+	  Use gst_adaptive_demux2_stream_is_selected_locked() instead, which is identical
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-25 03:39:47 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux.h:
+	  adaptivedemux2: Move GST_ADAPTIVE_DEMUX_FLOW_BUSY to adaptivedemux.h
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-15 02:27:27 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  adaptivedemux2: Add start/stop vfuncs
+	  Remove the can_start() vfunc, in favour of vfuncs when the stream starts/stops,
+	  allowing the sub-class to do custom logic before (or preventing) the stream from
+	  starting and stopping.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-09 02:17:51 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Remove unused function argument
+	  Remove the demux argument from the
+	  gst_hls_demux_stream_update_rendition_playlist() method
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-09 02:07:57 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	* ext/adaptivedemux2/gstadaptivedemux.h:
+	  adaptivedemux2: Add gst_adaptive_demux_get_loop()
+	  Add an accessor function for retrieving the demuxer's scheduler thread loop.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-12-09 01:35:40 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/gstadaptivedemux-period.c:
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Add gst_adaptive_demux_period_add_stream()
+	  Make a function for adding a stream to a period, for better encapsulation.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/rtpmanager/rtpjitterbuffer.c:
-	  rtpjitterbuffer: Fix invalid memory access in rtp_jitter_buffer_pop()
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1973>
+2022-12-09 00:54:47 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-14 16:19:33 +0800  Hou Qi <qi.hou@nxp.com>
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Add new flow return value for BUSY and PREPARE stream state
+	  Neither are used yet, they're just placeholders.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: set frame duration according to framerate
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1953>
+2022-11-30 07:54:07 +0100  Edward Hervey <edward@centricular.com>
 
-2022-03-15 13:49:09 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: support old compilers
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtsp/gstrtspsrc.c:
-	* gst/rtsp/gstrtspsrc.h:
-	  rtspsrc: proxy new "add-reference-timestamp-meta" property from rtpjitterbuffer
-	  When syncing to an RFC7273 clock this will add the original
-	  reconstructed reference clock timestamp to buffers in form
-	  of a GstReferenceTimestampMeta.
-	  This is useful when we want to process or analyse data based
-	  on the original timestamps untainted by any local adjustments,
-	  for example reconstruct AES67 audio streams with sample accuracy.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1964>
+2022-11-26 03:01:14 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-15 11:56:28 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Place HLS delivery directives in UTF-8 order.
+	  Use new GstURI gst_uri_to_string_with_keys() API to produce the playlist URI
+	  with query arguments in UTF-8 order.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpbin.h:
-	  rtpbin: proxy new "add-reference-timestamp-meta" property from rtpjitterbuffer
-	  When syncing to an RFC7273 clock this will add the original
-	  reconstructed reference clock timestamp to buffers in form
-	  of a GstReferenceTimestampMeta.
-	  This is useful when we want to process or analyse data based
-	  on the original timestamps untainted by any local adjustments,
-	  for example reconstruct AES67 audio streams with sample accuracy.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1964>
+2022-11-26 02:59:48 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-15 01:35:17 +0000  Tim-Philipp Müller <tim@centricular.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Avoid assert in _has_next_fragment()
+	  gst_hls_demux_stream_has_next_fragment() can be called with a NULL
+	  current_segment if we're past the end of the current playlist. In that case,
+	  just return FALSE instead of hitting a critical in the playlist code.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpjitterbuffer.c:
-	* gst/rtpmanager/rtpjitterbuffer.c:
-	* gst/rtpmanager/rtpjitterbuffer.h:
-	  rtpjitterbuffer: add "add-reference-timestamp-meta" property
-	  When syncing to an RFC7273 clock this will add the original
-	  reconstructed reference clock timestamp to buffers in form
-	  of a GstReferenceTimestampMeta.
-	  This is useful when we want to process or analyse data based
-	  on the original timestamps untainted by any local adjustments,
-	  for example reconstruct AES67 audio streams with sample accuracy.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1964>
+2022-11-26 01:21:33 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-15 09:49:59 +0800  Hou Qi <qi.hou@nxp.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Include skipped segments in MSN calculation
+	  When a playlist has skipped segments, increment the MSN to account for them so
+	  the remaining segments end up with the right sequence numbers.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec: safely retrun from video_dec_loop with stream unlock
-	  This is to avoid decoder hang when doing trick play between
-	  different resolutions.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1960>
+2022-11-25 03:57:38 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-14 13:59:37 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Use partial segment for playlist update interval
+	  When playing LL-HLS playlists in LL-HLS mode, update the playlist more often (on
+	  the partial segment interval) or else we end up downloading them in bursts and
+	  playing further from the live edge than intended.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/rtpmanager/rtpjitterbuffer.c:
-	  rtpjitterbuffer: Improve accuracy of RFC7273 clock time calculations
-	  Previously the result of the calculations included inaccuracies caused
-	  by the NTP clock estimation, which caused the timestamps to jitter
-	  +/- 1/clockrate.
-	  By reorganizing the calculations it is possible to get rid of this
-	  inaccuracy and calculate deterministic and exact packet timestamps based
-	  on the actual NTP clock as long as the estimation is not off by more
-	  than 2**31 clockrate units.
-	  The only remaining inaccuracy that is introduced now is caused by the
-	  conversion from the NTP clock to the pipeline clock.
-	  Also split up debug output, demote many messages to the trace debug
-	  level and output more intermediate results.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1955>
-
-2022-03-14 12:29:04 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-11-25 01:14:12 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/rtpsession.c:
-	* gst/rtpmanager/rtptwcc.c:
-	  twcc: Add some logging to debug TWCC feedback
-	  This should allow people to debug when TWCC feedback is not enabled
-	  because they haven't set the extmap in the caps.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1952>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Do a full playlist reload if delta fails
+	  If we do a delta playlist request but then can't merge the result with the
+	  existing playlist, retry with a full playlist request.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-14 13:45:36 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-11-18 04:35:51 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtpsession.c:
-	  twcc: Note that packet-loss-pct can count reordering as loss
-	  This is difficult to encounter in ordinary networks, but is
-	  encountered when using tc-netem to add random delays to packets, and
-	  also when your UDP stream is bonded over multiple links with varying
-	  characteristics.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1952>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Fill in skipped segments if possible
+	  After reloading a playlist using a delta request, use the previous playlist to
+	  fill in skipped segments if possible.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2017-08-11 16:33:23 +0200  Havard Graff <havard.graff@gmail.com>
+2022-11-18 04:35:00 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxsend.c:
-	* tests/check/elements/rtprtx.c:
-	  rtprtxsend: don't require clock-rate in caps
-	  For multiplexing, the rtpstreams you are multiplexing might not use
-	  the same clock-rate.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1881>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Add gst_hls_media_playlist_sync_skipped_segments()
+	  Add a method that transfers over skipped segments from a reference playlist,
+	  used to repopulate a delta playlist
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2016-11-04 11:47:20 +0100  Havard Graff <havard.graff@gmail.com>
+2022-11-18 03:24:38 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxsend.c:
-	* tests/check/elements/rtprtx.c:
-	  rtprtxsend: don't start the task unless we are doing rtx
-	  The rtxsend element can do pass-through when not enabled (no pt-map set)
-	  and in those cases there is no point in starting an additional task
-	  that does absolutely nothing.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1880>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Parse EXT-X-SKIP tag
+	  Parse the attributes from the EXT-X-SKIP tag
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2016-06-27 14:28:06 +0200  Havard Graff <havard.graff@gmail.com>
+2022-11-18 01:55:48 +1100  Jan Schmidt <jan@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtprtxreceive.c:
-	* gst/rtpmanager/gstrtprtxreceive.h:
-	  rtprtxreceive: add ssrc-map property
-	  Mirroring the rtxsend, this allows the application to "pre-map" the
-	  retransmission-ssrcs to the "real" ssrc, if this information is known.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1878>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Use skip and blocking playlist delivery directives
+	  Detect when we can use a blocking request and delta playlist update requests and
+	  add the required delivery directives to the request URI.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2019-09-08 15:54:08 +0200  Carlos Rafael Giani <crg7475@mailbox.org>
+2022-11-18 01:23:54 +1100  Jan Schmidt <jan@centricular.com>
 
-	* ext/mpg123/gstmpg123audiodec.c:
-	* ext/mpg123/gstmpg123audiodec.h:
-	* gst/audioparsers/gstmpegaudioparse.c:
-	* tests/check/elements/mpg123audiodec.c:
-	* tests/files/sine-1009ms-1ch-32000hz-gapless-with-lame-tag.mp3:
-	  mpg123: Add gapless playback support
-	  Co-authored-by: Sebastian Dröge <sebastian@centricular.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1028>
-
-2019-09-07 19:15:42 +0200  Carlos Rafael Giani <crg7475@mailbox.org>
-
-	* gst/audioparsers/gstmpegaudioparse.c:
-	* gst/audioparsers/gstmpegaudioparse.h:
-	* tests/check/elements/mpegaudioparse.c:
-	  mpegaudioparse: Support gapless playback
-	  Gapless playback is handled by adjusting buffer timestamps & durations
-	  and by adding GstAudioClippingMeta.
-	  Support for "Frankenstein" streams (= poorly stitched together streams)
-	  is also added, so that gapless playback support doesn't prevent those
-	  from being properly played.
-	  Co-authored-by: Sebastian Dröge <sebastian@centricular.com>
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1028>
-
-2022-03-11 10:32:42 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
-
-	* gst/deinterlace/tvtime/scalerbob.c:
-	  deinterlace: scalerbob: Reduce latency to 0
-	  We only need the current field, just like `linear`.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1926>
-
-2022-03-12 17:13:48 +0200  Vivia Nikolaidou <vivia@ahiru.eu>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Add gst_hls_media_playlist_get_next_msn_and_part()
+	  Add a function that computes the media sequence number and/or part index that
+	  are immediately after the end of the playlist, for use in blocking playlist
+	  requests
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/deinterlace/yadif.c:
-	  yadif: Fix CHECK macro for YUY2 format
-	  Used to make comb artifacts for videotestsrc pattern=ball for YUY2
-	  format only (not AYUV).
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1938>
+2022-11-18 01:18:48 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-10 17:03:45 +0900  Damian Hobson-Garcia <dhobsong@igel.co.jp>
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	  hlsdemux2: Use stream llhls_enabled flag
+	  Use the stream's copy of the llhls_enabled flag when deciding whether to do
+	  preload requests - the value that was cached when the stream started
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* docs/gst_plugins_cache.json:
-	  doc: New cropping parameters added to v4l2src
-	  v4l2src add several new parameters to control cropping of
-	  the captured video stream.  Update the doc cache to reflect
-	  this.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1089>
+2022-11-18 00:26:41 +1100  Jan Schmidt <jan@centricular.com>
 
-2021-10-15 18:33:50 +0900  Damian Hobson-Garcia <dhobsong@igel.co.jp>
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	* ext/adaptivedemux2/gstadaptivedemux.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-stream.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	* ext/adaptivedemux2/hls/meson.build:
+	  hlsdemux2: Split the stream object out
+	  Move the stream object to a separate file to split the demux level behaviour
+	  from the stream behaviour better.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* tests/examples/v4l2/meson.build:
-	* tests/examples/v4l2/v4l2src-crop.c:
-	  examples: v4l2: Add v4l2src crop example
-	  Add a simple utility to illustrate how to set input cropping on v4l2src.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1089>
+2022-11-17 02:59:37 +1100  Jan Schmidt <jan@centricular.com>
 
-2021-10-14 17:22:19 +0900  Damian Hobson-Garcia <dhobsong@igel.co.jp>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Add a timestamp to the playlist
+	  Store the timestamp for this playlist. If valid it represents the monotonic time
+	  at which the data was retrieved, minus any proxy cache Age field.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* sys/v4l2/gstv4l2src.c:
-	* sys/v4l2/gstv4l2src.h:
-	  v4l2src: Add support for cropping at capture source input
-	  Add properties to control input cropping in the V4L2 device.
-	  The input cropping is applied before composing the result to the
-	  capture buffer.  By default the capture size will be set to the same
-	  size as the crop region, but it can be scaled to a different output
-	  frame size if supported by the V4L2 device.
-	  If scaling is not supported, the cropped image will
-	  be composed as is into the top-left corner of the capture buffer.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1089>
-
-2021-10-13 17:33:12 +0900  Damian Hobson-Garcia <dhobsong@igel.co.jp>
+2022-11-17 03:49:12 +1100  Jan Schmidt <jan@centricular.com>
 
-	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2object.h:
-	  v4l2object: Add function to get crop regions from device
-	  Get the current crop bounding region from the V4L2 device so
-	  that it can be provided to applications and used to validate
-	  crop settings. Also make the default crop region available so
-	  that it can be used to reset the crop when appropriate.
-	  Uses the selection API when available with fallback to the crop
-	  API for older kernels.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1089>
+	* ext/adaptivedemux2/downloadrequest.c:
+	* ext/adaptivedemux2/downloadrequest.h:
+	  adaptivedemux2/downloadrequest: add a helper to retrieve the Age header
+	  Add a method to look at HTTP response headers and parse and return any Age
+	  header, provided by caching proxy servers if the data was provided from a cache.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2021-09-30 17:56:56 +0900  Damian Hobson-Garcia <dhobsong@igel.co.jp>
+2022-10-27 23:53:51 +1100  Jan Schmidt <jan@centricular.com>
 
-	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2object.h:
-	* sys/v4l2/gstv4l2transform.c:
-	  v4l2object: rename crop function to reflect its usage
-	  The gst_v4l2_object_set_crop() is used for removing buffer
-	  alignment padding. Give it a name that better reflects
-	  that usage.  This helps to distinguish from cropping of the
-	  input image (e.g. cropping at the image sensor on a captre
-	  device), which can be  unrelated to the memory buffer padding,
-	  especially if scaling is involved.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1089>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Fix missed segment match for partial-only segment
+	  Fix a case where the matching code might not select the final partial-only
+	  segment because it has too short a duration (while it's still being created)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-10 18:43:45 +0900  Sangchul Lee <sc11.lee@samsung.com>
+2022-10-27 03:44:29 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtp/gstrtpvp8depay.c:
-	  rtpvp8depay: Fix crash when making 'GstRTPPacketLost' custom event
-	  This patch fixes a seg.fault in gst_structure_new() with warnings as below.
-	  GLib-GObject-WARNING **:
-	  ../gobject/gtype.c:4330: type id '0' is invalid
-	  GLib-GObject-WARNING **:
-	  can't peek value table for type '<invalid>' which is not currently referenced
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1918>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: In live, match buffering to the hold back distance
+	  When playing a live stream, make the recommended buffering threshold at most the
+	  hold-back distance from live. If we start 3 seconds from the live edge, there's
+	  no point trying to buffer more - we'll just hit the live edge and have to wait
+	  for more data to be available anyway.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-04 18:58:56 +0100  Tomasz Andrzejak <andreiltd@gmail.com>
+2022-10-25 01:03:16 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	  rtpbin: allow FEC elements with Always pads
-	  This patch enable picking up FEC decoder or enocder that have
-	  static repair packets pad.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1860>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Resync stream time on partial segment boundaries
+	  When resyncing stream times in a playlist, support at any partial segment
+	  position, not just at full segment boundaries.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-09 12:17:11 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-10-14 23:32:56 +1100  Jan Schmidt <jan@centricular.com>
 
-	* ext/soup/gstsouploader.c:
-	  soup: Load the runtime library, not the development library
-	  libsoup-2.4.so / libsoup-3.0.so are symlinks installed by development
-	  packages, they are not available at runtime.
-	  Also eliminate G_MODULE_SUFFIX since it's not useful for us, and is
-	  actually incorrect on macOS anyway.
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1071
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1899>
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.c:
+	  hlsdemux2: Calculate / transfer timing info for preloads
+	  When fulfilling data requests, transfer timing information so the stream can
+	  calculate data bitrates.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2019-07-10 17:21:01 +0200  Edward Hervey <edward@centricular.com>
+2022-10-14 23:30:59 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Propagate stick events downstream when creating pads
-	  If upstream provided a stream collection event before any pads were created,
-	  make sure it's propagated downstream when pads are created.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1891>
+	* ext/adaptivedemux2/downloadhelper.c:
+	* ext/adaptivedemux2/downloadrequest.c:
+	* ext/adaptivedemux2/downloadrequest.h:
+	  adaptivedemux2: Add most recent data time and offset helper
+	  Add a field to the DownloadRequest that reports the most recent time at which
+	  data arrived. Update it in the DownloadHelper.
+	  Add a method to retrieve the GST_BUFFER_OFFSET() for the DownloadRequest's data
+	  buffer (if any).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2021-02-01 16:07:08 +0100  Havard Graff <havard.graff@gmail.com>
+2022-10-14 22:12:26 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxsend.c:
-	  rtprtxsend: if no rtx is present, don't expose a rtx-ssrc in caps
-	  The point here is that rtpsession will create a new rtpsource when
-	  the field "rtx-ssrc" is present, and when not doing rtx, that means
-	  a random ssrc will create a new rtpsource that will be included in RTCP
-	  messages for the current session.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1882>
+	* ext/adaptivedemux2/downloadrequest.c:
+	  adaptivedemux2: Handle another case in download_request_take_buffer_range
+	  Handle the case where we want to get a range from the available data that
+	  doesn't start at the first available byte (and discard the bytes before that
+	  start offset).
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2016-06-22 14:48:59 +0200  Havard Graff <havard.graff@gmail.com>
+2022-10-14 22:01:55 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxsend.c:
-	  rtprtxsend: don't process or warn if no map is set
-	  This makes it more gentle when doing "pass-through"
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1879>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Cancel preload before fetching something else
+	  When submitting a request for fragment or header that doesn't match any preload,
+	  make sure there's not an ongoing preload for that data type, to avoid parallel
+	  downloads using up bandwidth.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2016-08-23 19:06:49 +0200  Mikhail Fludkov <misha@pexip.com>
+2022-10-14 06:41:21 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxreceive.c:
-	* tests/check/elements/rtprtx.c:
-	  rtprtxreceive: fix crash when RTX payload has zero length
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1875>
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.c:
+	  hlsdemux2: Fix assertion on shutdown
+	  After cancelling a DownloadRequest, the download helper may not do so
+	  immediately, so we can't assert on the in_use flag. Also, since there's no
+	  refcount on the preload hint struct in the download request callback data, make
+	  sure no callbacks will be dispatched when we're going to free the preload hint
+	  struct.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2016-06-26 22:25:46 +0200  Havard Graff <havard.graff@gmail.com>
+2022-10-14 06:28:47 +1100  Jan Schmidt <jan@centricular.com>
 
-	* gst/rtpmanager/gstrtprtxreceive.c:
-	  rtprtxreceive: allow passthrough and non-rtp buffers
-	  To avoid mapping rtp buffers when RTX is not in use, and to not
-	  do a full error on receiving a non-rtp buffer, since you have no control
-	  of what a rouge sender might send you.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1874>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Implement the stream submit_request() vfunc
+	  Implement the submit_request() vfunc for streams and fulfil requests from the
+	  preload hint data if possible.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-10-14 06:25:28 +1100  Jan Schmidt <jan@centricular.com>
+
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.h:
+	  hlsdemux2/preloader: Implement basic request handling
+	  Implement fulfilment of HTTP requests from the active preload downloads by
+	  finding any preload request that can provide the requested data and feeding
+	  bytes from the internal DownloadRequest to the caller provided target
+	  DownloadRequest.
+	  Doesn't yet calculate timestamps to make the target request have a sensible
+	  apparent bitrate.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-10-14 06:22:35 +1100  Jan Schmidt <jan@centricular.com>
 
-2021-02-08 21:40:19 +0100  Havard Graff <havard@pexip.com>
+	* ext/adaptivedemux2/downloadrequest.c:
+	* ext/adaptivedemux2/downloadrequest.h:
+	  adaptivedemux2/downloadrequest: Add new methods
+	  Add download_request_take_buffer_range() and
+	  download_request_get_bytes_available() methods.
+	  download_request_take_buffer_range() takes bytes from the front of the request
+	  that satisfy the requested start/end byterange, and puts any remaining bytes
+	  back into the DownloadRequest
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/rtpmanager/gstrtprtxreceive.c:
-	* gst/rtpmanager/gstrtprtxreceive.h:
-	* gst/rtpmanager/gstrtprtxsend.c:
-	* gst/rtpmanager/gstrtprtxsend.h:
-	  rtprtx: don't access type-system per buffer
-	  When doing only a single stream of audio/video this hardly matters,
-	  but when doing many at the same time, the fact that you have to get
-	  a hold of the glib global type-system lock every time you process a buffer,
-	  means that there is a limit to how many streams you can process in
-	  parallel.
-	  Luckily the fix is very simple, by doing a cast rather than a full
-	  type-check.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1873>
+2022-10-13 21:35:00 +1100  Jan Schmidt <jan@centricular.com>
 
-2020-02-10 14:37:30 +0100  Havard Graff <havard@pexip.com>
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	  adaptivdemux2: Improve a comment about unlocking download requests.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* tests/check/elements/rtprtx.c:
-	  rtprtx: signed/unsigned and style fixes
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1872>
+2022-10-13 14:49:35 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-03 13:47:05 +0800  Hou Qi <qi.hou@nxp.com>
+	* ext/adaptivedemux2/gstadaptivedemux-stream.c:
+	* ext/adaptivedemux2/gstadaptivedemux-stream.h:
+	  adaptivedemux2: Make download mechanism overrideable
+	  Make the mechanism by which DownloadRequests are fulfilled overrideable by the
+	  subclass, in case it has an internal mechanism it can use (such as blocking
+	  preloads in HLS)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* sys/v4l2/gstv4l2bufferpool.c:
-	  v4l2bufferpool: Fix race condition between qbuf and pool streamoff
-	  There is a chance that pool->buffers[index] sets BUFFER_STATE_QUEUED, but
-	  it has not been queued yet which makes pool->buffers[index] still NULL.
-	  At this time, if pool_streamff release all buffers with BUFFER_STATE_QUEUED
-	  state regardless of whether the buffer is NULL or not, it will cause segfault.
-	  To fix this, also check buffer when streamoff release buffer.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1842>
+2022-10-13 05:19:57 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-03 14:22:10 +0800  Hou Qi <qi.hou@nxp.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux-preloader.h:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	* ext/adaptivedemux2/hls/meson.build:
+	  hlsdemux2: Add preloader helper.
+	  Add a helper that submits and handles blocking preload requests for future
+	  PART/MAP data from live playlists. Add handling in the hlsdemux stream to submit
+	  preload requests when hitting the end of the available segments in a live
+	  playlist.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/flv/gstflvmux.c:
-	  flvmux: Add protection when unref GstFlvMuxPad
-	  This is to avoid gst_object_unref: assertion 'object != NULL' failed.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1843>
+2022-10-13 05:17:59 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-04 14:57:30 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Add preload equality helper
+	  Add a helper function that compares two preload hints for equality based on URI
+	  and requested byte range.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* docs/gst_plugins_cache.json:
-	  doc: AV1 demuxers now expose their alignment
-	  Update the chache accordingly.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1837>
+2022-10-13 05:15:17 +1100  Jan Schmidt <jan@centricular.com>
 
-2022-03-02 16:31:24 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	* ext/adaptivedemux2/downloadhelper.h:
+	  adaptivedemux2: Define RFC8673_LAST_BYTE_POS
+	  Define the recommended value from RFC8673 for the last byte of an open-ended
+	  range request intended to invoke chunked blocking downloads of an incrementally
+	  created resource.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/matroska/matroska-demux.c:
-	* gst/matroska/matroska-mux.c:
-	  matroska: Fix AV1 alignment to TU
-	  Matroska stores AV1 in temporal unit, so that all OBU sharing the same
-	  timestamp are put together. This was previously just assumed, which isn't
-	  safe now that we have more alignments.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1837>
+2022-08-17 02:12:21 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-03-02 16:24:38 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Ignore partial segments when not live
+	  Add some checks that LL-HLS support is enabled and that the current playlist is
+	  live before proceeding to play any partial segments.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/isomp4/gstqtmuxmap.c:
-	* gst/isomp4/qtdemux.c:
-	  isomp4: Fix AV1 default alignment
-	  ISOMP4 store TU (temporal units) worth of AV1. Expose this in the
-	  caps to reduce overhead in the parser, and in the muxer to avoid
-	  storing frames split in the wrong way.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1837>
+2022-08-16 22:05:03 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-07 17:51:39 -0500  Tristan Matthews <tmatth@videolan.org>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Print playlist age in debug output
+	  Store the timestamp when playlists are updated, and add some debug output to the
+	  update_fragment_info that estimates how far from the live edge the fragment
+	  currently is
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/matroska/matroska-mux.c:
-	  matroskamux: allow width+height caps changes for VP8/9
-	  For VP8 and VP9, width+height changes are signalled inband.
-	  Refs https://github.com/Kurento/bugtracker/issues/535 and
-	  https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/1047/diffs?commit
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1657>
+2022-08-13 05:44:24 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-07 16:41:40 -0500  Tristan Matthews <tmatth@videolan.org>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Implement LL-HLS flag and part-hold-back/hold-back in live.
+	  Add a flag to hlsdemux to enable or disable LL-HLS handling.
+	  When LL-HLS is enabled and an LL-HLS playlist is loaded, use the part-hold-back
+	  threshold to choose a starting segment.
+	  For live streams that aren't LL-HLS, use the provided hold-back attribute, or
+	  fall back to landing 3 segments from the end.
+	  Make the gst_hls_media_playlist_seek() method able to choose a partial segment
+	  within 2 target durations of the end of the playlist when requested.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-08-12 05:00:32 +1000  Jan Schmidt <jan@centricular.com>
 
-	* gst/matroska/matroska-mux.c:
-	  matroskamux: allow width + height changes for avc3|hev1
-	  For avc3 and hev1, the intent was to allow more flexibility for caps changes
-	  (see https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/1047/diffs?commit_id=9bd8d608d5bae27ec5ff09e733f76ca32b17420c)
-	  however width and resolution were previously omitted.
-	  avc3 and hev1 specifically support changing stream-parameters on the fly, whereas avc1/hvc1 disallow in-band SPS.
-	  This commit allows for changes to width and height for these which is in line with matroskamux's behaviour prior to 1.14.0.
-	  Practically speaking, one use case where this is commonly seen is when capturing a WebRTC stream, as the browser will adapt the resolution live.
-	  Suggested-by: Mathieu Duponchelle "<mathieu@centricular.com>"
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1657>
-
-2022-03-04 15:36:20 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  m3u8demux2: Fix off-by-one and leak.
+	  Fix an off-by-one in gst_hls_media_playlist_sync_to_playlist() that would ignore
+	  the first fragment in the reference playlist.  The error was harmless, since we
+	  expect the reference playlist to be older than the playlist we're
+	  synchronising (so the first/oldest segment in the reference playlist will likely
+	  not exist in the new playlist), so this is just for correctness.
+	  Also fix a segment leak in gst_hls_media_playlist_advance_fragment() when
+	  ignoring the partial_only segment.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
+
+2022-08-12 04:59:09 +1000  Jan Schmidt <jan@centricular.com>
 
-	* gst/deinterlace/gstdeinterlace.c:
-	  deinterlace: Prevent race between _set_method and latency query
-	  It's possible that the method is being manipulated while downstream
-	  queries our latency, leading to crashes.
-	  Prevent that from happening.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1854>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Fix segment advance on partial segments.
+	  Fix the duration passed to gst_adaptive_demux2_stream_advance_fragment() when
+	  advancing from a partial segment.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-03 23:04:36 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-08-12 04:58:47 +1000  Jan Schmidt <jan@centricular.com>
 
-	* ext/soup/gstsouploader.c:
-	  soup: Fix static build with MSVC
-	  ../ext/soup/gstsouploader.c(818): error C4098: '_soup_session_send_async': 'void' function returning a value
-	  It's technically a false warning, but that's how MSVC works, so fix
-	  it.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1805>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Improve some debug output
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-03 00:37:57 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-08-12 04:56:47 +1000  Jan Schmidt <jan@centricular.com>
 
-	* ext/soup/meson.build:
-	  soup: Fix pkgconfig generation and documentation
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1805>
+	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Add and use gst_hls_media_playlist_find_position()
+	  Add a function for synchronising current position with the contents of a
+	  playlist that is specifically for that and can handle synchronising to a partial
+	  segment.
+	  gst_hls_media_playlist_seek() will be used only when performing external seek
+	  requests, to find the best segment or partial segment at which to resume
+	  playback.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-02 23:22:39 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-08-12 02:14:47 +1000  Jan Schmidt <jan@centricular.com>
 
-	* ext/soup/meson.build:
-	  soup: Fix static build when default_library=both
-	  Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1007
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1805>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Add debug in find_segment_in_playlist()
+	  In m3u8 segment matching, print the PDT that was matched between playlists.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-03-02 23:11:09 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+2022-08-12 02:11:34 +1000  Jan Schmidt <jan@centricular.com>
 
-	* ext/soup/meson.build:
-	  soup: Don't error out in static build unless option is enabled
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1805>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Fix some m3u8 segment leaks
+	  Make sure unref m3u8 segments in some missed paths.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-02-27 15:45:01 +0100  Philippe Normand <philn@igalia.com>
+2022-08-07 15:02:26 +1000  Jan Schmidt <jan@centricular.com>
 
-	* ext/soup/gstsouploader.c:
-	  soup: Lookup libsoup dylib files on Apple platforms
-	  Fixes #1007
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1805>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Allow starting at the partial_only segment
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2021-10-15 15:32:22 +0900  Damian Hobson-Garcia <dhobsong@igel.co.jp>
+2022-08-07 14:28:38 +1000  Jan Schmidt <jan@centricular.com>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2src: Reset the compose window to the default after setting format
-	  When the size of V4L2 capture or output is changes with VIDIOC_S_FMT,
-	  the device is only required to update the compisition window to fit
-	  inside the new frame size.  This can result in captured data only being
-	  updated on a portion of the frame after a resize.
-	  Update the composition window to the default value determined by the
-	  V4L2 device driver whenever the format is changed to make sure that
-	  all image data is composed to its full size.
-	  Fixes #765
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1806>
-
-2022-03-01 20:59:30 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Recalculate partial segments in anchor segment
+	  When recalculating the partial segment stream times in
+	  gst_hls_media_playlist_recalculate_stream_time(), don't miss the anchor segment
+	  itself.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/matroska/matroska-mux.c:
-	  matroska-mux: Handle pixel-aspect-ratio caps field correctly when checking caps equality
-	  Not having this field is equivalent with it being 1/1 so consider
-	  it like that. The generic caps functions are not aware of these
-	  semantics and would consider the caps different, causing a negotiation
-	  failure when caps are changing from caps with to caps without or the
-	  other way around.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1826>
+2022-08-07 14:27:47 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-03-01 20:56:43 +0200  Sebastian Dröge <sebastian@centricular.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Dump init uri details for segments.
+	  When dumping an m3u8 playlist to debug, include information about any
+	  initialisation data.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/matroska/matroska-mux.c:
-	  matroska-mux: Handle multiview-mode/flags caps fields correctly when checking caps equality
-	  Not having these fields is equivalent with them being mono/0 so consider
-	  them like that. The generic caps functions are not aware of these
-	  semantics and would consider the caps different, causing a negotiation
-	  failure when caps are changing from caps with to caps without or the
-	  other way around.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1826>
+2022-08-07 14:26:45 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-27 03:17:26 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: Use gst_hls_media_playlist_recalculate_stream_time()
+	  Instead of recalculating stream times manually in a playlist, let the playlist
+	  do it, so that it fixes up partial segment stream times too.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/matroska/matroska-mux.c:
-	  matroska-mux: If a stream has a TITLE tag, use it for the name.
-	  If a title tag is pushed to a pad, store it as the Track name.
-	  This means that players will use it as the human readable
-	  description of the track, instead of something generic like 'Video'
-	  or 'Subtitle'
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1798>
+2022-08-04 23:20:33 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-27 02:39:28 +1100  Jan Schmidt <jan@centricular.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	  hlsdemux2: LL-HLS improvements
+	  Fixes for stream_time recalculation and handling in partial segments.
+	  Disallow bitrate switching when in the middle of partial segments - only at a
+	  full segment (or right before the first partial segment of a segment).
+	  It's possible but more difficult to switch bitrates in the middle of a partial
+	  segment group, since they are less likely to have aligned keyframes. In any
+	  case, the seek code can't do that right now.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/matroska/matroska-demux.c:
-	* gst/matroska/matroska-demux.h:
-	  matroskademux: Don't parse Tracks element twice
-	  If the tracks element was parsed from the SeekEntry, don't
-	  parse it a second time and recreate tracks, as this
-	  loses any tags that were read using the seek table.
-	  If a genuinely new Tracks element is found, do read that
-	  as it is needed for MSE support.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1798>
+2022-08-04 05:37:59 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-23 11:10:11 +0100  Sebastian Fricke <sebastian.fricke@collabora.com>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	  hlsdemux2: Continue implementing LL-HLS support
+	  Somewhat working support for proceeding into the partial segments appearing at
+	  the live edge of the playlist.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* README.md:
-	  Maintain build instructions at a single location
-	  Do not maintain similar build instructions within each gst-plugins-*
-	  subproject and the subproject/gstreamer subproject. Use the build
-	  instructions from the mono-repository and link to them via hyperlink.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1743>
+2022-07-30 00:47:18 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-03-01 00:12:56 +0200  Vivia Nikolaidou <vivia@ahiru.eu>
+	* ext/adaptivedemux2/hls/gsthlsdemux-util.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Mark locations where partial segments need handling
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/deinterlace/x86/yadif.asm:
-	  yadif.asm: Fix improper usage of LOAD macro
-	  LOAD macro relies in m7 being zero for interleaving purposes. Using LOAD
-	  on the m7 register makes it interleave with its new content instead of
-	  with 0.
-	  The effect of this bug was bobbing on some static lines that appeared
-	  over fast-moving content.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1816>
+2022-06-22 00:15:04 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-03-01 00:12:33 +0200  Vivia Nikolaidou <vivia@ahiru.eu>
+	* ext/adaptivedemux2/hls/gsthlsdemux.c:
+	* ext/adaptivedemux2/hls/gsthlsdemux.h:
+	  hlsdemux2: Start adding partial_segment handling
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/deinterlace/x86/yadif.asm:
-	  yadif.asm: Typo fixes in comments
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1816>
+2022-06-17 14:51:39 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-28 20:39:11 +0200  Vivia Nikolaidou <vivia@ahiru.eu>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Note STABLE-RENDITION-ID is not handled
+	  Add a comment that STABLE-RENDITION-ID is not yet parsed or used.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/deinterlace/yadif.c:
-	  yadif: Fix bug in C implementation of CHECK
-	  It was different compared to the corresponding part in both ffmpeg and
-	  the asm implementation. Fixing this makes videotestsrc pattern=spokes
-	  not jump at all when not using the asm optimisations.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1816>
+2022-06-09 01:19:46 +1000  Jan Schmidt <jan@centricular.com>
 
-2021-10-19 16:10:06 +0800  Ming Qian <ming.qian@nxp.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: Calculate stream times for partial segments
+	  When calculating stream times for segments, fill in the stream time fields on
+	  any attached partial segments
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* sys/v4l2/gstv4l2videodec.c:
-	* sys/v4l2/gstv4l2videodec.h:
-	  v4l2videodec : enable resolution change
-	  The dynamic resolution changes when
-	  the sequence starts when the decoder detects a coded frame with one or
-	  more of the following parameters different from those previously
-	  established (and reflected by corresponding queries):
-	  1.coded resolution (OUTPUT width and height),
-	  2.visible resolution (selection rectangles),
-	  3.the minimum number of buffers needed for decoding,
-	  4.bit-depth of the bitstream has been changed.
-	  Although gstreamer parser has parsed the stream resolution.
-	  but there are some case that we need to handle resolution change event.
-	  1. bit-depth is different from the negotiated format.
-	  2. the capture buffer count can meet the demand
-	  3. there are some hardware limitations that the decoded resolution may
-	  be larger than the display size. For example, the stream size is
-	  1920x1080, but some vpu may decode it to 1920x1088.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1381>
-
-2021-10-26 10:03:42 +0800  Ming Qian <ming.qian@nxp.com>
+2022-06-08 23:48:31 +1000  Jan Schmidt <jan@centricular.com>
 
-	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2object.h:
-	* sys/v4l2/gstv4l2videodec.c:
-	  v4l2videodec : refactor the setup process of capture
-	  v4l2videodec do some refactoring so that it can support
-	  dynamic resolution change event.
-	  1.wrap the setup process of capture as a function,
-	  as decoder need setup the capture again when
-	  dynamic resolution change event is received.
-	  2.move the function "remove_padding"
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1381>
-
-2022-02-22 11:18:53 +0200  Sebastian Dröge <sebastian@centricular.com>
-
-	* gst/rtp/gstrtpac3pay.c:
-	* gst/rtp/gstrtpamrpay.c:
-	* gst/rtp/gstrtpdvpay.c:
-	* gst/rtp/gstrtpg723pay.c:
-	* gst/rtp/gstrtpg729pay.c:
-	* gst/rtp/gstrtpgstpay.c:
-	* gst/rtp/gstrtph261pay.c:
-	* gst/rtp/gstrtph263pay.c:
-	* gst/rtp/gstrtph263ppay.c:
-	* gst/rtp/gstrtph264pay.c:
-	* gst/rtp/gstrtph265pay.c:
-	* gst/rtp/gstrtpj2kpay.c:
-	* gst/rtp/gstrtpjpegpay.c:
-	* gst/rtp/gstrtpklvpay.c:
-	* gst/rtp/gstrtpmp4apay.c:
-	* gst/rtp/gstrtpmp4gpay.c:
-	* gst/rtp/gstrtpmp4vpay.c:
-	* gst/rtp/gstrtpmpapay.c:
-	* gst/rtp/gstrtpmpvpay.c:
-	* gst/rtp/gstrtpreddec.c:
-	* gst/rtp/gstrtpvp8pay.c:
-	* gst/rtp/gstrtpvp9pay.c:
-	* gst/rtp/gstrtpvrawpay.c:
-	* gst/rtp/rtpulpfeccommon.c:
-	* tests/check/elements/rtpred.c:
-	  rtp: In payloaders map the RTP marker flag to the corresponding buffer flag
-	  This allows downstream of a payloader to know the RTP header's marker
-	  flag without first having to map the buffer and parse the RTP header.
-	  Especially inside RTP header extension implementations this can be
-	  useful to decide which packet corresponds to e.g. the last packet of a
-	  video frame.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1776>
+	* tests/check/elements/hlsdemux_m3u8.c:
+	  hlsdemux2: Add unit test for parsing LL-HLS playlist
+	  Test parsing of partial segments (EXT-X-PART, EXT-X-PART-INF) and preload
+	  hints (EXT-X-PRELOAD)
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2021-12-06 12:49:18 -0500  Joseph Donofry <rubberduckie3554@gmail.com>
+2022-06-09 00:39:13 +1000  Jan Schmidt <jan@centricular.com>
 
-	* sys/osxaudio/gstosxaudiodeviceprovider.c:
-	  osxaudiosrc: Support a device as both input and output
-	  osxaudiodeviceprovider now probes devices more than once to determine
-	  if the device can function as both an input AND and output device.
-	  Previously, if the device provider detected that a device had any output
-	  capabilities, it was treated solely as an Audio/Sink.  This causes issues
-	  that have both input and output capabilities (for example, USB interfaces
-	  for professional audio have both input and output channels).  Such devices
-	  are now listed as both an Audio/Sink as well as an Audio/Source.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1385>
-
-2022-02-24 20:28:23 +0530  Sanchayan Maity <sanchayan@asymptotic.io>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Implement preload hint parsing
+	  Load EXT-X-PRELOAD-HINT into a preload_hints array in the media playlist
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtp/gstrtpldacpay.c:
-	* gst/rtp/gstrtpldacpay.h:
-	  rtp: ldac: Set frame count information in payload
-	  The RTP payload seems to be required as it carries the frame count
-	  information. Also, gst_rtp_base_payload_allocate_output_buffer had
-	  the second argument incorrect.
-	  Strangely some devices like Shanling MP4 and Sony XM3 would still
-	  work without this while some like the Sony XM4 do not.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1797>
-
-2022-02-21 11:37:26 -0500  Xavier Claessens <xavier.claessens@collabora.com>
+2022-06-08 14:33:56 +1000  Jan Schmidt <jan@centricular.com>
 
-	* ext/vpx/meson.build:
-	* gst/equalizer/meson.build:
-	* gst/isomp4/meson.build:
-	  devenv: Add some missing GStreamer specific env variables
-	  This should make "meson devenv" closer to what "gst-env.py" sets.
-	  - GST_VALIDATE_SCENARIOS_PATH
-	  - GST_VALIDATE_APPS_DIR
-	  - GST_OMX_CONFIG_DIR
-	  - GST_ENCODING_TARGET_PATH
-	  - GST_PRESET_PATH
-	  - GST_PLUGIN_SCANNER
-	  - GST_PTP_HELPER
-	  - _GI_OVERRIDES_PATH
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1768>
-
-2022-02-25 12:44:26 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Implement EXT-X-SERVER-CONTROL parsing
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/deinterlace/tvtime/greedyh.c:
-	  deinterlace: greedyh: Stop adding 2 to cur_field_idx
-	  Just a simplification.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1790>
+2022-06-08 01:35:44 +1000  Jan Schmidt <jan@centricular.com>
 
-2022-02-24 17:36:40 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	* ext/adaptivedemux2/hls/m3u8.h:
+	  hlsdemux2: Add parsing of partial segments
+	  Add partial segments to each media segment, and potentially create a trailing
+	  dummy segment if there are partial segments at the end of the playlist
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-	* gst/deinterlace/tvtime/greedyh.c:
-	  deinterlace: greedyh: Use _plane in _packed, fix planar formats
-	  This greatly reduces code duplication. It also exposed the cause for
-	  planar formats not being properly deinterlaced:
-	  The planar path was missing the initial offset adjustment that the
-	  packed path did to `L2` and `L2P` in the case of an even field, which
-	  caused it to select the wrong weave lines every other field.
-	  Add those offsets in `_plane`.
-	  Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1047
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1790>
-
-2022-02-25 12:39:31 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+2022-06-08 23:08:24 +1000  Jan Schmidt <jan@centricular.com>
 
-	* gst/deinterlace/tvtime/greedyh.c:
-	  deinterlace: greedyh: Rename _planar_plane to _plane
-	  As well as `i` to `plane`.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1790>
+	* ext/adaptivedemux2/hls/m3u8.c:
+	  hlsdemux2: make helper function for parsing times
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-02-25 12:36:17 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+2022-12-08 08:17:56 +0100  Edward Hervey <edward@centricular.com>
 
-	* gst/deinterlace/tvtime/greedyh.c:
-	  deinterlace: greedyh: Move code from _planar into _planar_plane
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1790>
+	* ext/adaptivedemux2/gstadaptivedemux-private.h:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	  adaptivedemux2: Global output position is always positive
+	  Change to non-signed GstClockTime for tracking
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
 
-2022-02-25 12:30:21 +0100  Jan Alexander Steffens (heftig) <jan.steffens@ltnglobal.com>
+2022-10-03 15:19:16 -0300  Thibault Saunier <tsaunier@igalia.com>
 
-	* gst/deinterlace/tvtime/greedyh.c:
-	  deinterlace: greedyh: Move _planar_plane upwards
-	  In preparation of refactoring. No functional change.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1790>
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/gstadaptivedemux-track.c:
+	  adaptivedemux2: Generate proper stream-id taking into account upstream
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3160>
 
-2022-02-22 10:13:28 +0100  Guillaume Desmottes <guillaume.desmottes@onestream.live>
+2023-01-24 15:58:24 +1100  Matthew Waters <matthew@centricular.com>
 
-	* gst/rtpmanager/rtpsource.c:
-	  rtpsource: fix rtp_source_get_nack_deadlines doc
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1775>
+	* ext/qt6/gstplugin.cc:
+	* ext/qt6/gstqml6gloverlay.cc:
+	* ext/qt6/gstqml6gloverlay.h:
+	* ext/qt6/gstqt6elements.h:
+	* ext/qt6/gstqt6glutility.cc:
+	* ext/qt6/gstqt6glutility.h:
+	* ext/qt6/meson.build:
+	* ext/qt6/qt6glrenderer.cc:
+	* ext/qt6/qt6glrenderer.h:
+	* tests/examples/qt6/meson.build:
+	* tests/examples/qt6/qmloverlay/main.cpp:
+	* tests/examples/qt6/qmloverlay/main.qml:
+	* tests/examples/qt6/qmloverlay/meson.build:
+	* tests/examples/qt6/qmloverlay/overlay.qml:
+	* tests/examples/qt6/qmloverlay/overlay2.qml:
+	* tests/examples/qt6/qmloverlay/qmloverlay.qrc:
+	  qml6: implement qml6gloverlay
+	  Based on the Qt5 version of qmlgloverlay.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3845>
+
+2023-01-31 16:44:10 +0100  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-2022-02-21 13:27:06 +1100  Matthew Waters <matthew@centricular.com>
+	* gst/rtpmanager/gstrtpptdemux.c:
+	  rtpptdemux: set different stream-id on each src pad
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3855>
 
-	* gst/rtp/gstrtpulpfecenc.c:
-	  ulpfecenc: slightly safer dispose impl
-	  Technically dispose can be called more than once (even if gstelement is
-	  not actually set up to do that) so need to protect against that.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1761>
+2023-01-31 15:28:22 +0100  Guillaume Desmottes <guillaume.desmottes@onestream.live>
 
-2022-02-21 13:24:07 +1100  Matthew Waters <matthew@centricular.com>
+	* gst/rtpmanager/gstrtpssrcdemux.c:
+	  rtpssrcdemux: set different stream-id on each src pad
+	  All the RTP src pads were sharing the same stream-id while each actually
+	  carry a different stream.
+	  This was causing problem for example when funneling the streams together
+	  and then trying to split them using 'streamiddemux'.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3855>
 
-	* gst/rtp/gstrtpulpfecenc.c:
-	  ulpfecenc: fix unmatched free() call
-	  One must always match a g_slice_new with a g_slice_free and a g_new with
-	  a g_free.  This was not the case for the internal ctx struct.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1761>
+2023-01-31 16:56:18 +0200  Sebastian Dröge <sebastian@centricular.com>
 
-2021-11-09 17:37:24 +1100  Matthew Waters <matthew@centricular.com>
+	* gst/rtsp/gstrtspsrc.c:
+	  rtspsrc: Also consider "Method Not Valid In This State" error in broken control URL handling workaround
+	  Some servers send a 455 error instead of any reasonable error when using
+	  a correctly constructed control URL.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3854>
 
-	* gst/rtp/gstrtpulpfecenc.c:
-	  rtpulpfecenc: add some debug logging
-	  Like, what configuration we are using or whether a fec packet is
-	  generated.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1761>
+2022-12-22 18:11:19 +1100  Matthew Waters <matthew@centricular.com>
 
-2022-02-18 15:23:13 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
+	* ext/qt6/gstplugin.cc:
+	* ext/qt6/gstqml6glsrc.cc:
+	* ext/qt6/gstqml6glsrc.h:
+	* ext/qt6/gstqt6elements.h:
+	* ext/qt6/meson.build:
+	* ext/qt6/qt6glwindow.cc:
+	* ext/qt6/qt6glwindow.h:
+	* tests/examples/qt6/meson.build:
+	* tests/examples/qt6/qmlsrc/grabqml.pro:
+	* tests/examples/qt6/qmlsrc/main.cpp:
+	* tests/examples/qt6/qmlsrc/main.qml:
+	* tests/examples/qt6/qmlsrc/meson.build:
+	* tests/examples/qt6/qmlsrc/qmlsrc.qrc:
+	  qt6: add qml6glsrc element
+	  Same functionality as qmlglsrc (Qt5) but for Qt6.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3737>
 
-	* gst/matroska/matroska-demux.c:
-	  matroska-demux: Emit a warning when no codec data found
-	  It is bad if an mkv file does not have codec data for the ProRes
-	  variant, so we should emit a warning. ffmpeg does the same thing.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1739>
+2020-01-02 22:24:36 +0100  Alicia Boya García <aboya@igalia.com>
 
-2022-02-20 15:20:07 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+	* gst/isomp4/qtdemux.c:
+	  qtdemux: Use safer clearing functions in dispose()
+	  In theory, `dispose()` functions should be idempotent and should be
+	  prepared not to crash or cause a double-free if an unref done from
+	  inside caused a recursive call to `dispose()` of the same object.
+	  https://developer.gnome.org/gobject/stable/howto-gobject-destruction.html
+	  This patch modifies the `dispose()` method to honor these constraints.
+	  Since the double `dispose()` call won't actually occur in qtdemux (there
+	  is no cycle detection mechanism that could invoke it to work that way),
+	  this is more of a code cleanup than a user-facing problem fix.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3822>
+
+2021-09-23 10:00:45 +0200  Daniel Knobe <daniel-knobe@web.de>
 
 	* docs/gst_plugins_cache.json:
-	  doc: Add NV12_16L32S into the cache
-	  Autogenerated by CI
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1567>
+	* gst/imagefreeze/gstimagefreeze.c:
+	  imagefreeze: add bayer support
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3807>
 
-2022-01-26 10:06:50 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-01-25 00:52:28 +0000  Pawel Stawicki <stawel+gstreamer@gmail.com>
 
-	* sys/v4l2/gstv4l2transform.c:
-	  v4l2transform: Handle caps changes
-	  As this element is single threaded, we only need to stop the objects to
-	  allow changing the format again. Fixes assertion notably on shutdown and
-	  on some other situation where the format may be set twice without
-	  actually activating the element.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1567>
+	* sys/v4l2/gstv4l2object.c:
+	  v4l2h264dec: Fix Raspberry Pi4 will not play video in application
+	  Ensure object v4l2object->pool will be released by
+	  correctly releasing the temporary thread-safety lock
+	  Fixes issue #1729
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3786>
 
-2022-01-26 09:55:09 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-01-11 01:11:06 +0530  Nirbheek Chauhan <nirbheek@centricular.com>
 
-	* sys/v4l2/gstv4l2object.c:
-	  v4l2object: Avoid crash on early failure
-	  This happens while an external error lead to an early shutdown.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1567>
+	* ext/qt/meson.build:
+	  meson: Add build_rpath for qt5 plugin on macOS
+	  Without this, the plugin cannot be loaded in a devenv because the
+	  RPATH is not added to the plugin dylib. This RPATH will be stripped on
+	  install, which is what we want.
+	  When deploying apps, people are supposed to use `macdeployqt` to
+	  create an AppBundle that bundles Qt for you and sets the RPATHs
+	  correctly to point to that bundled Qt.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3708>
 
-2022-01-25 14:34:32 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-01-24 22:50:37 +0100  Mathieu Duponchelle <mathieu@centricular.com>
 
-	* sys/v4l2/gstv4l2object.c:
-	  video4linux2: Add MM21 support
-	  This enables mtk-vcodec and MDP driver from mainline Linux kernel.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1567>
+	* gst/rtp/gstrtpredenc.c:
+	  redenc: fix setting of extension ID for twcc
+	  1 was previously hardcoded in, and the bug went under the radar because
+	  webrtcsink hardcodes the number too.
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3785>
 
-2022-01-25 14:08:47 -0500  Nicolas Dufresne <nicolas.dufresne@collabora.com>
+2023-01-08 17:40:11 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* sys/v4l2/gstv4l2bufferpool.c:
-	* sys/v4l2/gstv4l2object.c:
-	* sys/v4l2/gstv4l2object.h:
-	  Port plugins to gst_video_format_info_extrapolate_stride()
-	  This reduces code duplication and simplify addition of new
-	  pixel formats into related plugins.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1567>
+	* tests/check/elements/rtpcollision.c:
+	* tests/examples/rpicamsrc/webrtc-unidirectional-h264.c:
+	  good: tests: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-02-02 12:49:29 +0100  Rouven Czerwinski <rouven@czerwinskis.de>
+2023-01-08 17:38:17 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* sys/v4l2/gstv4l2tuner.c:
-	  gstv4l2tuner: return NULL if no norm set
-	  If the video4linux device supports norms but has no norm set, norm is
-	  returned as an uninitialized variable after the ioctl call, leading to
-	  gst_v4l2_tuner_get_norm_by_std_id() returning a random norm from the
-	  supported norms. Catch this case and instead return NULL to indicate
-	  that no norm is setup.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1625>
+	* sys/v4l2/gstv4l2allocator.c:
+	* sys/v4l2/gstv4l2bufferpool.c:
+	* sys/v4l2/v4l2-utils.c:
+	  v4l2: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-01-14 23:42:27 -0600  Tim Mooney <Tim.Mooney@ndsu.edu>
+2023-01-08 17:36:33 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* sys/v4l2/ext/types-compat.h:
-	  v4l2: include <sys/ioccom.h> on Illumos
-	  Needed for _IOR/_IORW
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1524>
+	* gst/xingmux/gstxingmux.c:
+	  xingmux: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-02-17 17:36:22 +0100  Sebastian Wick <sebastian.wick@redhat.com>
+2023-01-08 17:36:13 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/matroska/matroska-demux.c:
-	  matroska: default prores fourcc apcn
-	  If there is no codec private data for prores it should default to Apple
-	  ProRes 422 Standard Definition (apcn). Can be tested with
-	  strobe_scientist.mkv from
-	  https://developers.google.com/media/vp9/hdr-encoding
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1734>
+	* gst/udp/gstmultiudpsink.c:
+	  multiudpsink: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2021-11-16 17:35:25 +0900  Seungha Yang <seungha@centricular.com>
+2023-01-08 17:33:50 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/isomp4/qtdemux.c:
-	  qtdemux: Do not send unnecessary GAP events
-	  Each stream may have its own segment timeline
-	  (i.g., different segment.start or segment.base)
-	  depending on edit-list and composition-to-decode atom.
-	  Make sure whether time position of a stream has been actually
-	  far behind than that of current target stream.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1352>
+	* gst/rtpmanager/gstrtpmux.c:
+	* gst/rtpmanager/gstrtpptdemux.c:
+	* gst/rtpmanager/gstrtprtxreceive.c:
+	* gst/rtpmanager/gstrtprtxsend.c:
+	* gst/rtpmanager/rtpjitterbuffer.c:
+	* gst/rtpmanager/rtpsession.c:
+	* gst/rtpmanager/rtpsource.c:
+	* gst/rtpmanager/rtptimerqueue.c:
+	  rtpmanager: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2021-10-01 20:27:28 +0900  Seungha Yang <seungha@centricular.com>
+2023-01-08 17:31:57 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* meson.build:
-	  meson: Do hard build error for some MSVC warnings
-	  Handle various MSVC warnings as errors for development version.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1006>
+	* gst/rtp/gstrtpmparobustdepay.c:
+	* gst/rtp/gstrtpreddec.c:
+	* gst/rtp/gstrtpredenc.c:
+	* gst/rtp/rtpstoragestream.c:
+	  rtp: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2021-04-08 12:18:09 +0300  Sebastian Dröge <sebastian@centricular.com>
+2023-01-08 17:31:29 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/isomp4/gstqtmux.c:
-	  qtmux: Don't post an error message if pushing a sample failed with FLUSHING
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1711>
+	* gst/multifile/gstsplitmuxpartreader.c:
+	* gst/multifile/gstsplitmuxsink.c:
+	* gst/multifile/gstsplitmuxsrc.c:
+	  multifile: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-02-11 21:35:54 +0100  Heiko Becker <heirecka@exherbo.org>
+2023-01-08 17:31:03 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/lame/meson.build:
-	  meson: Don't build lame plugin with -Dlame=disabled
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1686>
+	* gst/matroska/matroska-read-common.c:
+	  matroska: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-02-11 23:55:57 +0100  Marek Vasut <marex@denx.de>
+2023-01-08 17:27:38 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/jpeg/gstjpegdec.c:
-	  jpegdec: Pull row_stride from GST_VIDEO_FRAME_PLANE_STRIDE()
-	  The libjpeg-turbo internal state might not be correctly initialized for
-	  the first frame in a stream, pull the frame stride from gstreamer frame
-	  metadata instead, which is correct even for the first frame, and which
-	  makes this code consistent with the surrounding lines.
-	  Fixes: e6d83d8f96 ("jpegdec: Support libjpeg-turbo colorspace conversion")
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1687>
+	* gst/flv/gstflvmux.c:
+	* gst/flv/gstindex.c:
+	* gst/flv/gstmemindex.c:
+	  flv: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-02-11 23:44:24 +0100  Marek Vasut <marex@denx.de>
+2023-01-08 17:27:21 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/jpeg/gstjpegdec.c:
-	  jpegdec: Call gst_jpeg_turbo_parse_ext_fmt_convert() before jpeg_start_decompress()
-	  It is imperative that the libjpeg-turbo state is properly initialized
-	  before jpeg_start_decompress() is called. Make sure cinfo.out_color_space
-	  and cinfo.raw_data_out are set to their final values matching their peer
-	  caps before calling jpeg_start_decompress().
-	  Fixes: e6d83d8f96 ("jpegdec: Support libjpeg-turbo colorspace conversion")
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1687>
+	* gst/dtmf/gstdtmfsrc.c:
+	* gst/dtmf/gstrtpdtmfsrc.c:
+	  dtmf: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-02-11 23:44:20 +0100  Marek Vasut <marex@denx.de>
+2023-01-08 17:26:17 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/jpeg/gstjpegdec.c:
-	  jpegdec: Factor out gst_jpeg_turbo_parse_ext_fmt_convert()
-	  Pull out peer caps checking code into gst_jpeg_turbo_parse_ext_fmt_convert().
-	  This code is used by libjpeg-turbo extras to determine whether peer is capable
-	  of handling buffers into which libjpeg-turbo can directly decode data. This
-	  kind of check must be performed before jpeg_start_decompress() is called in
-	  gst_jpeg_dec_prepare_decode() as well as in gst_jpeg_dec_negotiate(), hence
-	  the common code.
-	  This commit does modify the code a little to make it easier to call from both
-	  call sites without much duplication, hence the extra `if (*clrspc)` test.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1687>
-
-2022-02-11 23:29:27 +0100  Marek Vasut <marex@denx.de>
+	* ext/vpx/gstvp8enc.c:
+	* ext/vpx/gstvp9enc.c:
+	* ext/vpx/gstvpxenc.c:
+	  vpx: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-	* ext/jpeg/gstjpegdec.c:
-	  Revert "jpegdec: only allow conversions from RGB"
-	  This reverts commit 2aa2477208c029b0e1b8232d69f4f99a3bf1d473.
-	  The commit is completely wrong, libjpeg-turbo is perfectly capable
-	  of decoding I420 (YUV) to RGB. The test case provided alongside the
-	  aforementioned commit passes without this revert because it decodes
-	  image of JCS_YCrCb color space, so the new `if (clrspc == JCS_RGB)`
-	  condition is false on that image, and the libjpeg-turbo decoding
-	  does not get used. The real bug is hidden by that commit.
-	  The real problem is in the call order of gst_jpeg_dec_prepare_decode()
-	  and gst_jpeg_dec_negotiate(). The gst_jpeg_dec_prepare_decode() calls
-	  jpeg_start_decompress() which sets up internal state of the libjpeg,
-	  however, neither cinfo.out_color_space nor cinfo.raw_data_out are
-	  set correctly yet. Those two are set up in gst_jpeg_dec_negotiate()
-	  which is called a bit later. Therefore, the real fix is the set up
-	  cinfo.out_color_space and cinfo.raw_data_out before calling
-	  jpeg_start_decompress(). This is however a separate patch.
-	  Fixes: 2aa2477208 ("jpegdec: only allow conversions from RGB")
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1687>
-
-2022-02-01 14:28:24 +0100  Bastien Nocera <hadess@hadess.net>
-
-	* ext/gtk/gtkgstglwidget.c:
-	  gtk: Fix rotation not being applied when paused
-	  The video wouldn't be redrawn immediately when a rotation was applied
-	  but the pipeline was paused, as no new buffers were scheduled to be
-	  displayed.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1618>
-
-2022-02-01 14:26:02 +0100  Bastien Nocera <hadess@hadess.net>
+2023-01-08 17:25:46 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* ext/gtk/gtkgstbasewidget.c:
-	* ext/gtk/gtkgstbasewidget.h:
-	  gtk: Add a way to queue redrawing the base GTK widget
-	  This will be used to request a redraw of the GTK widget should the
-	  display be changed using properties not directly handled by the base GTK
-	  widget, but by one of its descendants.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1618>
+	* ext/gdk_pixbuf/gstgdkpixbufsink.c:
+	  gdkpixbuf: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2022-01-18 17:53:30 +0100  Robert Rosengren <robertr@axis.com>
+2023-01-08 17:25:25 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	  rtpbin: Safer ts-offset-smoothing-factor calculation
-	  Protect the ts-offset-smoothing-factor calculation from overflow. Output
-	  warning and fallback to ts-offset if it is detected.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1409>
+	* ext/pulse/pulsesink.c:
+	  pulseaudio: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2021-11-23 09:03:28 +0100  Robert Rosengren <robertr@axis.com>
+2023-01-08 17:22:46 +0000  Tim-Philipp Müller <tim@centricular.com>
 
-	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpbin.h:
-	  rtpbin: add ts-offset-smoothing-factor property
-	  Add property to set the TS offset smoothing factor and set default value
-	  to not use it.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1409>
+	* ext/adaptivedemux2/dash/gstdashdemux.c:
+	* ext/adaptivedemux2/dash/gstmpdadaptationsetnode.c:
+	* ext/adaptivedemux2/dash/gstmpdclient.c:
+	* ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.c:
+	* ext/adaptivedemux2/dash/gstmpdparser.c:
+	* ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.c:
+	* ext/adaptivedemux2/dash/gstmpdsegmentbasenode.c:
+	* ext/adaptivedemux2/dash/gstmpdsegmenturlnode.c:
+	* ext/adaptivedemux2/dash/gstmpdurltypenode.c:
+	* ext/adaptivedemux2/dash/gstxmlhelper.c:
+	* ext/adaptivedemux2/downloadrequest.c:
+	* ext/adaptivedemux2/gstadaptivedemux.c:
+	* ext/adaptivedemux2/gstadaptivedemuxutils.c:
+	* ext/adaptivedemux2/mss/gstmssmanifest.c:
+	  adaptivedemux2: drop use of GSlice
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3695>
 
-2019-02-26 16:39:55 +0100  Danny Smith <dannys@axis.com>
+2023-01-23 12:01:00 +0100  David Svensson Fors <davidsf@axis.com>
 
-	* gst/rtpmanager/gstrtpbin.c:
-	  rtpbin: applied smoothing to jittery sender time-stamps
-	  Applying a moving average filter to the timestamp offsets
-	  for smoothing jittery and preventing aggressive skew handling.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1409>
+	* gst/udp/gstudpsrc.c:
+	  udpsrc: GstSocketTimestampMessage only for SCM_TIMESTAMPNS
+	  Deserialize socket control messages as GstSocketTimestampMessage only
+	  if (level, type) is (SOL_SOCKET, SCM_TIMESTAMPNS).
+	  Without this patch, messages with types SCM_RIGHTS or SCM_CREDENTIALS
+	  could be deserialized as GstSocketTimestampMessage instead of
+	  GUnixFDMessage or GUnixCredentialsMessage from gio.
+	  Fixes #1736
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3777>
 
-2018-05-29 16:24:02 +0200  Danny Smith <dannys@axis.com>
+2023-01-09 08:16:36 +0000  Hiero32 <unnoh@tkd.att.ne.jp>
 
 	* docs/gst_plugins_cache.json:
-	* gst/rtpmanager/gstrtpbin.c:
-	* gst/rtpmanager/gstrtpbin.h:
-	  rtpbin: added option for setting min_ts_offset in ntp-sync mode
-	  Constantly updating the ts_offset results in audiable glitches
-	  when streaming audio using ntp-sync=true. By requiring a minimum
-	  offset before updating ts_offset this can be mitigated. Added a
-	  parameter which can be used to set min_ts_offset in ntp-sync mode.
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1409>
+	* gst/debugutils/gsttaginject.c:
+	* gst/debugutils/gsttaginject.h:
+	  taginject: Add scope property
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3697>
 
-2022-02-04 11:15:47 +0000  Tim-Philipp Müller <tim@centricular.com>
+2023-01-23 23:04:53 +0000  Tim-Philipp Müller <tim@centricular.com>
 
 	* docs/gst_plugins_cache.json:
 	* meson.build:
 	  Back to development
-	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1635>
+	  Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3775>
 
-=== release 1.20.0 ===
+=== release 1.22.0 ===
 
diff --git a/NEWS b/NEWS
index 9802493d3252203d2e26c8c315a9bdbde953807d..ddf0da3d5c7194fdd5f4fdd48e8dfa139eb31aae 100644
--- a/NEWS
+++ b/NEWS
@@ -1,1151 +1,1295 @@
-GStreamer 1.22 Release Notes
-
-GStreamer 1.22.0 was originally released on 23 January 2023.
-
-See https://gstreamer.freedesktop.org/releases/1.22/ for the latest
-version of this document.
-
-Last updated: Monday 23 January 2023, 17:00 UTC (log)
-
-Introduction
-
-The GStreamer team is proud to announce a new major feature release in
-the stable 1.x API series of your favourite cross-platform multimedia
-framework!
-
-As always, this release is again packed with many new features, bug
-fixes and other improvements.
-
-Highlights
-
--   AV1 video codec support improvements
--   New HLS, DASH and Microsoft Smooth Streaming adaptive streaming
-    clients
--   Qt6 support for rendering video inside a QML scene
--   Minimal builds optimised for binary size, including only the
-    individual elements needed
--   Playbin3, Decodebin3, UriDecodebin3, Parsebin enhancements and
-    stabilisation
--   WebRTC simulcast support and support for Google Congestion Control
--   WebRTC-based media server ingestion/egress (WHIP/WHEP) support
--   New easy to use batteries-included WebRTC sender plugin
--   Easy RTP sender timestamp reconstruction for RTP and RTSP
--   ONVIF timed metadata support
--   New fragmented MP4 muxer and non-fragmented MP4 muxer
--   New plugins for Amazon AWS storage and audio transcription services
--   New gtk4paintablesink and gtkwaylandsink renderers
--   New videocolorscale element that can convert and scale in one go for
-    better performance
--   High bit-depth video improvements
--   Touchscreen event support in navigation API
--   Rust plugins now shipped in macOS and Windows/MSVC binary packages
--   H.264/H.265 timestamp correction elements for PTS/DTS reconstruction
-    before muxers
--   Improved design for DMA buffer sharing and modifier handling for
-    hardware-accelerated video decoders/encoders/filters and
-    capturing/rendering on Linux
--   Video4Linux2 hardware accelerated decoder improvements
--   CUDA integration and Direct3D11 integration and plugin improvements
--   New H.264 / AVC, H.265 / HEVC and AV1 hardware-accelerated video
-    encoders for AMD GPUs using the Advanced Media Framework (AMF) SDK
--   applemedia: H.265 / HEVC video encoding + decoding support
--   androidmedia: H.265 / HEVC video encoding support
--   New “force-live” property for audiomixer, compositor, glvideomixer,
-    d3d11compositor etc.
--   Lots of new plugins, features, performance improvements and bug
-    fixes
-
-Major new features and changes
-
-AV1 video codec support improvements
-
-AV1 is a royalty free next-generation video codec by AOMedia and a free
-alternative to H.265/HEVC.
-
-While supported in earlier versions of GStreamer already, this release
-saw a lot of improvements across the board:
-
--   Support for hardware encoding and decoding via VAAPI/VA, AMF, D3D11,
-    NVCODEC, QSV and Intel MediaSDK. Hardware codecs for AV1 are slowly
-    becoming available in embedded systems and desktop GPUs (AMD, Intel,
-    NVIDIA), and these can now be used via GStreamer.
-
--   New AV1 RTP payloader and depayloader elements.
-
--   New encoder settings in the AOM reference encoder-based av1enc
-    element.
-
--   Various improvements in the AV1 parser and in the MP4/Matroska/WebM
-    muxers/demuxers.
-
--   dav1d and rav1e based software decoder/encoder elements shipped as
-    part of the binaries.
-
--   AV1 parser improvements and various bugfixes all over the place.
-
-Touchscreen event support in Navigation API
-
-The Navigation API supports the sending of key press events and mouse
-events through a GStreamer pipeline. Typically these will be picked up
-by a video sink on which these events happen and then the event is
-transmitted into the pipeline so it can be handled by elements inside
-the pipeline if it wasn’t handled by the application.
-
-This has traditionally been used for DVD menu support, but can also be
-used to forward such inputs to source elements that render a web page
-using a browser engine such as WebKit or Chromium.
-
-This API has now gained support for touchscreen events, and this has
-been implemented in various plugins such as the GTK, Qt, XV, and x11
-video sinks as well as the wpevideosrc element.
-
-GStreamer CUDA integration
-
--   New gst-cuda library
--   integration with D3D11 and NVIDIA dGPU NVMM elements
--   new cudaconvertscale element
+GStreamer 1.24 Release Notes
 
-GStreamer Direct3D11 integration
+GStreamer 1.24.0 was originally released on 4 March 2024.
 
--   New gst-d3d11 public library
-    -   gst-d3d11 library is not integrated with GStreamer documentation
-        system yet. Please refer to the examples
--   d3d11screencapture: Add Windows Graphics Capture API based capture
-    mode, including Win32 application window capturing
--   d3d11videosink and d3d11convert can support flip/rotation and crop
-    meta
--   d3d11videosink: New emit-present property and present signal so that
-    applications can overlay an image on Direct3D11 swapchain’s
-    backbuffer via Direct3D/Direct2D APIs. See also C++ and Rust
-    examples
--   d3d11compositor supports YUV blending/composing without intermediate
-    RGB(A) conversion to improve performance
--   Direct3D11 video decoders are promoted to GST_RANK_PRIMARY or
-    higher, except for the MPEG2 decoder
-
-H.264/H.265 timestamp correction elements
-
--   Muxers are often picky and need proper PTS/DTS timestamps set on the
-    input buffers, but that can be a problem if the encoded input media
-    stream comes from a source that doesn’t provide proper signalling of
-    DTS, such as is often the case for RTP, RTSP and WebRTC streams or
-    Matroska container files. Theoretically parsers should be able to
-    fix this up, but it would probably require fairly invasive changes
-    in the parsers, so two new elements h264timestamper and
-    h265timestamper bridge the gap in the meantime and can reconstruct
-    missing PTS/DTS.
+The latest bug-fix release in the stable 1.24 series is 1.24.10 and was released on 06 January 2025.
 
-Easy sender timestamp reconstruction for RTP and RTSP
+See https://gstreamer.freedesktop.org/releases/1.24/ for the latest version of this document.
 
--   it was always possible to reconstruct and retrieve the original RTP
-    sender timestamps in GStreamer, but required a fair bit of
-    understanding of the internal mechanisms and the right property
-    configuration and clock setup.
+Last updated: Tuesday 06 January 2025, 19:30 UTC (log)
 
--   rtspsrc and rtpjitterbuffer gained a new
-    “add-reference-timestamp-meta” property that if set puts the
-    original absolute reconstructed sender timestamps on the output
-    buffers via a meta. This is particularly useful if the sender is
-    synced to an NTP clock or PTP clock. The original sender timestamps
-    are either based on the RTCP NTP times, NTP RTP header extensions
-    (RFC6051) or RFC7273-style clock signalling.
+## Introduction
 
-Qt6 support
+The GStreamer team is proud to announce a new major feature release in the stable 1.x API series of your favourite
+cross-platform multimedia framework!
 
--   new qml6glsink element for Qt6 similar to the existing Qt5 element.
-    Matching source and overlay elements will hopefully follow in the
-    near future.
+As always, this release is again packed with many new features, bug fixes and other improvements.
 
-OpenGL + Video library enhancements
+## Highlights
 
--   Support for new video formats (NV12_4L4, NV12_16L32S, NV12_8L128,
-    NV12_10BE_8L128) and dmabuf import in more formats (Y410, Y212_LE,
-    Y212_BE, Y210, NV21, NV61)
+-   New Discourse forum and Matrix chat space
+-   New Analytics and Machine Learning abstractions and elements
+-   Playbin3 and decodebin3 are now stable and the default in gst-play-1.0, GstPlay/GstPlayer
+-   The va plugin is now preferred over gst-vaapi and has higher ranks
+-   GstMeta serialization/deserialization and other GstMeta improvements
+-   New GstMeta for SMPTE ST-291M HANC/VANC Ancillary Data
+-   New unixfd plugin for efficient 1:N inter-process communication on Linux
+-   cudaipc source and sink for zero-copy CUDA memory sharing between processes
+-   New intersink and intersrc elements for 1:N pipeline decoupling within the same process
+-   Qt5 + Qt6 QML integration improvements including qml6glsrc, qml6glmixer, qml6gloverlay, and qml6d3d11sink elements
+-   DRM Modifier Support for dmabufs on Linux
+-   OpenGL, Vulkan and CUDA integration enhancements
+-   Vulkan H.264 and H.265 video decoders
+-   RTP stack improvements including new RFC7273 modes and more correct header extension handling in depayloaders
+-   WebRTC improvements such as support for ICE consent freshness, and a new webrtcsrc element to complement webrtcsink
+-   WebRTC signallers and webrtcsink implementations for LiveKit and AWS Kinesis Video Streams
+-   WHIP server source and client sink, and a WHEP source
+-   Precision Time Protocol (PTP) clock support for Windows and other additions
+-   Low-Latency HLS (LL-HLS) support and many other HLS and DASH enhancements
+-   New W3C Media Source Extensions library
+-   Countless closed caption handling improvements including new cea608mux and cea608tocea708 elements
+-   Translation support for awstranscriber
+-   Bayer 10/12/14/16-bit depth support
+-   MPEG-TS support for asynchronous KLV demuxing and segment seeking, plus various new muxer features
+-   Capture source and sink for AJA capture and playout cards
+-   SVT-AV1 and VA-API AV1 encoders, stateless AV1 video decoder
+-   New uvcsink element for exporting streams as UVC camera
+-   DirectWrite text rendering plugin for windows
+-   Direct3D12-based video decoding, conversion, composition, and rendering
+-   AMD Advanced Media Framework AV1 + H.265 video encoders with 10-bit and HDR support
+-   AVX/AVX2 support and NEON support on macOS on Apple ARM64 CPUs via new liborc
+-   GStreamer C# bindings have been updated
+-   Rust bindings improvements and many new and improved Rust plugins
+-   Rust plugins now shipped in packages for all major platforms including Android and iOS
+-   Lots of new plugins, features, performance improvements and bug fixes
 
--   Improved support for tiled formats with arbitrary tile dimensions,
-    as needed by certain hardware decoders/encoders
+## Major new features and changes
 
--   glvideomixer: New “crop-left,”crop-right, “crop-top” and
-    “crop-bottom” pad properties for cropping inputs
+### Discourse forum and Matrix chat space
 
--   OpenGL support for gst_video_sample_convert():
+-   The new Discourse forum and Matrix chat space are now our preferred communication channels for support and developer chat.
 
-    -   Used for video snapshotting and thumbnailing, to convert buffers
-        retrieved from appsinks or sink “last-sample” properties in
-        JPG/PNG thumbnails.
-    -   This function can now take samples and buffers backed by GL
-        textures as input and will automatically plug a gldownload
-        element in that case.
-
-High bit-depth support (10, 12, 16 bits per component value) improvements
-
--   compositor can now handle any supported input format and also mix
-    high-bitdepth (10-16 bit) formats (naively)
-
--   videoflip has gained support for higher bit depth formats.
-
--   vp9enc, vp9dec now support 12-bit formats and also 10-bit 4:4:4
-
-WebRTC
-
--   Allow insertion of bandwidth estimation elements e.g. for Google
-    Congestion Control (GCC) support
-
--   Initial support for sending or receiving simulcast streams
-
--   Support for asynchronous host resolution for STUN/TURN servers
-
--   GstWebRTCICE was split into base classes and implementation to make
-    it possible to plug custom ICE implementations
-
--   webrtcsink: batteries-included WebRTC sender (Rust)
-
--   whipsink: WebRTC HTTP ingest (WHIP) to a MediaServer (Rust)
-
--   whepsrc: WebRTC HTTP egress (WHEP) from a MediaServer (Rust)
-
--   Many other improvements and bug fixes
-
-New HLS, DASH and MSS adaptive streaming clients
-
-A new set of “adaptive demuxers” to support HLS, DASH and MSS adaptive
-streaming protocols has been added. They provide improved performance,
-new features and better stream compatibility compared to the previous
-elements. These new elements require a “streams-aware” pipeline such as
-playbin3, uridecodebin3 or urisourcebin.
-
-The previous elements’ design prevented implementing several use-cases
-and fixing long-standing issues. The new elements were re-designed from
-scratch to tackle those:
-
--   Scheduling Only 3 threads are present, regardless of the number of
-    streams selected. One in charge of downloading fragments and
-    manifests, one in charge of outputting parsed data downstream, and
-    one in charge of scheduling. This improves performance, resource
-    usage and latency.
-
--   Better download control The elements now directly control the
-    scheduling and download of manifests and fragments using libsoup
-    directly instead of depending on external elements for downloading.
-
--   Stream selection, only the selected streams are downloaded. This
-    improves bandwith usage. Switching stream is done in such a way to
-    ensure there are no gaps, meaning the new stream will be switched to
-    only once enough data for it has been downloaded.
-
--   Internal parsing, the downloaded streams are parsed internally. This
-    allows the element to fully respect the various specifications and
-    offer accurate buffering, seeking and playback. This is especially
-    important for HLS streams which require parsing for proper
-    positioning of streams.
-
--   Buffering and adaptive rate switching, the new elements handle
-    buffering internally which allows them to have a more accurate
-    visibility of which bandwith variant to switch to.
-
-Playbin3, Decodebin3, UriDecodebin3, Parsebin improvements
-
-The “new” playback elements introduced in 1.18 (playbin3 and its various
-components) have been refactored to allow more use-cases and improve
-performance. They are no longer considered experimental, so applications
-using the legacy playback elements (playbin and (uri)decodebin) can
-migrate to the new components to benefit from these improvements.
-
--   Gapless The “gapless” feature allows files and streams to be
-    fetched, buffered and decoded in order to provide a “gapless”
-    output. This feature has been refactored extensively in the new
-    components:
-    -   A single (uri)decodebin3 (and therefore a single set of
-        decoders) is used. This improves memory and cpu usage, since on
-        identical codecs a single decoder will be used.
-    -   The “next” stream to play will be pre-rolled “just-in-time”
-        thanks to the buffering improvements in urisourcebin (see below)
-    -   This feature is now handled at the uridecodebin3 level.
-        Applications that wish to have a “gapless” stream and process it
-        (instead of just outputting it, for example for transcoding,
-        retransmission, …) can now use uridecodebin3 directly. Note that
-        a streamsynchronizer element is required in that case.
--   Buffering improvements The urisourcebin element is in charge of
-    fetching and (optionally) buffering/downloading the stream. It has
-    been extended and improved:
-    -   When the parse-streams property is used (by default in
-        uridecodebin3 and playbin3), compatible streams will be demuxed
-        and parsed (via parsebin) and buffering will be done on the
-        elementary streams. This provides a more accurate handling of
-        buffering. Previously buffering was done on a best-effort basis
-        and was mostly wrong (i.e. downloading more than needed).
-    -   Applications can use urisourcebin with this property as a
-        convenient way of getting elementary streams from a given URI.
-    -   Elements can handle buffering themselves (such as the new
-        adaptive demuxers) by answering the GST_QUERY_BUFFERING query.
-        In that case urisourcebin will not handle it.
--   Stream Selection Efficient stream selection was previously only
-    possible within decodebin3. The downside is that this meant that
-    upstream elements had to provide all the streams from which to chose
-    from, which is inefficient. With the addition of the
-    GST_QUERY_SELECTABLE query, this can now be handled by elements
-    upstream (i.e. sources)
-    -   Elements that can handle stream selection internally (such as
-        the new adaptive demuxer elements) answer that query, and handle
-        the stream selection events themselves.
-    -   In this case, decodebin3 will always process all streams that
-        are provided to it.
--   Instant URI switching This new feature allows switching URIs
-    “instantly” in playbin3 (and uridecodebin3) without having to change
-    states. This mimics switching channels on a television.
-    -   If compatible, decoders will be re-used, providing lower
-        latency/cpu/memory than by switching states.
-    -   This is enabled by setting the instant-uri property to true,
-        setting the URI to switch to immediately, and then disabling the
-        instant-uri property again afterwards.
--   playbin3, decodebin3, uridecodebin3, parsebin, and urisrc are no
-    longer experimental
-    -   They were originally marked as ‘technology preview’ but have
-        since seen extensive usage in production settings, so are
-        considered ready for general use now.
-
-Fraunhofer AAC audio encoder HE-AAC and AAC-LD profile support
-
--   fdkaacenc:
-    -   Support for encoding to HE-AACv1 and HE-AACv2 profile
-    -   Support for encoding to AAC Low Delay (LD) profile
-    -   Advanced bitrate control options via new “rate-control”,
-        “vbr-preset”, “peak-bitrate”, and “afterburner” properties
-
-RTP rapid synchronization support in the RTP stack (RFC6051)
-
-RTP provides several mechanisms how streams can be synchronized relative
-to each other, and how absolute sender times for RTP packets can be
-obtained. One of these mechanisms is via RTCP, which has the
-disadvantage that the synchronization information is only distributed
-out-of-band and usually some time after the start.
-
-GStreamer’s RTP stack, specifically the rtpbin, rtpsession and
-rtpjitterbuffer elements, now also have support for retrieving and
-sending the same synchronization information in-band via RTP header
-extensions according to RFC6051 (Rapid Synchronisation of RTP Flows).
-Only 64-bit timestamps are supported currently.
-
-This provides per packet synchronization information from the very
-beginning of a stream and allows accurate inter-stream, and (depending
-on setup) inter-device, synchronization at the receiver side.
-
-ONVIF XML Timed Metadata support
-
-The ONVIF standard implemented by various security cameras also
-specifies a format for timed metadata that is transmitted together with
-the audio/video streams, usually over RTSP.
-
-Support for this timed metadata is implemented in the MP4 demuxer now as
-well as the new fragmented MP4 muxer and the new non-fragmented MP4
-muxer from the GStreamer Rust plugins. Additionally, the new onvif
-plugin ‒ which is part of the GStreamer Rust plugins ‒ provides general
-elements for handling the metadata and e.g. overlaying certain parts of
-it over a video stream.
-
-As part of this support for absolute UTC times was also implemented
-according to the requirements of the ONVIF standards in the
-corresponding elements.
-
-MP3 gapless playback support
-
-While MP3 can probably considered a legacy format at this point, a new
-feature was added with this release.
-
-When playing back plain MP3 files, i.e. outside a container format,
-switches between files can now be completely gapless if the required
-metadata is provided inside the file. There is no standardized metadata
-for this, but the LAME MP3 encoder writes metadata that can be parsed by
-the mpegaudioparse element now and forwarded to decoders for ensuring
-removal of padding samples at the front and end of MP3 files.
-
-“force-live” property for audio + video aggregators
-
-This is a quality of life fix for playout and streaming applications
-where it is common to have audio and video mixer elements that should
-operate in live mode from the start and produce output continuously.
-
-Often one would start a pipeline without any inputs hooked up to these
-mixers in the beginning, and up until now there was no way to easily
-force these elements into live mode from the start. One would have to
-add an initial live video or audio test source as dummy input to achieve
-this.
-
-The new “force-live” property makes these audio and video aggregators
-start in live mode without the need for any dummy inputs, which is
-useful for scenarios where inputs are only added after starting the
-pipeline.
-
-This new property should usually be used in connection with the
-“min-upstream-latency” property, i.e. you should always set a non-0
-minimum upstream latency then.
-
-This is now supported in all GstAudioAggregator and GstVideoAggregator
-subclasses such as audiomixer, audiointerleave, compositor,
-glvideomixer, d3d11compositor, etc.
+-   The mailing lists and IRC channel are on their way to being phased out, but Discourse can be used via e-mail as well.
 
-New elements and plugins
+-   For release announcements please subscribe to the News + Announcements category on Discourse, although we will continue to
+    also send announcements to the mailing list for the time being.
 
--   new cudaconvertscale element that can convert and scale in one pass
+### Playbin3, decodebin3 now stable and default
 
--   new gtkwaylandsink element based on gtksink, but similar to
-    waylandsink and uses Wayland APIs directly instead of rendering with
-    Gtk/Cairo primitives. This approach is only compatible with Gtk3,
-    and like gtksink this element only supports Gtk3.
+-   After a year of stability, testing and more improvements, playbin3, and its various components (uridecodebin3, decodebin3
+    and urisourcebin), are now the recommended playback components.
 
--   new h264timestamper and h265timestamper elements to reconstruct
-    missing pts/dts from inputs that might not provide them such as
-    e.g. RTP/RTSP/WebRTC inputs (see above)
+-   Some playback components have now switched to defaulting to playbin3: gst-play-1.0 and the GstPlay / GstPlayer libraries.
+    Application developers are strongly recommended to switch to using those components instead of the legacy playbin and
+    (uri)decodebin.
 
--   mfaacdec, mfmp3dec: Windows MediaFoundation AAC and MP3 decoders
+Improvements in this cycle:
 
--   new msdkav1enc AV1 video encoder element
+-   Better support missing/faulty decoders, attempt to use another one or gracefully un-select the stream.
 
--   new nvcudah264enc, nvcudah265enc, nvd3d11h264enc, and nvd3d11h265enc
-    NVIDIA GPU encoder elements to support zero-copy encoding, via CUDA
-    and Direct3D11 APIs, respectively
+-   Many fixes for more complex gapless and instant-switching scenarios
 
--   new nvautogpuh264enc and nvautogpuh265enc NVIDIA GPU encoder
-    elements: The auto GPU elements will automatically select a target
-    GPU instance in case multiple NVIDIA desktop GPUs are present, also
-    taking into account the input memory. On Windows CUDA or Direct3D11
-    mode will be determined by the elements automatically as well. Those
-    new elements are useful if target GPU and/or API mode (either CUDA
-    or Direct3D11 in case of Windows) is undeterminable from the encoder
-    point of view at the time when pipeline is configured, and therefore
-    lazy target GPU and/or API selection are required in order to avoid
-    unnecessary memory copy operations.
+-   Lower latency for live pipelines
 
--   new nvav1dec AV1 NVIDIA desktop GPU decoder element
+-   Fix for “chained” streams (ex: Ogg, or PMT update in MPEG-TS)
 
--   new qml6glsink element to render video with Qt6
+-   Fixes for hardware-accelerated playback with subtitles (provided the sink can handle offloading composition). This was also
+    partly due to a historical confusion between subtitle “decoders” (which decode the format to text and “parsers” (which only
+    do timing detection and optional seeking).
 
--   qsv: New Intel OneVPL/MediaSDK (a.k.a Intel Quick Sync) based
-    decoder and encoder elements, with gst-d3d11 (on Windows) and gst-va
-    (on Linux) integration
+### GstMeta serialization/deserialization and other GstMeta improvements
 
-    -   Support multi-GPU environment, for example, concurrent video
-        encoding using Intel iGPU and dGPU in a single pipeline
-    -   H.264 / H.265 / VP9 and JPEG decoders
-    -   H.264 / H.265 / VP9 / AV1 / JPEG encoders with dynamic encoding
-        bitrate update
-    -   New plugin does not require external SDK for building on Windows
+-   GstMeta serialization/deserialization allows metas to be transmitted or stored. This is used by the unixfd and cudaipc
+    plugins for inter-process communication (IPC). Implemented so far for GstCustomMeta, GstVideoMeta, GstAudioMeta and
+    GstReferenceTimestampMeta.
 
--   vulkanoverlaycompositor: new vulkan overlay compositor element to
-    overlay upstream GstVideoOverlayCompositonMeta onto the video
-    stream.
+-   Simplified GstCustomMeta registration with gst_meta_register_custom_simple() for the simple case where tags and transform
+    functions are not needed.
 
--   vulkanshaderspv: performs operations with SPIRV shaders in Vulkan
+-   GstMetaClearFunction clears the content of the meta. This will be called by buffer pools when a pooled buffer is returned to
+    the pool.
 
--   win32ipcvideosink, win32ipcvideosrc: new shared memory videosrc/sink
-    elements for Windows
+-   Add gst_meta_info_new() and gst_meta_info_register() to register a GstMeta in two steps for easier extensibility.
 
--   wicjpegdec, wicpngdec: Windows Imaging Component (WIC) based JPEG
-    and PNG decoder elements.
+### New unixfd plugin for efficient 1:N inter-process communication on Linux
 
--   Many exciting new Rust elements, see Rust section below
+-   unixfdsink and unixfdsrc are elements that, inspired by shmsink andn shmsrc, send UNIX file descriptors (e.g. memfd, dmabuf)
+    from one sink to multiple source elements in other processes on Linux.
 
-New element features and additions
+-   The unixfdsink proposes a memfd/shm allocator to upstream elements which allows for example videotestsrc to write directly
+    into memory that can be transfered to other processes without copying.
 
--   audioconvert: Dithering now uses a slightly slower, less biased PRNG
-    which results in better quality output. Also dithering can now be
-    enabled via the new “dithering-threshold” property for target bit
-    depths of more than 20 bits.
+### New GstMeta for SMPTE ST-291M HANC/VANC Ancillary Data
 
--   av1enc: Add “keyframe-max-dist” property for controlling max
-    distance between keyframes, as well as “enc-pass”, “keyframe-mode”,
-    “lag-in-frames” and “usage-profile” properties.
+-   Previously only various specific GstMeta for ancillary data were provided, such as GstVideoCaptionMeta and GstVideoAFDMeta.
+    The new GstAncillaryMeta allows passing arbitrary ancillary data between elements, including custom and non-standard
+    ancillary data. See GstAncillaryMeta for details.
 
--   cccombiner: new “output-padding” property
+-   Add with gst_buffer_add_ancillary_meta() and retrieve with gst_buffer_get_ancillary_meta() or
+    gst_buffer_iterate_ancillary_meta().
 
--   decklink: Add support for 4k DCI, 8k/UHD2 and 8k DCI modes
+-   Supported by the newly added AJA sink and source elements
 
--   dvbsubenc: Support for >SD resolutions is working correctly now.
+### DSD audio support
 
--   fdkaacenc: Add HE-AAC / HE-AACv2 profile support
+-   DSD audio is a non-PCM raw audio format representation and the GstAudio library gained support for this in form of new
+    GstDsdInfo and GstDsdFormat API.
 
--   glvideomixer: New “crop-left,”crop-right, “crop-top” and
-    “crop-bottom” pad properties for cropping inputs
+-   Support for DSD audio has been implemented in alsasink as well as the GstAudioSink and GstAudioRingBuffer base classes, and
+    the gst-libav plugin to enable FFmpeg-based DSD elements and functionality.
 
--   gssink: new ‘content-type’ property. Useful when one wants to upload
-    a video as video/mp4 instead of ’video/quicktime` for example.
+### Analytics and Machine Learning
 
--   jpegparse: Rewritten using the common parser library
+-   A new library, GstAnalytics, has been added. It defines a GstAnalyticsRelationMeta that can efficiently hold a large number
+    of observations from a data analysis process, for example from machine learning. It also contains a matrix of the
+    relationship between those observations.
+
+-   Three types of metadata are already defined in the library: object detection, classification and tracking.
+
+-   A new objectdetectionoverlay element has been merged that draws the bounding boxes and the classes from the object detection
+    and classification metadata types.
+
+-   The onnxinference element has been split into two parts. The first part works with the ONNX Runtime library to do the actual
+    inference, while the second part called ssdobjectdetector interprets the produced tensor. This new element creates
+    GstAnalyticsRelationMeta.
+
+-   The onnxinference element now accepts video frames without transformation if the module declares that it accepts the “Image”
+    type and the format is something that GStreamer knows.
+
+-   In the next release, tensor decoders such as ssdobjectdetector will live outside of the ONNX plugin so they can be used with
+    other machine learning acceleration frameworks.
+
+### Qt5 + Qt6 QML integration improvements
+
+-   The Qt5 qmlglsink, qmlgloverlay, qmlglmixer received support for directly consuming BGRA and YV12 video frames without a
+    prior glcolorconvert.
+
+-   New qml6glsrc, qml6glmixer, and qml6gloverlay elements as Qt6 counterparts to the existing Qt5 elements, also with support
+    for directly consuming BGRA and YV12 video frames without a prior glcolorconvert.
+
+-   qml6d3d11sink is a new Direct3D11 Qt6 QML sink for Windows as an alternative to the existing qml6glsink.
+
+### DRM Modifier Support for dmabufs on Linux
+
+The Linux dmabuf subsystem provides buffer sharing across different hardware device drivers and subsystems, and is used
+extensively by the DRM subsystem to exchange buffers between processes, contexts, and library APIs within the same process, and
+also to exchange buffers with other subsystems such as Linux Media.
+
+In GStreamer, it’s used on the capture side (v4l2src, pipewire), hardware-accelerated video decoders and encoders, OpenGL
+integration, Wayland renderers, etc.
+
+GStreamer has had support for dmabufs for a long time and was able to negotiate “zero-copy” paths between different components,
+however it only supported and assumed simple linear formats and was not able to negotiate complex non-linear formats. This meant
+that dmabuf support actually had to be disabled in many scenarios to avoid “garbled video”.
+
+With GStreamer 1.24 there is now full DRM modifier support and complex non-linear formats can be supported and negotiated
+between components.
+
+This is achieved with an extra drm_format field in video/x-raw(memory:DMABuf), format=(string)DMA_DRM caps, e.g.
+drm-format=(string)NV12:0x0x0100000000000001.
+
+See the GStreamer DMA buffers design documentation for more details.
+
+This is used in the VA-API va plugin, waylandsink, the MSDK plugin, and the OpenGL integration. Video4Linux support is expected
+to land in one of the next minor releases.
+
+New API has been added for easy handling of these new caps:
+
+-   GstVideoInfoDmaDrm plus associated functions, similar to GstVideoInfo, including conversion to and from GstVideoInfo with
+    gst_video_info_dma_drm_from_video_info() and gst_video_info_dma_drm_to_video_info()
+
+-   GST_VIDEO_DMA_DRM_CAPS_MAKE
+
+-   GST_VIDEO_FORMAT_DMA_DRM
+
+### OpenGL integration enhancements
+
+-   When using EGL, if both OpenGL ES and OpenGL are available, OpenGL ES is preferred over OpenGL. OpenGL ES supports some
+    necessary features required for dmabuf support. This does not apply if an external library/application chooses an OpenGL API
+    first.
+
+-   Improved support for dmabuf use cases. The glupload element now supports the new and improved dmabuf negotiation with
+    explicit modifiers.
+
+-   Base classes for mixing with OpenGL are now public API. GstGLBaseMixer and GstGLMixer are exposed matching the existing
+    filter-based GstGLBaseFilter and GstGLFilter base classes. The new OpenGL mixer base classes are based on
+    GstVideoAggregator.
+
+-   Add support for a ‘surfaceless’ EGL context using EGL_MESA_platform_surfaceless.
+
+-   Expose Vivante Framebuffer build-related files (pkg-config, gir) as public API
+
+-   Add support for more video formats:
+
+    -   A420 8/10/12/16-bit.
+    -   A422 8/10/12/16-bit.
+    -   A444 8/10/12/16-bit.
+    -   I420 10/12 bit.
+    -   RBGA.
+
+-   Add support for tiled video formats
+
+    -   NV12_16L32S (Mediatek format)
+    -   NV12_4L4 (Verisilicon Hantro format)
+
+-   glcolorconvert now has API for retrieving shader strings for:
+
+    -   swizzling (reordering components).
+    -   YUV->RGB conversion.
+    -   RGB->YUV conversion.
+
+-   Add more helpers for information about a particular video and/or GL format e.g. number of components, bytes used, or pixel
+    ordering.
+
+-   glvideomixer has new sink pad properties sizing-policy, xalign, yalign matching compositor.
+
+-   GstGLBufferPool now has a configuration option for allowing a number of buffers to be always outstanding allowing for
+    reducing the potential synchronisation delay when reusing OpenGL memory backed buffers.
+
+### Vulkan integration enhancements
+
+-   Add support for the Vulkan H.264 and H.265 decoders.
+
+-   Add support for timeline semaphores.
+
+-   Optionally use newer Vulkan functions for format selection.
+
+-   Add support for GPU-assisted validation.
+
+-   Vulkan/Wayland: add support for xdg_wm_base protocol for creating a visible debug window. Required as the previous wl_shell
+    interface is being removed from compositors.
+
+### CUDA / NVCODEC integration and feature additions
+
+-   New cudaipcsrc and cudaipcsink elements for zero-copy CUDA memory sharing between processes
+
+-   New nvJPEG library based nvjpegenc JPEG encoder element
+
+-   The NVIDIA desktop GPU decoder nvh264sldec, nvh265sldec, nvvp8sldec, and nvvp9sldec elements were renamed to nvh264dec,
+    nvh265dec, nvvp8dec, and nvvp9dec, respectively.
+
+-   GStreamer NVIDIA H.264 and H.265 encoders except for nvh264enc and nvh265enc gained support for CEA708 Closed Caption
+    inserting.
+
+-   OpenGL memory support is added to nv{cuda,autogpu}h264enc and nv{cuda,autogpu}h265enc elements
+
+-   CUDA stream integration: As of 1.24, CUDA stream synchronization is an application’s responsibility, and GStreamer will not
+    execute unnecessary synchronization operations. If an application needs direct access to CUDA memory via GST_MAP_CUDA map
+    flag, cuStreamSynchronize() or gst_cuda_memory_sync() call is required unless application-side CUDA operation is executed
+    with the GstCudaMemory’s associated CUDA stream.
+
+### RTP stack improvements
+
+-   New rtppassthroughpay element which just passes RTP packets through unchanged, but appears like an RTP payloader element.
+    This is useful for relaying an RTP stream as-is through gst-rtsp-server, which expects an RTP payloader with certain
+    properties at the end of an RTSP media sub-pipeline.
+
+-   New “timeout-inactive-rtp-sources” property on rtpbin, sdpdemux and rtpsession to allow applications to disable automatic
+    timeout of sources from which no data has been received for a while.
+
+-   rtpvp8pay, rtpvp9pay: expose “picture-id” as a property, and add a “picture-id-offset” property to the VP9 payloader to
+    bring it in line with the VP8 payloader.
+
+-   rtpjitterbuffer has seen improved media clock handling (clock equality and clock setting/resetting), as well as two new
+    properties that allow reconstruction of absolute PTP timestamps without actually syncing to the PTP clock, which can be
+    useful in scenarios where one wants to reconstruct the absolute PTP clock timestamps on a machine that doesn’t have access
+    to the network of the PTP clock provider. The two new properties are:
+
+    -   “rfc7273-use-system-clock”: allows the jitter buffer to assume that the system clock is synced sufficiently close to the
+        media clock used by an RFC7273 stream. By default the property is disabled and the jitter buffer will create a media
+        clock and try to sync to it, but this is only required to determine in which wraparound period from the media clock’s
+        Epoch the current RTP timestamps refer to (and thus to reconstruct absolute time stamps from them). If the property is
+        enabled the wraparound period and current offset from the Epoch will be determined based on the local system clock,
+        which means that no direct network connection to the media clock provider is needed to reconstruct absolute timestamps.
+        There is also no start-up delay, because there’s no clock sync that needs to be established first.
+
+    -   “rfc7273-reference-timestamp-meta-only”: If this property is enabled then the jitter buffer will do the normal timestamp
+        calculations for the output buffers according to the configured mode instead of making use of the RFC7273 media clock
+        for that. It will still calculate RFC7273 media clock timestamps, but only attach them to the output buffers in form of
+        a clock reference meta.
+
+-   RTP payloaders and depayloaders now have an “extensions” property for retrieving the list of currently enabled RTP header
+    extensions.
+
+-   rtpbin and webrtcbin no longer blindly set properties on the jitter buffer assuming it’s a standard rtpjitterbuffer, but
+    instead check if the property is available first, to better support non-standard jitterbuffers or even an identity element
+    in lieu of a jitter buffer.
+
+-   RTP header extension handling fixes for depayloaders that aggregate multiple input buffers into a single output buffer.
+    Before, only the last RTP input buffer was checked for header extensions. Now the depayloader remembers all RTP packets
+    pushed before an output buffer is produced and checks all RTP input buffers for header extensions.
+
+    -   Affected depayloaders: rtph264depay, rtph265depay, rtpvp8depay, rtpvp9depay, rtpxqtdepay, rtpasfdepay, rtpmp4gdepay,
+        rtpsbcdepay, rtpvorbisdepay, rtpmp4vdepay, rtptheoradepay, rtpsv3vdepay, rtpmp4adepay, rtpklvdepay, rtpjpegdepay,
+        rtpj2kdepay, rtph263pdepay, rtph263depay, rtph261depay. rtpgstdepay.
+
+### WebRTC improvements
+
+-   Add support for ICE consent freshness (RFC 7675). This requires libnice >= 0.1.22.
+
+-   Advertise the local side of an end-of-candidates with an empty candidate string.
+
+-   Add the number of Data Channels opened and closed to webrtcbin’s statistics.
+
+-   Various improvements and feature additions in the Rust webrtc plugin, which provides webrtcsrc and webrtcsink elements as
+    well as specific elements for different WebRTC signalling protocols. See the Rust plugins section below for more details.
+
+### Adaptive Streaming improvements and Low-Latency HLS (LL-HLS) support
+
+-   hlsdemux2 now supports Low-Latency HLS (LL-HLS)
+
+-   hlsdemux2 asynchronous playlist download and update improves responsiveness and bandwith usage.
+
+-   hlsdemux2 handles fallback variant URLs.
+
+-   hlsdemux2 is more responsive and accurate when handling seeks.
+
+-   dashdemux2 and hlsdemux2 have a new “start-bitrate” property, improving the decision for which initial stream variant that
+    will be used.
+
+-   dashdemux2, hlsdemux2, mssdemux2 have received many improvements regarding seeking, along with support for “early-seek”
+    which allows playback to start immediately from the requested position without any previous download.
+
+-   dashdemux2, hlsdemux2, mssdemux2 better handle errors on or near the live edge.
+
+-   dashsink can now use the dashmp4mux muxer from the Rust plugins and will also produce better and RFC 6381-compatible codec
+    strings. The “suggested-presentation-delay” property allows to set the suggested presentation delay in the MPD.
+
+-   No development took place on the legacy demuxers (dashdemux, hlsdemux, mssdemux). Application developers are reminded to use
+    the new demuxers instead. They are automatically picked up when using urisourcebin, uridecodebin3 or playbin3.
+
+### W3C Media Source Extensions library
+
+-   A new GStreamer library (mse) implementing the W3C Media Source Extensions specification was added.
+
+-   Applications can embed this library along with GStreamer in order to integrate software that uses the Media Source APIs
+    without relying on a web browser engine. Typically an application consuming this library will wrap the C API with JavaScript
+    bindings that match the Media Source API so their existing code can integrate with this library.
+
+### Closed Caption handling improvements
+
+-   ccconverter supports converting between the two CEA-608 fields.
+
+-   New cea608mux element for muxing multiple CEA-608 streams together.
+
+-   Various improvements and feature additions in the Rust-based closed caption elements. Check out the Rust plugins section
+    below for more details.
+
+### Precision Time Protocol (PTP) clock improvements
+
+-   Many fixes and compatibility/interoperability improvements.
+
+-   Better support for running on devices with multiple network interfaces.
+
+-   Allow sync to master clock on same host.
+
+-   PTP clock support is now also available on Windows.
+
+-   The standalone ptp-helper binary has been rewritten in Rust for portability and security. This works on Linux, Android,
+    Windows, macOS, FreeBSD, NetBSD, OpenBSD, DragonFlyBSD, Solaris and Illumos. Newly supported compared to the C version is
+    Windows. Compared to the C version various error paths are handled more correctly and a couple of memory leaks are fixed.
+    Otherwise it should work identically. The minimum required Rust version for compiling this is 1.48, i.e. the version
+    currently in Debian oldstable. On Windows, Rust 1.54 is needed at least.
+
+-   New ptp-helper Meson build option so PTP support can be disabled or required.
+
+-   gst_ptp_init_full() allows for a more fine-grained and extensible configuration and initialization of the GStreamer PTP
+    subsystem, including TTL configuration.
+
+### Bayer 10/12/14/16-bit depth support
+
+-   bayer2rgb and rgb2bayer now support bayer with 10/12/14/16 bit depths
+
+-   v4l2src and videotestsrc now support bayer with 10/12/14/16 bit depths
+
+-   imagefreeze gained bayer support as well
+
+### MPEG-TS improvements
+
+-   mpegtsdemux gained support for
+    -   segment seeking for seamless non-flushing looping, and
+    -   synchronous KLV
+-   mpegtsmux now
+    -   allows attaching PCR to non-PES streams
+    -   allows setting of the PES stream number for AAC audio and AVC video streams via a new “stream-number” property on the
+        muxer sink pads. Currently the PES stream number is hard-coded to zero for these stream types.
+    -   allows writing arbitrary Opus channel mapping families and up to 255 channels
+    -   separate handling of DVB and ATSC AC3 descriptors
+
+## New elements and plugins
+
+-   analyticsoverlay visualises object-detection metas on a video stream.
+
+-   autovideoflip and autodeinterlace are two new auto elements.
+
+-   AJA source and sink elements plus device provider for AJA capture and playout cards, including support for HANC/VANC
+    ancillary data.
+
+-   New cea608mux element for muxing multiple CEA-608 streams together.
+
+-   The codec2json plugin adds av12json, h2642json, h2652json and vp82json elements which convert AV1, H.264, H.265 and VP8
+    frame parameters into human readable JSON data, which is useful for debugging and testing purposes.
+
+-   New lc3 plugin with a decoder and encoder for the Bluetooth LC3 audio codec.
+
+-   New onnxinference element to run ONNX inference models on video buffers.
+
+-   New rtppassthroughpay element which just passes RTP packets through unchanged, but appears like an RTP payloader element.
+    This is mostly useful for medias that simply pass through an existing RTP stream in gst-rtsp-server.
+
+-   Qt6: qml6glsrc, qml6glmixer, qml6gloverlay, and qml6d3d11sink
+
+-   New SVT-AV1 encoder plugin, imported from SVT-AV1 but with many fixes.
+
+-   Many exciting new Rust elements, see Rust section below.
+
+-   New DirectWrite text rendering and Direct3D12 plugins (see Windows section below).
+
+-   New vaav1enc element for encoding video in AV1 (See VA-API section)
+
+-   New uvcsink element for exporting streams as UVC camera
+
+## New element features and additions
+
+-   alphacombine supports I420_10LE now for 10-bit WebM/alpha support.
+
+-   The amfcodec for hardware-accelerated video encoding using the Advanced Media Framework (AMF) SDK for AMD GPUs gained some
+    new features:
+
+    -   10-bit and HDR support for H.265 / HEVC and AV1 video encoders
+    -   B-frame support in the H.264 encoder
+    -   Initial support of pre-analysis and pre-encoding
+    -   Initial support of Smart Access Video for optimal distribution amongst multiple AMD hardware instances.
+
+-   appsink: new “propose-allocation” signal so applications can provide a buffer pool or allocators to the upstream elements,
+    as well as “max-time” and “max-buffers” properties to configure the maximum size of the appsink-internal queue in addition
+    to the existing “max-bytes” property.
+
+-   autovideoconvert exposes colorspace and scaler elements for well know elements
+
+-   avtp: add AVTP Raw Video Format payload and de-payload support.
+
+-   cacasink’s output driver can now be selected via the “driver” property.
+
+-   camerabin: various fixes and stability improvements
+
+-   clocksync: “QoS” property to optionally send QoS events upstream like a synchronising sink would.
+
+-   cutter: can add GstAudioLevelMeta on output buffers, which can be enabled via the new “audio-level-meta” property.
+
+-   dashdemux2 has a new “start-bitrate” property.
+
+-   dashsink can now use the dashmp4mux muxer from the Rust plugins and will also produce better and RFC 6381-compatible codec
+    strings. The “suggested-presentation-delay” property allows to set the suggested presentation delay in the MPD.
+
+-   deinterlace: Add support for 10/12/16-bit planar YUV formats
+
+-   The dvdspu subpicture overlay now implements GstVideoOverlayComposition support to make it work better with hardware
+    decoders where the video data should ideally stay on the GPU/VPU and the overlay blitting be delegated to the renderer.
+
+-   encodebin now automatically autoplugs timestamper elements such as h264timestamper or h265timestamper, based on new
+    “Timestamper” element factory type and rank.
+
+-   New fakevideodec element (see debugging section below).
+
+-   filesink: “file-mode” property to allow the ability to specify rb+ file mode, which overwrites an existing file. This is
+    useful in combination with splitmuxsink so that files can be pre-allocated which can be useful to reduce disk fragmentation
+    over time.
+
+-   flvmux: add “enforce-increasing-timestamps” property to allow disabling a hack that was added back in the day because
+    librtmp as used in rtmpsink would get confused by timestamps going backwards, but this is no longer required with rtmpsink2.
+    If set to true (still the default, for backwards compatibility), flvmux will modify buffers timestamps to ensure they are
+    always strictly increasing, inside one stream and also between the audio and video streams.
+
+-   giostreamsink: Add a property to close the stream on stop().
+
+-   h264parse improved its AU boundary detection.
+
+-   h264parse, h265parse, mpegvideoparse now support multiple unregistered user data SEI messages.
+
+-   insertbin is now a registered element and available via the registry, so can be constructed via parse-launch and not just
+    via the insertbin API.
+
+-   jack: libjack is now loaded dynamically at runtime instead of linking it at build time. This means the plugin can be shipped
+    on Windows and macOS and will work if there’s a user-installed JACK server/library setup.
+
+-   jpegparse now has a rank so it will be autoplugged if needed.
+
+-   kmssink: Add auto-detection for NXP i.MX8M Plus LCDIFv3, ST STM32 LTDC, and Texas Instruments TIDSS display controllers.
+
+-   matroskademux and matroskamux gained support for more raw video formats, namely RGBA64_LE, BGRA64_LE, GRAY10_BE32, GRAY16_LE
+
+-   mpg123audiodec’s rank was changed from MARGINAL to PRIMARY so it’s now higher than avdec_mp3, as it works better with
+    “freeformat” MP3s.
 
 -   msdk:
 
-    -   new msdkav1enc AV1 video encoder element
-    -   msdk decoders: Add support for Scaler Format Converter (SFC) on
-        supported Intel platforms for hardware accelerated conversion
-        and scaling
-    -   msdk encoders: support import of dmabuf, va memory and D3D11
-        memory
-    -   msdk encoders: add properties for low delay bitrate control and
-        max frame sizes for I/P frames
-    -   msdkh264enc, msdkh265enc: more properties to control intra
-        refresh
-    -   note that on systems with multi GPUs the Windows D3D11
-        integration might only work reliably if the Intel GPU is the
-        primary GPU
+    -   DRM modifier support on Linux
 
--   mxfdemux: Add support for Canon XF-HEVC
+    -   only expose codecs and capabilities actually supported by the platform
 
--   openaptx: Support the freeaptx library
+    -   msdkvpp video post-processing:
 
--   qroverlay:
+        -   new “hdr-tone-mapping” property to enable HDR-to-SDR tone mapping
+        -   new compute scaling mode
 
-    -   new “qrcode-case-sensitive” property allows encoding case
-        sensitive strings like wifi SSIDs or passwords.
-    -   added the ability to pick up data to render from an
-        upstream-provided custom GstQROverlay meta
+    -   Decoders sport D3D11 and VA integration, and the VP9 decoder supports certain resolution changes.
 
--   qtdemux: Add support for ONVIF XML Timed MetaData and AVC-Intra
-    video
+    -   Encoders:
 
--   rfbsrc now supports the uri handler interface, so applications can
-    use RFB/VNC sources in uridecodebin(3) and playbin, with
-    e.g. rfb://:password@10.1.2.3:5903?shared=1
+        -   msdkh264enc, msdkh265enc**: “pic-timing-sei” property to insert pic timing SEI
+        -   msdkh264enc, msdkh265enc**: Add properties to allow different max/min-qp values for I/P/B frames
+        -   msdkh264enc: Added BGRx format DMABuf support
+        -   Advertise special image formats in low power mode
 
--   rtponviftimestamp: Add support for using reference timestamps
+-   mxfdemux gained support for FFV1 demuxing
 
--   rtpvp9depay now has the same keyframe-related properties as
-    rtpvp8depay and rtph264depay: “request-keyframe” and
-    “wait-for-keyframe”
+-   opusenc, opusdec now support decoding and encoding more than 8 channels, and can also handle unknown/unpositioned channel
+    layouts.
 
--   rtspsrc: Various RTSP servers are using invalid URL operations for
-    constructing the control URL. Until GStreamer 1.16 these worked
-    correctly because GStreamer was just appending strings itself to
-    construct the control URL, but starting version 1.18 the correct URL
-    operations were used. With GStreamer 1.22, rtspsrc now first tries
-    with the correct control URL and if that fails it will retry with
-    the wrongly constructed control URL to restore support for such
-    servers.
+-   The oss plugin gained a device provider for audio device discovery
 
--   rtspsrc and rtpjitterbuffer gained a new
-    “add-reference-timestamp-meta” property that makes them put the
-    unmodified original sender timestamp on output buffers for NTP or
-    PTP clock synced senders
+-   pcapparse learned how to handle the Linux “cooked” capture encapsulation v2
 
--   srtsrc, srtsink: new “auto-reconnect” property to make it possible
-    to disable automatic reconnects (in caller mode) and make the
-    elements post an error immediately instead; also stats improvements
+-   Intel Quick Sync plugin improvements:
 
--   srtsrc: new “keep-listening” property to avoid EOS on disconnect and
-    keep the source running while it waits for a new connection.
+    -   qsvh264enc gained more encoding options
+    -   qsvh265dec now supports GBR decoding and HEVC RExt profiles
 
--   videocodectestsink: added YUV 4:2:2 support
+-   qtdemux now adds audio clipping meta when playing gapless m4a content, supports CENC sample grouping, as well as the SpeedHQ
+    video codec.
 
--   wasapi2src: Add support for process loopback capture
+-   ristsrc gained support for dynamic payloads via the new “caps” and “encoding-name” properties. These can be used to make the
+    ristsrc receive other payload types than MPEG-TS.
 
--   wpesrc: Add support for modifiers in key/touch/pointer events
+-   rtmp2src: a new “no-eof-is-error” property was added: There is currently no way for applications to know if the stream has
+    been properly terminated by the server or if the network connection was disconnected, as an EOS is sent in both cases. With
+    the property set, connection errors will be reported as errors, allowing applications to distinguish between both scenarios.
 
-Plugin and library moves
+-   rtspsrc: new “extra-http-request-headers” property for adding custom http request headers when using http tunnelling.
 
--   The xingmux plugin has been moved from gst-plugins-ugly into
-    gst-plugins-good.
+-   sdpdemux now supports SDP source filters as per RFC 4570; audio-only or video-only streaming can be selected via the new
+    “media” property, and RTCP feedback can be disabled via the “rtcp-mode” property.
 
--   The various Windows directshow plugins in gst-plugins-bad have been
-    unified into a single directshow plugin.
+-   splitmuxsrc uses natural ordering to sort globbed filenames now, i.e. 0, 1, 2, 10, 11 instead of 0, 1, 10, 11, 2, …
 
-Plugin removals
+-   srt: Add more fields to the statistics to see how many packets were retransmitted and how many were dropped.
 
--   The dxgiscreencapsrc element has been removed, use
-    d3d11screencapturesrc instead
+-   switchbin: many improvements, especially for caps handling and passthrough.
 
-Miscellaneous API additions
+-   taginject: a “scope” property was added to allow injection of global tags in addition to the current default which is stream
+    tags.
 
--   GST_AUDIO_FORMAT_INFO_IS_VALID_RAW() and
-    GST_VIDEO_FORMAT_INFO_IS_VALID_RAW() can be used to check if a
-    GstAudioFormatInfo or GstVideoFormatInfo has been initialised to a
-    valid raw format.
+-   timeoverlay: add buffer-count and buffer-offset time modes.
 
--   Video SEI meta: new GstVideoSEIUserDataUnregisteredMeta to carry
-    H.264 and H.265 metadata from SEI User Data Unregistered messages.
+-   udpsrc: new “multicast-source” property to support IGMPv3 Source Specific Muliticast (SSM) as per RFC 4604.
 
--   vulkan: Expose gst_vulkan_result_to_string()
+-   videoconvertscale, videoconvert: add a “converter-config” property to allow fine-tuning conversion parameters that are not
+    exposed directly as property.
 
-Miscellaneous performance, latency and memory optimisations
+-   videoflip: many orientation tag handling fixes and improvements
 
--   liborc 0.4.33 adds support for aarch64 (64-bit ARM) architecture
-    (not enabled by default on Windows yet though) and improvements for
-    32-bit ARM and should greatly enhance performance for certain
-    operations that use ORC.
+-   videorate: add “drop-out-of-segment” property to force dropping of out-of-segment buffers.
 
--   as always there have been plenty of performance, latency and memory
-    optimisations all over the place.
+-   volume now supports arbitrarily-large positive gains via a new “volume-full-range” property (it was not possibly to just
+    allow a bigger maximum value for the existing “volume” property for GstController-related backwards-compatibility reasons).
 
-Miscellaneous other changes and enhancements
+-   waylandsink, gtkwaylandsink: improved frame scheduling reducing frame drops and improve throughput.
 
--   the audio/video decoder base classes will not consider decoding
-    errors a hard error by default anymore but will continue trying to
-    decode. Previously more than 10 consecutive errors were considered a
-    hard error but this caused various partially broken streams to fail.
-    The threshold is configurable via the “max-errors” property.
+-   webpenc now has support for animated WebP which can be enabled via the new “animated” property. By default it will just
+    output a stand-alone WebP image for each input buffer, just like before.
 
--   compatibility of the GStreamer PTP clock implementation with
-    different PTP server implementations was improved, and
-    synchronization is achieved successfully in various scenarios that
-    failed before.
+-   wpe: added a WebProcess crash handler; gained WPEWebKit 2.0 API support.
 
-Tracing framework and debugging improvements
+-   x264enc gained support for 8-bit monochrome video (GRAY8).
 
-New tracers
+-   ximagesrc gained navigation support (mouse and keyboard events).
+
+-   y4mdec now parses extended headers to support high bit depth video.
+
+## Plugin and library moves
+
+-   The AMR-NB and AMR-WB plugins have been moved from -ugly to -good.
+
+## Plugin and element removals
+
+-   The entire gst-omx package and plugin has been retired. See the OMX section below for more details.
+
+-   The RealServer RTSP extension, RDT handling and PNM source have been removed from the realmedia plugin.
+
+-   The kate subtitle plugin has been removed.
+
+## Miscellaneous API additions
+
+GStreamer Core
+
+-   gst_pipeline_get_configured_latency() and gst_pipeline_is_live() convenience functions to query liveness and configured
+    latency of a pipeline.
+
+-   Plugins can now provide status info messages for plugins that will be displayed in gst-inspect-1.0 and is useful for dynamic
+    plugins that register features at runtime. They are now able to provide information to the user why features might not be
+    available. This is now used in the amfcodec, nvcodec, qsv, and va plugins.
+
+-   GST_OBJECT_AUTO_LOCK() and GST_PAD_STREAM_AUTO_LOCK() are g_autoptr(GMutexLocker)-based helpers for GstPad and GstObject
+    that unlock the mutex automatically when the helper goes out of scope. This is not portable so should not be used in
+    GStreamer code that needs to be portable to e.g. Windows with MSVC.
+
+-   gst_clear_context(), gst_clear_promise(), gst_clear_sample()
+
+-   gst_util_ceil_log2() and gst_util_simplify_fraction() utility functions
+
+-   New TAG_CONTAINER_SPECIFIC_TRACK_ID tag for container specific track ID as used in an HTML5 context, plus basic support in
+    matroskademux, qtdemux, dashdemux and dashdemux2
+
+-   New utility functions to create a stream-id without a pad for elements:
+
+    -   gst_element_decorate_stream_id()
+    -   gst_element_decorate_stream_id_printf_valist()
+    -   gst_element_decorate_stream_id_printf()
+
+-   GstQueueArray gained API for sorting and sorted insertion
+
+-   Add strict GstStructure serialisation with gst_structure_serialize_full() in combination with GST_SERIALIZE_FLAG_STRICT
+    which only succeeds if the result can later be fully deserialised again.
+
+-   GstBaseSrc enhancements: the “automatic-eos” property can be used to do the equivalent to gst_base_src_set_automatic_eos().
+    gst_base_src_push_segment() sends a segment event right away which can be useful for subclasses like appsrc which have their
+    own internal queuing.
 
--   buffer-lateness: Records lateness of buffers and the reported
-    latency for each pad in a CSV file. Comes with a script for
-    visualisation.
+-   GstBaseSink gained a new custom GST_BASE_SINK_FLOW_DROPPED flow return which can be used by subclasses from the virtual
+    ::render method to signal to the base class that a frame is not being rendered. This is used in e.g. waylandsink and ensures
+    that elements such as fpsdisplaysink will correctly report the rate of frames rendered and dropped.
 
--   pipeline-snapshot: Creates a .dot file of all pipelines in the
-    application whenever requested via SIGUSR1 (on UNIX systems)
+GstDiscoverer
 
--   queue-levels: Records queue levels for each queue in a CSV file.
-    Comes with a script for visualisation.
+-   New “load-serialized-info” signal to retrieve a serialized GstDiscovererInfo
+
+GstSDP
+
+-   Add gst_sdp_message_remove_media()
+
+Video Library
+
+DRM Modifier Support for dmabufs on Linux
+
+See section above.
+
+List of Video Formats for Passthrough
+
+New helper API was added to get a list of all supported video formats, including DMA_DRM formats, and can be used to advertise
+all supported formats for passthrough purposes:
+
+-   GST_VIDEO_FORMATS_ANY_STR, GST_VIDEO_FORMATS_ANY
+-   gst_video_formats_any() which can be used by bindings or for code that prefers GstVideoFormat values instead of strings.
+
+New Video Formats
+
+-   12-bit and 16-bit A420 / A422 / A444 (YUV with alpha channel) variants:
+
+    -   A444_12BE, A444_12LE
+    -   A422_12BE, A422_12LE
+    -   A420_12BE, A420_12LE
+    -   A444_16BE, A444_16LE
+    -   A422_16BE, A422_16LE
+    -   A420_16BE, A420_16LE
+
+-   8-bit A422 / A444 (YUV with alpha channel) variant:
+
+    -   A422
+    -   A444
+
+-   Planar 16-bit 4:4:4 RGB formats:
+
+    -   GBR_16BE
+    -   GBR_16LE
+
+-   RBGA, intended to be used by hardware decoders where VUYA is only supported 4:4:4 decoding surface but the stream is encoded
+    with GBR color space, such as HEVC and VP9 GBR streams for example.
+
+-   Two tiled Mediatek 10-bit formats:
+
+    -   MT2110T
+    -   MT2110R
+
+-   Tiled 10-bit NV12 format NV12_10LE40_4L4 (Verisilicon Hantro)
+
+## Miscellaneous performance, latency and memory optimisations
+
+-   liborc 0.4.35 (latest: 0.4.38) adds support for AVX/AVX2 and contains improvements for the SSE backend.
+
+-   liborc 0.4.37 adds support for NEON on macOS on Apple ARM64 CPUs.
+
+-   Most direct use of the GLib GSLice allocator has been removed, as there is little evidence that it actually still provides
+    much advantage over the standard system allocator on Linux or Windows in 2024. There is strong evidence however that it
+    causes memory fragmentation for standard GStreamer workloads such as RTSP/RTP/WebRTC streaming.
+
+-   As always there have been plenty of performance, latency and memory optimisations all over the place.
+
+## Tracing framework and debugging improvements
+
+-   The gst-stats tool can now be passed a custom regular expression
+
+-   gst-debug-viewer from the devtools module has seen minor improvements and fixes
+
+New tracers
+
+-   None in this release.
 
 Debug logging system improvements
 
--   new log macros GST_LOG_ID, GST_DEBUG_ID, GST_INFO_ID,
-    GST_WARNING_ID, GST_ERROR_ID, and GST_TRACE_ID allow passing a
-    string identifier instead of a GObject. This makes it easier to log
-    non-gobject-based items and also has performance benefits.
+-   Nothing major in this cycle.
 
-Tools
+Fake video decoder
 
--   gst-play-1.0 gained a --no-position command line option to suppress
-    position/duration queries, which can be useful to reduce debug log
-    noise.
+-   The new fakevideodec element does not decode the input bitstream, it only reads video width, height and framerate from the
+    caps and then pushes out raw video frames of the expected size in RGB format.
 
-GStreamer FFMPEG wrapper
+-   It draws a snake moving from left to right in the middle of the frame, which is reasonably light weight and still provides
+    an idea about how smooth the rendering is.
 
--   Fixed bitrate management and timestamp inaccuracies for video
-    encoders
+## Tools
 
--   Fix synchronization issues and errors created by the (wrong)
-    forwarding of upstream segment events by ffmpeg demuxers.
+-   gst-launch-1.0 gained a new --prog-name command line option to set the program name, which will be used by GTK and GStreamer
+    to set the class or app-id.
 
--   Clipping meta support for gapless mp3 playback
+-   gst-play-1.0 now defaults to using playbin3, but can still be made to use the old playbin by passing the --use-playbin2
+    command line argument.
 
-GStreamer RTSP server
+## GStreamer FFmpeg wrapper
 
--   Add RFC5576 Source-specific media attribute to the SDP media for
-    signalling the CNAME
+-   New avvideocompare element to compare two incoming video buffers using a specified comparison method (e.g. SSIM or PSNR).
 
--   Add support for adjusting request response on pipeline errors
+-   Prefer using FFmpeg Musepack decoder/demuxer over musepackdec as they work better with decodebin3 and playbin3 which likes
+    to have parsers and decoders separate.
 
-    -   Give the application the possibility to adjust the error code
-        when responding to a request. For that purpose the pipeline’s
-        bus messages are emitted to subscribers through a
-        “handle-message” signal. The subscribers can then check those
-        messages for errors and adjust the response error code by
-        overriding the virtual method
-        GstRTSPClientClass::adjust_error_code().
+-   Added codec mappings for AV1, MxPEG, FFVHuff video
 
--   Add gst_rtsp_context_set_token() method to make it possible to set
-    the RTSPToken on some RTSPContext from bindings such as the Python
-    bindings.
+-   Added raw video format support for P010, VUYA, Y410, P012, Y212 and Y412.
 
--   rtspclientsink gained a “publish-clock-mode” property to configure
-    whether the pipeline clock should be published according to RFC7273
-    (RTP Clock Source Signalling), similar to the same API on
-    GstRTSPMedia.
+-   Newer, non-deprecated APIs are used by the plugin when built with FFmpeg 6.0 or newer.
 
-GStreamer VA-API support
+-   The FFmpeg meson subproject wrap has been updated to v6.1
 
--   Development activity has shifted towards the new va plugin, with
-    gstreamer-vaapi now basically in maintenance-only mode. Most of the
-    below refers to the va plugin (not gstreamer-vaapi).
+-   Note: see Known Issues section below for known issues with FFmpeg 6.0.0 and the latest FFmpeg 7.x release
 
--   new gst-va library for GStreamer VA-API integration
+## GStreamer RTSP server
 
--   vajpegdec: new JPEG decoder
+-   New “ensure-keyunit-on-start” property: While the suspend modes NONE and PAUSED provided a low startup latency for
+    connecting clients, it did not ensure that streams started on fresh data. With this new property it is possible to maintain
+    the low startup latency of those suspend modes while also ensuring that a stream starts on a key unit. Furthermore, by
+    setting the new “ensure-keyunit-on-start-timeout” property it is also possible to accept a key unit of a certain age, but
+    discard it if too much time has passed and instead force a new key unit.
 
--   vah264enc, vah265enc: new H.264/H.265 encoders
+-   rtspclientsink: apply “port-range” property for RTCP port selection as well
 
--   vah264lpenc, vah265lpenc: new low power mode encoders
+## GStreamer VA-API support
 
--   vah265enc: Add extended formats support such as 10/12 bits, 4:2:2
-    and 4:4:4
+GstVA
 
--   Support encoder reconfiguration
+-   vah264dec, vah265dec, vavp8dec, vavp9dec, vampeg2dec and vaav1dec were promoted to rank PRIMARY+1 on Linux
 
--   vacompositor: Add new compositor element using the VA-API VPP
-    interface
+-   Improved support for dmabuf use cases. All va elements now negotiate the new and improved dmabuf capabilities with explicit
+    modifiers. This supports both import and export of dmabufs.
 
--   vapostproc:
+-   Added vaav1enc element, available in recent Intel and AMD GPUs
 
-    -   new “scale-method” property
-    -   Process HDR caps if supported
-    -   parse video orientation from tags
+-   Added support for the experimental VA-Win32 backend. It needs at least libva 1.18
 
--   vaapipostproc: Enable the use of DMA-Buf import and export
-    (gstreamer-vaapi)
+-   Improved handling of multi-GPU systems. Still, sharing buffers among them is not advised.
 
-GStreamer Video4Linux2 support
+-   Bumped minimum libva version to 1.12
 
--   Added support for Mediatek Stateless CODEC (VP8, H.264, VP9)
+-   Enhanced support for RadeonSI Mesa driver for 10bit decoding
 
--   Stateless H.264 interlaced decoder support
+-   Register elements only for allowed drivers (Intel and Mesa, for the moment)
 
--   Stateless H.265 decoder support
+GStreamer-VAAPI
 
--   Stateful decoder support for driver resolution change events
+-   The new GstVA elements (see above) should be preferred when possible.
 
--   Stateful decoding support fixes for NXP/Amphion driver
+-   Ranks of decoders were demoted to NONE so they won’t be used automatically by playbin and similar elements anymore.
 
--   Support for hardware crop in v4l2src
+-   Clean-ups and minimal fixes.
 
--   Conformance test improvement for stateful decoders
+-   gstreamer-vaapi should be considered deprecated and may be discontinued as soon as the va plugin is fully feature
+    equivalent. Users who rely on gstreamer-vaapi are encouraged to migrate and test the va elements at the earliest
+    opportunity.
 
--   Fixes for Raspberry Pi CODEC
+## GStreamer Video4Linux2 support
 
-GStreamer OMX
+-   New uvcsink element, based on v4l2sink allow streaming your pipeline as a UVC camera using Linux UVC Gadget driver.
 
--   There were no changes in this module
+-   v4l2src now supports 10/12/14/16-bit bayer formats.
 
-GStreamer Editing Services and NLE
+-   Stateful decoders now pass too large encoded frames over multiple buffers.
 
--   Handle compositors that are bins around the actual compositor
-    implementation (like glvideomixers which wraps several elements)
+-   AV1 Stateless video decoder.
 
--   Add a mode to disable timeline editing API so the user can be in
-    full control of its layout (meaning that the user is responsible for
-    ensuring its validity/coherency)
+-   Stateless decoders now tested using Virtual driver (visl), making it possible to run the tests in the cloud based CI
 
--   Add a new fade-in transition type
+## GStreamer OMX
 
--   Add support for non-1/1 PAR source videos
+-   The gst-omx module has been removed. The OpenMAX standard is long dead and even the Raspberry Pi OS no longer supports it.
+    There has not been any development since 1.22 was released. Users of these elements should switch to the Video4Linux-based
+    video encoders and decoders which have been the standard on embedded Linux for quite some time now.
 
--   Fix frame accuracy when working with very low framerate streams
+-   Hardware vendors which still use OpenMAX are known to have non-standard forks and it is recommended that they maintain it
+    while planning their move to the Video4Linux API.
 
-GStreamer validate
+## GStreamer Editing Services and NLE
 
--   Clean up and stabilize API so we can now generate rust bindings
+-   Implement a gesvideoscale effect which gives user the ability to chooses where a clip has to be scaled in the chain of
+    effects. By default scaling is done in the compositor.
 
--   Enhance the appsrc-push action type allowing to find tune the
-    buffers more in details
+-   Add support for gessrc as sub-timeline element so third party can implement their own formatters and use their timelines as
+    sub-timelines. Before that, only timelines serialized as files on the filesystem could be loaded as sub-timelines (using
+    gesdemux).
 
--   Add an action type to verify currently configured pad caps
+-   Implement a new GESDiscovererManager singleton object making management of the discoverers used to discoverer media files
+    cleaner and allowing to expose the following APIs:
 
--   Add a way to run checks from any thread after executing a ‘wait’
-    action. This is useful when waiting on a signal and want to check
-    the value of a property right when it is emited for example.
+    -   load-serialize-info signal so GstDiscovererInfo can be serialized by users the way they like and load them without
+        requiring discovering the file when reloading a project.
+    -   source-setup signal so user can tweak source elements during discovery
 
-GStreamer Python Bindings
+-   Expose GESFrameCompositionMeta in public API so user can implement their own effects targetting GES which take into account
+    that meta.
 
--   Add a Gst.init_python() function to be called from plugins which
-    will initialise everything needed for the GStreamer Python bindings
-    but not call Gst.init() again since this will have been called
-    already.
+-   Expose audioconvert:mix-matrix property in audio sources
 
--   Add support for the GstURIHandlerInterface that allows elements to
-    advertise what URI protocols they support.
+-   Port GESPipeline rendering to use encodebin2. This allows rendering timelines directly with a muxing sink (like hlssinkX
+    etc..) and leverage all new features of that new element.
 
-GStreamer C# Bindings
+ges-launch
 
--   Fix AppSrc and AppSink constructors
+-   Fix setting keyframes
 
--   The C# bindings have yet to be updated to include new 1.22 API,
-    which requires improvements in various places in the bindings /
-    binding generator stack. See issue #1718 in GitLab for more
-    information and to track progress.
+-   Add an ignore-eos option
 
-GStreamer Rust Bindings and Rust Plugins
+-   Allow overriding container profile so that the user can build encoding profiles following the media format of a specific
+    media file, for example, but ensuring it is muxed using a specific format
 
-The GStreamer Rust bindings are released separately with a different
-release cadence that’s tied to gtk-rs, but the latest release has
-already been updated for the new GStreamer 1.22 API. Check the bindings
-release notes for details of the changes since 0.18, which was released
-around GStreamer 1.20.
+-   Ensure sink elements are inside a GstBin and never in a GstPipeline
 
-gst-plugins-rs, the module containing GStreamer plugins written in Rust,
-has also seen lots of activity with many new elements and plugins. A
-list of all Rust plugins and elements provided with the 0.9 release can
-be found in the repository.
+-   Move +effect stack effects from source to last effect added, so it feels more natural to user as adding them at the
+    beginning of the chain while the syntax is +effect felt wrong
 
--   33% of GStreamer commits are now in Rust (bindings + plugins), and
-    the Rust plugins module is also where most of the new plugins are
-    added these days.
+## GStreamer validate
 
--   The Rust plugins are now shipped as part of the Windows MSVC + macOS
-    binary packages. See below for the list of shipped plugins and the
-    status of Rust support in cerbero.
+-   In action types, add a way to avoid checking property value after setting it, in case elements do it async for example.
 
--   The Rust plugins are also part of the documentation on the GStreamer
-    website now.
+-   Add a vmethod to free GstValidateActionParameters to be more binding friendly.
 
--   Rust plugins can be used from any programming language. To the
-    outside they look just like a plugin written in C or C++.
+-   Allow scenarios to define the pipeline state target in the metadata instead of assuming PLAYING state.
 
-New Rust plugins and elements
+-   Add support to run sub-pipelines/scenarios
 
--   rtpav1pay / rtpav1depay: RTP (de)payloader for the AV1 video codec
--   gtk4paintablesink: a GTK4 video sink that provides a GdkPaintable
-    for rendering a video in any place inside a GTK UI. Supports
-    zero-copy rendering via OpenGL on Linux and macOS.
--   ndi: source, sink and device provider for NewTek NDI protocol
--   onvif: Various elements for parsing, RTP (de)payloading, overlaying
-    of ONVIF timed metadata.
--   livesync: Element for converting a live stream into a continuous
-    stream without gaps and timestamp jumps while preserving live
-    latency requirements.
--   raptorq: Encoder/decoder elements for the RaptorQ FEC mechanism that
-    can be used for RTP streams (RFC6330).
+    -   Added support to forward buffers from appsink to appsrc
 
-WebRTC elements
+-   Add a way to set pipeline base-time, start-time and force using the system clock.
 
--   webrtcsink: a WebRTC sink (batteries included WebRTC sender with
-    specific signalling)
--   whipsink: WebRTC HTTP ingest (WHIP) to MediaServer
--   whepsrc: WebRTC HTTP egress (WHEP) from MediaServer
--   rtpgccbwe: RTP bandwidth estimator based on the Google Congestion
-    Control algorithm (GCC), used by webrtcsink
+-   Add a ‘fill-mode’ to the appsrc-push action type so we can create some type of streams easily using an appsrc, giving
+    control when writing scenarios without requiring files with the content.
 
-Amazon AWS services
+-   Add a “select-streams” action type to test “stream aware” elements.
 
--   awss3src / awss3sink: A source and sink element to talk to the
-    Amazon S3 object storage system.
--   awss3hlssink: A sink element to store HLS streams on Amazon S3.
--   awstranscriber: an element wrapping the AWS Transcriber service.
--   awstranscribeparse: an element parsing the packets of the AWS
-    Transcriber service.
+-   Add a way to wait for a property to reach a specified value before executing an action. For example it is possible to wait
+    for a pad to get some specific caps set before executing an action.
 
-Video Effects (videofx)
+-   validate: Add support to replace variables in deeply nested structures in particular for more complex action types where
+    some of the properties are inside structures.
 
--   roundedcorners: Element to make the corners of a video rounded via
-    the alpha channel.
--   colordetect: A pass-through filter able to detect the dominant
-    color(s) on incoming frames, using color-thief.
--   videocompare: Compare similarity of video frames. The element can
-    use different hashing algorithms like Blockhash, DSSIM, and others.
+-   Fixed compatibility with Python 3.12.
 
-New MP4 muxer + Fragmented MP4 muxer
+## GStreamer Python Bindings
 
--   fmp4mux: New fragmented MP4/ISOBMFF/CMAF muxer for generating
-    e.g. DASH/HLS media fragments.
--   isomp4mux: New non-fragmented, normal MP4 muxer.
+gst-python is an extension of the regular GStreamer Python bindings based on gobject-introspection information and PyGObject,
+and provides “syntactic sugar” in form of overrides for various GStreamer APIs that makes them easier to use in Python and more
+pythonic; as well as support for APIs that aren’t available through the regular gobject-introspection based bindings, such as
+e.g. GStreamer’s fundamental GLib types such as Gst.Fraction, Gst.IntRange etc.
 
-Both plugins provides elements that replace the existing qtmux/mp4mux
-element from gst-plugins-good. While not feature-equivalent yet, the new
-codebase and using separate elements for the fragment and non-fragmented
-case allows for easier extensability in the future.
+-   Added a GstTagList override that makes a tag list act like a dict
 
-Cerbero Rust support
+-   Fix build and usage in Windows
 
--   Starting this release, cerbero has support for building and shipping
-    Rust code on Linux, Windows (MSVC) and macOS. The Windows (MSVC) and
-    macOS binaries also ship the GStreamer Rust plugins in this release.
-    Only dynamic plugins are built and shipped currently.
+-   Various fixes for Python >= 3.12
 
--   Preliminary support for Android, iOS and Windows (MinGW) exists but
-    more work is needed. Check the tracker issue for more details about
-    future work.
+-   Rework libpython loading to be relocatable
 
--   The following plugins are included currently: audiofx, aws, cdg,
-    claxon, closedcaption, dav1d, fallbackswitch, ffv1, fmp4, gif,
-    hlssink3, hsv, json, livesync, lewton, mp4, ndi, onvif, rav1e,
-    regex, reqwest, raptorq, png, rtp, textahead, textwrap, threadshare,
-    togglerecord, tracers, uriplaylistbin, videofx, webrtc, webrtchttp.
+-   Fix libpython dlopen on macOS
 
-Build and Dependencies
+## GStreamer C# Bindings
 
--   meson 0.62 or newer is required
+-   The GStreamer C# bindings have been updated to a more recent version of GtkSharp and the bindings have been regenerated with
+    that version.
 
--   GLib >= 2.62 is now required (but GLib >= 2.64 is strongly
-    recommended)
+-   GStreamer API added in recent GStreamer releases is now available
 
--   libnice >= 0.1.21 is now required and contains important fixes for
-    GStreamer’s WebRTC stack.
+-   GstRtspServer bindings have been added, plus an RTSP server example
 
--   liborc >= 0.4.33 is recommended for 64-bit ARM support and 32-bit
-    ARM improvements
+## GStreamer Rust Bindings and Rust Plugins
 
--   onnx: OnnxRT >= 1.13.1 is now required
+The GStreamer Rust bindings and plugins are released separately with a different release cadence that’s tied to the twice-a-year
+GNOME release cycle.
 
--   openaptx: can now be built against libfreeaptx
+The latest release of the bindings (0.22) has already been updated for the new GStreamer 1.24 APIs, and works with any GStreamer
+version starting at 1.14.
 
--   opencv: allow building against any 4.x version
+gst-plugins-rs, the module containing GStreamer plugins written in Rust, has also seen lots of activity with many new elements
+and plugins. The GStreamer 1.24 binaries track the 0.12 release series of gst-plugins-rs, and fixes from newer versions will be
+backported as needed to the 0.12 brach for future 1.24.x bugfix releases.
 
--   shout: libshout >= 2.4.3 is now required
+Rust plugins can be used from any programming language. To applications they look just like a plugin written in C or C++.
 
--   gstreamer-vaapi’s Meson build options have been switched from a
-    custom combo type (yes/no/auto) to the built-in Meson feature type
-    (enabled/disabled/auto)
+### WebRTC
 
--   The GStreamer Rust plugins module gst-plugins-rs is now considered
-    an essential part of the GStreamer plugin offering and packagers and
-    distributors are strongly encouraged to package and ship those
-    plugins alongside the existing plugin modules.
+-   New element webrtcsrc that can act as a recvonly WebRTC client. Just like the opposite direction, webrtcsink, this can
+    support various different WebRTC signalling protocols. Some are included with the plugin and provide their own element
+    factory for easier usage but it is also possible for applications to provide new signalling protocol implementations.
 
--   we now make use of Meson’s install tags feature which allows
-    selective installation of installl components and might be useful
-    for packagers.
+-   webrtcsink now exposes the signaller as property and allows implementing a custom signaller by connecting signal handlers to
+    the default signaller.
 
-Monorepo build (gst-build)
+-   A new signaller and webrtcsink implementation for Janus’ VideoRoom implementation. The corresponding webrtcsrc signaller
+    implementation is currently in a merge request in GitLab.
 
--   new “orc-source” build option to allow build against a
-    system-installed liborc instead of forcing the use of orc as a
-    subproject.
+-   New whepsrc element that can receive WHEP WebRTC streams. This is currently not based on webrtcsrc but in the future a new
+    element around webrtcsrc will be added.
 
--   GStreamer command line tools can now be linked to the gstreamer-full
-    library if it’s built
+-   New whipserversrc element around webrtcsrc for ingesting WHIP WebRTC streams in GStreamer.
 
-Cerbero
+-   New whipclientsink element around webrtcsink for publishing WHIP WebRTC streams from GStreamer. This deprecates the old
+    whipsink element.
 
-Cerbero is a meta build system used to build GStreamer plus dependencies
-on platforms where dependencies are not readily available, such as
-Windows, Android, iOS, and macOS.
+-   A new signaller and webrtcsink implementation for LiveKit. The corresponding webrtcsrc signaller implementation was merged
+    into the git repository recently.
 
-General improvements
+-   A new signaller and webrtcsink implementation for AWS Kinesis Video Streams
 
--   Rust support was added for all support configurations, controlled by
-    the rust variant; see above for more details
--   All pkgconfig files are now reliably relocatable without requiring
-    pkg-config --define-prefix. This also fixes statically linking with
-    GStreamer plugins using the corresponding pkgconfig files.
--   New documentation on how to build a custom GStreamer repository
-    using Cerbero, please see the README
--   HTTPS certificate checking is enabled for downloads on all platforms
-    now
--   Fetching now automatically retries on error for robustness against
-    transient errors
--   Support for building the new Qt6 plugin was added
--   pkgconfig files for various recipes were fixed
--   Several recipes were updated to newer versions
--   New plugins: adaptivedemux2 aes codectimestamper dav1d
--   New libraries: cuda webrtcnice
-
-macOS / iOS
-
--   Added support for running Cerbero on ARM64 macOS
--   GStreamer.framework and all libraries in it are now relocatable,
-    which means they use LC_RPATH entries to find dependencies instead
-    of using an absolute path. If you link to GStreamer using the
-    pkgconfig files, no action is necessary. However, if you use the
-    framework directly or link to the libraries inside the framework by
-    hand, then you need to pass -Wl,-rpath,<path_to_libdir> to the
-    linker.
--   Apple bitcode support was dropped, since Apple has deprecated it
--   macOS installer now correctly advertises support for both x86_64 and
-    arm64
--   macOS framework now ships the gst-rtsp-server-1.0 library
--   Various fixes were made to make static linking to gstreamer
-    libraries and plugins work correctly on macOS
--   When statically linking to the applemedia plugin using Xcode 13, you
-    will need to pass -fno-objc-msgsend-selector-stubs which works
-    around a backwards-incompatible change in Xcode 14. This is not
-    required for the rest of GStreamer at present, but will be in the
-    future.
--   macOS installer now shows the GStreamer logo correctly
+-   webrtcsink has a new payloader-setup signal to allow the application more fine grained control over the RTP payloader
+    configuration, similar to the already existing encoder-setup signal for encoders.
 
-Windows
+-   webrtcsrc gained support for a custom navigation event protocol over the data channel, which is compatible with the
+    navigation event protocol supported by webrtcsink.
 
--   MSVC is now required by default on Windows, and the Visual Studio
-    variant is enabled by default
-    -   To build with MinGW, use the mingw variant
--   Visual Studio props files were updated for newer Visual Studio
-    versions
--   Visual Studio 2015 support was dropped
--   MSYS2 is now supported as the base instead of MSYS. Please see the
-    README for more details. Some advantages include:
-    -   Faster build times, since parallel make works
-    -   Faster bootstrap, since some tools are provided by MSYS2
-    -   Other speed-ups due to using MSYS2 tools instead of MSYS
--   Faster download by using powershell instead of hand-rolled Python
-    code
--   Many recipes were ported from Autotools to Meson, speeding up the
-    build
--   Universal Windows Platform is no longer supported, and binaries are
-    no longer shipped for it
--   New documentation on how to force a specific Visual Studio
-    installation in Cerbero, please see the README
--   New plugins: qsv wavpack directshow amfcodec wic win32ipc
--   New libraries: d3d11
-
-Windows MSI installer
-
--   Universal Windows Platform prebuilt binaries are no longer available
+-   webrtcsink supports encoded streams as input. Using encoded streams will disable webrtcsinks congestion control changing any
+    encoded stream parameters.
 
-Linux
+-   webrtcsink and webrtcsrc have a new signal ‘request-encoded-filter’ to allow transformations of the encoded stream. This can
+    be used, for example, for the same use-cases as the WebRTC Insertable Streams API.
 
--   Various fixes for RHEL/CentOS 7 support
--   Added support for running on Linux ARM64
+-   gstwebrtc-api: JavaScript API for interacting with the default signalling protocol used by webrtcsink / webrtcsrc.
 
-Android
+… and various other smaller improvements!
 
--   Android support now requires Android API version 21 (Lollipop)
--   Support for Android Gradle plugin 7.2
+### RTSP
 
-Platform-specific changes and improvements
+-   New rtspsrc2 element. Only a subset of RTSP features are implemented so far:
+    -   RTSP 1.0 support
+    -   TCP, UDP, UDP-Multicast lower transports
+    -   RTCP SR, RTCP RR, RTCP-based A/V sync
+        -   Tested for correctness in multicast cases too
+    -   Lower transport selection and order (NEW!)
+        -   The existing rtspsrc has a hard-coded order list for lower transports
+    -   Many advanced features are not implemented yet, such as non-live support. See the README for the current status.
 
-Android
+### GTK4
 
--   Android SDK 21 is required now as minimum SDK version
+-   Support for rendering GL textures on X11/EGL, X11/GLX, Wayland, macOS, and WGL/EGL on Windows.
 
--   androidmedia: Add H.265 / HEVC video encoder mapping
+-   Create a window for testing purposes when running in gst-launch-1.0 or if GST_GTK4_WINDOW=1 is set.
 
--   Implement JNI_OnLoad() to register static plugins etc. automatically
-    in case GStreamer is loaded from Java using System.loadLibrary(),
-    which is also useful for the gst-full deployment scenario.
+-   New background-color property for setting the color of the background of the frame and the borders, if any. This also allows
+    setting a fully transparent background.
 
-Apple macOS and iOS
+-   New scale-filter property for defining how to scale the frames.
 
--   The GLib version shipped with the GStreamer binaries does not
-    initialize an NSApp and does not run a NSRunLoop on the main thread
-    anymore. This was a custom GLib patch and caused it to behave
-    different from the GLib shipped by Homebrew or anybody else.
+-   Add Python example application to the repository.
 
-    The change was originally introduced because various macOS APIs
-    require a NSRunLoop to run on the main thread to function correctly
-    but as this change will never get merged into GLib and it was
-    reverted for 1.22. Applications that relied on this behaviour should
-    move to the new gst_macos_main() function, which also does not
-    require the usage of a GMainLoop.
+-   Various bugfixes, including support for the new GTK 4.14 GL renderer. The plugin needs to be built with at least the
+    gtk_v4_10 feature to work with the new GTK 4.14 GL renderer, and will work best if built with the gtk_v4_14 feature.
 
-    See e.g. gst-play.c for an example for the usage of
-    gst_macos_main().
+### Closed Caption
 
--   GStreamer.framework and all libraries in it are now relocatable,
-    which means they use LC_RPATH entries to find dependencies instead
-    of using an absolute path. If you link to GStreamer using the
-    pkgconfig files, no action is necessary. However, if you use the
-    framework directly or link to the libraries inside the framework by
-    hand, then you need to pass -Wl,-rpath,<path_to_libdir> to the
-    linker.
+-   Add cea608tocea708 element for upconverting CEA-608 captions to their CEA-708 representation.
 
--   avfvideosrc: Allow specifying crop coordinates during screen capture
+-   Add support for translations within transcriberbin.
 
--   vtenc, vtdec: H.265 / HEVC video encoding + decoding support
+-   awstranscriber supports translating the transcribed text into different languages, including multiple languages at the same
+    time.
 
--   osxaudiosrc: Support a device as both input and output
+-   awstranscriber is using the new HTTP/2-based API now instead of the WebSocket-based one.
 
-    -   osxaudiodeviceprovider now probes devices more than once to
-        determine if the device can function as both an input AND and
-        output device. Previously, if the device provider detected that
-        a device had any output capabilities, it was treated solely as
-        an Audio/Sink. This caused issues for devices that have both
-        input and output capabilities (for example, USB interfaces for
-        professional audio have both input and output channels). Such
-        devicesare now listed as both an Audio/Sink as well as an
-        Audio/Source.
+### Other new elements
 
--   osxaudio: support hidden devices on macOS
+-   New awss3putobjectsink that works similar to awss3sink but with a different upload strategy.
 
-    -   These are devices that will not be shown in the macOS UIs and
-        that cannot be retrieved without having the specific UID of the
-        hidden device. There are cases when you might want to have a
-        hidden device, for example when having a virtual speaker that
-        forwards the data to a virtual hidden input device from which
-        you can then grab the audio. The blackhole project supports
-        these hidden devices and this change provides a way that if the
-        device id is a hidden device it will use it instead of checkinf
-        the hardware list of devices to understand if the device is
-        valid.
+-   New hlscmafsink element for writing HLS streams with CMAF/ISOBMFF fragments.
 
-Windows
+-   New inter plugin with new intersink and intersrc elements that allow to 1:N connect different pipelines in the same process.
+    This is implemented around the appsrc / appsink-based StreamProducer API that is provided as part of the GStreamer Rust
+    bindings, and is also used inside webrtcsrc and webrtcsink.
+
+-   New livesync element that allows maintaining a contiguous live stream without gaps from a potentially unstable source.
+
+-   New isomp4mux non-fragmented MP4 muxer element.
+
+### Other improvements
+
+-   audiornnoise
+    -   Attach audio level meta to output buffers.
+    -   Add voice detection threshold property
+-   fmp4mux
+    -   Add support for CMAF-style chunking, e.g. low-latency / LL HLS and DASH
+    -   Add support for muxing Opus, VP8, VP9 and AV1 streams
+    -   Add ‘offset-to-zero’ property and make media/track timescales configurable
+-   hlssink3
+    -   Allow adding EXT-X-PROGRAM-DATE-TIME tag to the manifest.
+    -   Allow generating I-frame-only playlist
+-   ndi
+    -   Closed Caption support in ndisrc / ndisink
+    -   Zero-copy output support in ndisrc for raw video and audio
+-   spotifyaudiosrc: Support configurable bitrate
+
+For a full list of changes in the Rust plugins see the gst-plugins-rs ChangeLog between versions 0.9 (shipped with GStreamer
+1.22) and 0.12 (shipped with GStreamer 1.24).
+
+## Cerbero Rust support
+
+-   As of GStreamer 1.24, the GStreamer Rust plugins are shipped as part of our binary packages on all major platforms. This
+    includes Android and iOS now in addition to macOS and Windows/MSVC.
+
+## Build and Dependencies
+
+-   Meson >= 1.1 is now required for all modules
+
+-   The GLib requirement has been bumped to >= 2.64
+
+-   liborc >= 0.4.38 is strongly recommended
+
+-   libnice >= 0.1.22 is strongly recommended, as it is required for WebRTC ICE consent freshness (RFC 7675).
+
+-   gst-libav was updated for FFmpeg API deprecations and removals
+
+-   libwebpmux is required for the animated WebP support
+
+-   The wpe plugin gained support for the WPEWebKit 2.0 API version
+
+-   Bumped minimum libva version to 1.12 for the va plugin.
+
+-   zxing: added support for the zxing-c++ 2.0 API
+
+-   The ptp-helper for Precision Time Protocol (PTP) support in GStreamer core has been rewritten in Rust, and the minimum
+    required Rust version for building this is 1.48, i.e. the version currently in Debian oldstable. On Windows, at least Rust
+    1.54 is needed. There is a new ptp-helper Meson feature option that can be used to make sure everything needed for PTP
+    support is available (if set to ptp-helper=enabled). cargo is not required for building.
+
+-   gst-plugins-rs requires Rust 1.70 or newer.
+
+-   Link to libsoup at build time in all cases on non-Linux, and only load it dynamically on Linux systems where we might need
+    to support a mix of applications with dependencies that might be using libsoup2 or libsoup3. A “soup-version” meson build
+    option was added to prefer a specific version. Distros should make sure that libsoup is still a package dependency, since
+    it’s still required at runtime for the soup and adaptivedemux2 plugins to function.
+
+-   libjack is now dynamically loaded at runtime by the JACK audio plugin, and no longer a hard build dependency. However, it
+    still is a runtime dependency, so distros should make sure it remains a package dependency.
+
+Monorepo build (née gst-build)
+
+-   There is now a top-level meson build option to enable/require webrtc
+
+-   The FFmpeg subproject wrap was udpated to 6.1
+
+-   A libvpx wrap was added (for VP8/VP9 software encoding/decoding)
+
+gstreamer-full
+
+-   Add full static build support, including on Windows: Allow a project to use gstreamer-full as a static library and link to
+    create a binary without dependencies. Introduce the meson build option gst-full-target-type to select the build type:
+    dynamic (default) or static.
+
+-   Registers all full features in a plugin now to offer the possibility to get information at the plugin level and get it from
+    the registry. All the full features are now registered in a fullstaticfeatures meta plugin instead of having a NULL plugin.
 
--   win32ipcvideosink, win32ipcvideosrc: new shared memory videosrc/sink
-    elements
+Development environment
 
--   wasapi2: Add support for process loopback capture for a specified
-    PID (requires Windows 11/Windows Server 2022)
+-   add VSCode IDE integration
 
--   The Windows universal UWP build is currently non-functional and will
-    need updating after the recent GLib upgrade. It is unclear if anyone
-    is using these binaries, so if you are please make yourself known.
+-   gst-env.py: Output a setting for the prompt with --only-environment
 
--   wicjpegdec, wicpngdec: Windows Imaging Component (WIC) based JPEG
-    and PNG decoder elements.
+### Cerbero
 
--   mfaacdec, mfmp3dec: Windows MediaFoundation AAC and MP3 decoders
+Cerbero is a meta build system used to build GStreamer plus dependencies on platforms where dependencies are not readily
+available, such as Windows, Android, iOS, and macOS.
 
--   The uninstalled development environment supports PowerShell 7 now
+General improvements
+
+-   New plugins have been added
+    -   codecalpha dvbsubenc rtponvif switchbin videosignal isac ivfparse inter rtspsrc2
+-   JACK plugin is now available all platforms (previously only Linux), and will be loaded if the JACK library is found at
+    plugin load time
+-   Several recipes were ported to meson, leading to faster builds and better MSVC support
+    -   ffmpeg, gperf, lame, libvpx, ogg, opencore-amr, sbc, speex, tiff, webrtc-audio-processing
+    -   For more information, please see the tracker issue
+-   Some recipes are now outdated or unnecessary and have been removed:
+    -   intltool, libkate
+-   Various recipe updated to their latest versions
+-   Rust toolchain updated to 1.76.0 (latest as of writing)
+-   Rust plugins are now stripped and debug info split out correctly, reducing their size
+-   Fix several spurious build issues, especially with the Rust toolchain
+-   CMake is picked up from the system if available
+-   Cerbero will no longer OOM or consume excessive resources on low-end systems
+-   Python recipes have been moved from setuptools to virtualenv
+-   Fixed support for Python 3.12+
+
+macOS
+
+-   Minimum macOS version has been increased to 10.13 (High Sierra)
+    -   Released 5½ years ago, >95% marketshare
+-   Fix macOS app store validation issue caused by absolute RPATHs
+-   Rosetta is automatically installed if required for universal builds on Apple Silicon
+-   The official macOS binaries now also include static libs for the GStreamer Rust plugins
+
+iOS
+
+-   Minimum iOS version has been increased to 12.0
+-   The iOS binary packages now include the GStreamer Rust plugins
+-   tremor and ivorbisdec plugins are no longer shipped on iOS
+
+Windows
+
+-   New features shipped with the official binaries:
+    -   plugins: dwrite d3d12 (MSVC) soundtouch (MSVC) taglib (MSVC) webrtcdsp (MSVC)
+    -   plugin features: d3d11-wgc (Windows Graphics Capture Support)
+    -   libraries: gstdxva-1.0
+-   New qml6 plugin can be built on Windows with the qt6 variant enabled
+    -   Similar to qt5, this plugin cannot be included in the official binaries
+-   GLib process handling helpers for Windows are now shipped
+-   Windows 11 SDK is now required for builds
+    -   Visual Studio 2019 and newer ship this SDK
+-   MSYS is now deprecated for building Windows binaries, will be removed in the next release
+    -   MSYS2 is required, and the bootstrap script tools/bootstrap-windows.ps can install it for you
+-   Windows bootstrap script tools/bootstrap-windows.ps1 is much more interactive and user-friendly now
+-   Fixed Pango crash on 32-bit Windows
+-   WiX packaging now works with cross-windows builds from linux
 
 Linux
 
--   Improved design for DMA buffer sharing and modifier handling for
-    hardware-accelerated video decoders/encoders/filters and
-    capture/rendering on Linux and Linux-like system.
+-   Linux packages will now also include static libs for the GStreamer Rust plugins
+-   Add Python support for multiarch distributions
+-   Build fixes for various recipes on multiarch distributions
+-   Use arch-specific libdir correctly on multiarch distributions
+-   gst-omx was removed from gstreamer, and hence is no longer shipped
+-   Fixed Gentoo support
+-   Added support for RHEL 9
+-   Added support for Rocky Linux
+-   Added support for Manjaro Linux
+
+Android
+
+-   Android NDK has been updated to r25c
+    -   Only the Clang toolchain is used from the NDK now (both target and host)
+    -   gnustl has been completely removed
+-   The Android binary packages now include the GStreamer Rust plugins
+-   tremor and ivorbisdec plugins are no longer shipped on Android
+-   openh264 plugin no longer enables ASM optimizations on Android x86 due to relocation errors
+
+## Platform-specific changes and improvements
+
+### Android
+
+-   Add NDK implementation of Android MediaCodec. This reduces the amount of Java <-> native calls, which should reduce
+    overhead.
+
+-   Add support for AV1 to the androidmedia video encoder and decoder.
+
+### Apple macOS and iOS
+
+-   osxaudio: audio clock improvements (interpolate based on system time)
+
+-   Set activation policy in gst_macos_main() and in osxvideosink and glimagesink. Setting the policy to
+    NSApplicationActivationPolicyAccessory by default makes sure that we can activate windows programmatically or by clicking on
+    them. Without that, windows would disappear if you clicked outside them and there would be no way to bring them to front
+    again. This change also allows osxvideosink to receive navigation events correctly.
+
+### Windows
 
--   kmssink
+-   New DirectWrite text rendering plugin with dwriteclockoverlay, dwritetimeoverlay, dwritetextoverlay, dwritesubtitlemux, and
+    dwritesubtitleoverlay elements, including closed caption overlay support in dwritetextoverlay.
 
-    -   new “fd” property which allows an application to provide their
-        own opened DRM device fd handle to kmssink. That way an
-        application can lease multiple fd’s from a DRM master to display
-        on different CRTC outputs at the same time with multiple kmssink
-        instances, for example.
-    -   new “skip-vsync” property to achieve full framerate with legacy
-        emulation in drivers.
-    -   HDR10 infoframe support
+-   PTP clock support is now also available on Windows
 
--   va plugin and gstreamer-vaapi improvements (see above)
+-   qml6d3d11sink is a new Direct3D11 Qt6 QML sink for Windows as an alternative to the existing qml6glsink.
 
--   waylandsink: Add “rotate-method” property and “render-rectangle”
-    property
+-   wasapi2 audio plugin:
 
--   new gtkwaylandsink element based on gtksink, but similar to
-    waylandsink and uses Wayland APIs directly instead of rendering with
-    Gtk/Cairo primitives. This approach is only compatible with Gtk3,
-    and like gtksink this element only supports Gtk3.
+    -   Added an option to monitor a loopback device’s mute state
+    -   Allows process loopback capture on Windows 10
 
-Documentation improvements
+-   win32ipc supports zero-copy rendering now through a shared bufferpool.
 
--   The GStreamer Rust plugins are now included and documented in the
-    plugin documentation.
+-   Add a Windows-specific plugin loader implementation (gst-plugin-scanner), so plugin loading during registry updates happens
+    in an external process on Windows as well.
 
-Possibly Breaking Changes
+-   gst_video_convert_sample() which is often used for thumbnailing gained a D3D11 specific conversion path.
 
--   the Opus audio RTP payloader and depayloader no longer accept the
-    lower case encoding-format=multiopus but instead produce and accept
-    only the upper case variant encoding-format=MULTIOPUS, since those
-    should always be upper case in GStreamer (caps fields are always
-    case sensitive). This should hopefully only affect applications
-    where RTP caps are set manually and multi-channel audio (>= 3
-    channels) is used.
+-   d3d11 plugin:
 
--   wpesrc: the URI handler protocols changed from wpe:// and web:// to
-    web+http://, web+https://, and web+file:// which means URIs are RFC
-    3986 compliant and the source can simply strip the prefix from the
-    protocol.
+    -   d3d11mpeg2dec element is promoted to PRIMARY + 1 rank
+    -   Added d3d11ipcsrc and d3d11ipcsink elements for zero-copy GPU memory sharing between multiple processes.
+    -   Added HLSL shader pre-compilation (at binary build time) support in MSVC build.
+    -   d3d11videosink and d3d11convert elements support 3D transform, MSAA (MultiSampling Anti-Aliasing) and anisotropic
+        sampling method.
+    -   Added support for more raw video formats by using compute shader. A list of supported raw video formats can be found in
+        the d3d11videosink plugin documentation.
+    -   Added d3d11overlay element for applications to be able to draw/blend an overlay image on Direct3D11 texture directly.
 
--   The Windows screen capture element dxgiscreencapsrc has been
-    removed, please use d3d11screencapturesrc instead.
+-   New Direct3D12 plugin: From a video decoding, conversion, composition, and rendering point of view, this new d3d12 plugin is
+    feature-wise near equivalent to the d3d11 plugin. Notable differences between d3d12 and d3d11 are:
+    -   The GStreamer Direct3D12 integration layer is not exposed as a GStreamer API yet. Thus, other plugins such as amfcodec,
+        nvcodec, qsv, and dwrite are not integrated with d3d12 yet.
+    -   H.264 video encoding support via d3d12h264enc element.
+        -   Direct3D12 video encoding API requires Windows 11 or DirectX 12 Agility SDK
+    -   IPC, overlay, and deinterlace elements are not implemented in d3d12
+    -   Windows Graphics Capture API based screen capturing is not implemented in d3d12
+    -   In this release, MSVC is the only officially supported toolchain for the d3d12 plugin build.
+    -   All d3d12 elements are zero ranked for now. Users will need to adjust rank of each d3d12 element via GST_PLUGIN_RANK
+        environment or appropriate plugin feature APIs if they want these elements autoplugged.
 
--   On Android the minimum supported Android API version is now version
-    21 and has been increased from 16.
+## Documentation improvements
 
--   On macOS, the GLib version shipped with the GStreamer binaries will
-    no longer initialize an NSApp or run an NSRunLoop on the main
-    thread. See macOS/iOS section above for details.
+-   hotdoc has been updated to the latest version, and the theme has also been updated, which should fix various usability
+    issues.
 
--   decklink: The decklink plugin is now using the 12.2.2 version of the
-    SDK and will not work with drivers older than version 12.
+## Possibly Breaking Changes
 
--   On iOS Apple Bitcode support was removed from the binaries. This
-    feature is deprecated since XCode 14 and not used on the App Store
-    anymore.
+-   gst_plugin_feature_check_version() has been updated to fix unexpected version check behaviour for git versions. It would
+    return TRUE if the plugin version is for a git development version (e.g. 1.24.0.1) and the check is for the “next” micro
+    version (1.24.1). Now it no longer does this. This just brings the runtime version check in line with the build time version
+    checks which have been fixed some time ago.
 
--   The MP4/Matroska/WebM muxers now require the “stream-format” to be
-    provided as part of the AV1 caps as only the original “obu-stream”
-    format is supported in these containers and not the “annexb” format.
+-   GstAudioAggregator and subclasses such as audiomixer now sync property values to output timestamp, which is what
+    GstVideoAggregator has been doing already since 2019, and which makes sense, as the properties need to change at every
+    output frame based on the output time because they may change even though the input frame is not changing.
 
-Known Issues
+-   rtpac3depay now outputs audio/x-ac3 instead of audio/ac3 as that is the canonical media format in GStreamer. audio/ac3 is
+    still sometimes accepted as input format for backwards compatibility (e.g. in rtpac3pay or ac3parse), but shouldn’t be
+    output.
 
--   The Windows UWP build in Cerbero needs fixing after the recent GLib
-    upgrade (see above)
+-   timecodestamper: The “drop-frame” property now defaults to TRUE
 
--   The C# bindings have not been updated to include new 1.22 API yet
-    (see above)
+-   The NVIDIA desktop GPU decoders nvh264sldec, nvh265sldec, nvvp8sldec and nvvp9sldec were renamed to nvh264dec, nvh265dec,
+    nvvp8dec and nvvp9dec, respectively.
 
-Statistics
+## Known Issues
 
--   4072 commits
+-   There are known issues with FFmpeg version 6.0.0 due to opaque passing being broken in that version. This affects at least
+    avdec_h264, but may affect other decoders as well. Versions before 6.0.0, and 6.0.1 or higher are not affected.
 
--   2224 Merge Requests
+-   gst-libav < 1.24.6 didn’t build against the latest FFmpeg 7.0 release. This has been worked on and tracked in this “libav:
+    Fix compatibility with ffmpeg 7” Merge Request.
 
--   716 Issues
+## Statistics
 
--   200+ Contributors
+-   4643 commits
 
--   ~33% of all commits and Merge Requests were in Rust modules
+-   2405 Merge Requests
+
+-   850 Issues
+
+-   290+ Contributors
+
+-   ~25% of all commits and Merge Requests were in Rust modules
 
 -   4747 files changed
 
@@ -1155,85 +1299,1891 @@ Statistics
 
 -   259791 lines added (net)
 
-Contributors
-
-Ádám Balázs, Adam Doupe, Adrian Fiergolski, Adrian Perez de Castro, Alba
-Mendez, Aleix Conchillo Flaqué, Aleksandr Slobodeniuk, Alicia Boya
-García, Alireza Miryazdi, Andoni Morales Alastruey, Andrew Pritchard,
-Arun Raghavan, A. Wilcox, Bastian Krause, Bastien Nocera, Benjamin
-Gaignard, Bill Hofmann, Bo Elmgreen, Boyuan Zhang, Brad Hards, Branko
-Subasic, Bruce Liang, Bunio FH, byran77, Camilo Celis Guzman, Carlos
-Falgueras García, Carlos Rafael Giani, Célestin Marot, Christian Wick,
-Christopher Obbard, Christoph Reiter, Chris Wiggins, Chun-wei Fan, Colin
-Kinloch, Corentin Damman, Corentin Noël, Damian Hobson-Garcia, Daniel
-Almeida, Daniel Morin, Daniel Stone, Daniels Umanovskis, Danny Smith,
-David Svensson Fors, Devin Anderson, Diogo Goncalves, Dmitry Osipenko,
-Dongil Park, Doug Nazar, Edward Hervey, ekwange, Eli Schwartz, Elliot
-Chen, Enrique Ocaña González, Eric Knapp, Erwann Gouesbet, Evgeny
-Pavlov, Fabian Orccon, Fabrice Fontaine, Fan F He, F. Duncanh, Filip
-Hanes, Florian Zwoch, François Laignel, Fuga Kato, George Kiagiadakis,
-Guillaume Desmottes, Gu Yanjie, Haihao Xiang, Haihua Hu, Havard Graff,
-Heiko Becker, He Junyan, Henry Hoegelow, Hiero32, Hoonhee Lee, Hosang
-Lee, Hou Qi, Hugo Svirak, Ignacio Casal Quinteiro, Ignazio Pillai, Igor
-V. Kovalenko, Jacek Skiba, Jakub Adam, James Cowgill, James Hilliard,
-Jan Alexander Steffens (heftig), Jan Lorenz, Jan Schmidt, Jianhui Dai,
-jinsl00000, Johan Sternerup, Jonas Bonn, Jonas Danielsson, Jordan
-Petridis, Joseph Donofry, Jose Quaresma, Julian Bouzas, Junsoo Park,
-Justin Chadwell, Khem Raj, Krystian Wojtas, László Károlyi, Linus
-Svensson, Loïc Le Page, Ludvig Rappe, Marc Leeman, Marek Olejnik, Marek
-Vasut, Marijn Suijten, Mark Nauwelaerts, Martin Dørum, Martin Reboredo,
-Mart Raudsepp, Mathieu Duponchelle, Matt Crane, Matthew Waters, Matthias
-Clasen, Matthias Fuchs, Mengkejiergeli Ba, MGlolenstine, Michael Gruner,
-Michiel Konstapel, Mikhail Fludkov, Ming Qian, Mingyang Ma, Myles
-Inglis, Nicolas Dufresne, Nirbheek Chauhan, Olivier Crête, Pablo Marcos
-Oltra, Patricia Muscalu, Patrick Griffis, Paweł Stawicki, Peter
-Stensson, Philippe Normand, Philipp Zabel, Pierre Bourré, Piotr
-Brzeziński, Rabindra Harlalka, Rafael Caricio, Rafael Sobral, Rafał
-Dzięgiel, Raul Tambre, Robert Mader, Robert Rosengren, Rodrigo
-Bernardes, Rouven Czerwinski, Ruben Gonzalez, Sam Van Den Berge,
-Sanchayan Maity, Sangchul Lee, Sebastian Dröge, Sebastian Fricke,
-Sebastian Groß, Sebastian Mueller, Sebastian Wick, Sergei Kovalev,
-Seungha Yang, Seungmin Kim, sezanzeb, Sherrill Lin, Shingo Kitagawa,
-Stéphane Cerveau, Talha Khan, Taruntej Kanakamalla, Thibault Saunier,
-Tim Mooney, Tim-Philipp Müller, Tomasz Andrzejak, Tom Schuring, Tong Wu,
-toor, Tristan Matthews, Tulio Beloqui, U. Artie Eoff, Víctor Manuel
-Jáquez Leal, Vincent Cheah Beng Keat, Vivia Nikolaidou, Vivienne
-Watermeier, WANG Xuerui, Wojciech Kapsa, Wonchul Lee, Wu Tong, Xabier
-Rodriguez Calvar, Xavier Claessens, Yatin Mann, Yeongjin Jeong, Zebediah
-Figura, Zhao Zhili, Zhiyuaniu, مهدي شينون (Mehdi Chinoune),
-
-… and many others who have contributed bug reports, translations, sent
-suggestions or helped testing.
-
-Stable 1.22 branch
-
-After the 1.22.0 release there will be several 1.22.x bug-fix releases
-which will contain bug fixes which have been deemed suitable for a
-stable branch, but no new features or intrusive changes will be added to
-a bug-fix release usually. The 1.22.x bug-fix releases will be made from
-the git 1.22 branch, which will be a stable branch.
-
-1.22.0
-
-1.22.0 was originally released on 23 January 2023.
-
-Schedule for 1.24
-
-Our next major feature release will be 1.24, and 1.23 will be the
-unstable development version leading up to the stable 1.24 release. The
-development of 1.23/1.24 will happen in the git main branch of the
-GStreamer mono repository.
-
-The plan for the 1.24 development cycle is yet to be confirmed.
-
-1.24 will be backwards-compatible to the stable 1.22, 1.20, 1.18, 1.16,
-1.14, 1.12, 1.10, 1.8, 1.6, 1.4, 1.2 and 1.0 release series.
-
-------------------------------------------------------------------------
-
-These release notes have been prepared by Tim-Philipp Müller with
-contributions from Edward Hervey, Matthew Waters, Nicolas Dufresne,
-Nirbheek Chauhan, Olivier Crête, Sebastian Dröge, Seungha Yang, and
-Thibault Saunier.
+## Contributors
+
+Aaron Boxer, Aaron Huang, Acky Xu, adixonn, Adrian Fiergolski, Adrien De Coninck, Akihiro Sagawa, Albert Sjölund, Alessandro
+Bono, Alexande B, Alexander Slobodeniuk, Alicia Boya García, amindfv, Amir Naghdinezhad, anaghdin, Anders Hellerup Madsen,
+Andoni Morales Alastruey, Antonio Kevo, Antonio Rojas, Arnaud Rebillout, Arnaud Vrac, Arun Raghavan, badcel, Balló György, Bart
+Van Severen, Bastien Nocera, Benjamin Gaignard, Bilal Elmoussaoui, Brad Hards, Camilo Celis Guzman, Carlo Cabrera, Carlos
+Falgueras García, Carlos Rafael Giani, Célestin Marot, Chao Guo, Charlie Blevins, Cheah, Vincent Beng Keat, Chris Degawa, Chris
+Spencer, Christian Curtis Veng, Christian Meissl, Christopher Degawa, Chris Wiggins, Cidana-Developers, Colin Kinloch, Damian
+Hobson-Garcia, Daniel Almeida, Daniel Knobe, Daniel Moberg, Daniel Morin, Daniel Pendse, Daniel Stone, Daniel Ulery, Dan
+Searles, Dario Marino Saccavino, Dave Patrick Caberto, David Craven, David Revay, David Rosca, David Svensson Fors, Detlev
+Casanova, Diego Nieto, Dominique Leroux, Dongyun Seo, Doug Nazar, Edward Hervey, Ekwang Lee, elenril, Elliot Chen, Enrique Ocaña
+González, Erik Fröbrant, Eva Pace, Evgeny Pavlov, Fabian Orccon, Felix Yan, Fernando Jiménez Moreno, Florian Zwoch, François
+Laignel, Frank Dana, Georges Basile Stavracas Neto, Guillaume Desmottes, Guillaume Gomez, Gwyn Ciesla, Haihua Hu, Hassene Tmar,
+hassount, Heiko Becker, He Junyan, hguermaz, Hiero32, Hosang Lee, Hou Qi, Hugo Svirak, Hugues Fruchet, Hu Qian, Hyung Song,
+Ignazio Pillai, Ilie Halip, Itamar Marom, Ivan Molodetskikh, Ivan Tishchenko, JackZhouVCD, Jacob Johnsson, jainl28patel, Jakub
+Adam, James Cowgill, James Hilliard, James Oliver, Jan Alexander Steffens (heftig), Jan Beich, Jan Schmidt, Jan Vermaete, Jayson
+Reis, Jeff Wilson, Jeongki Kim, Jeri Li, Jimmi Holst Christensen, Jimmy Ohn, Jochen Henneberg, Johan Adam Nilsson, Johan
+Sternerup, John King, Jonas Danielsson, Jonas K Danielsson, Jonas Kvinge, Jordan Petridis, Jordan Yelloz, Josef Kolář, Juan
+Navarro, Julien Isorce, Jun Zhao, Jurijs Satcs, Kalev Lember, Kamal Mostafa, kelvinhu325, Kevin Song, Khem Raj, Kourosh
+Firoozbakht, Leander Schulten, Leif Andersen, L. E. Segovia, Lieven Paulissen, lijing0010, Lily Foster, Link Mauve, Li Yuanheng,
+Loïc Le Page, Loïc Molinari, Lukas Geiger, Luke McGartland, Maksym Khomenko, Ma, Mingyang, Manel J, Marcin Kolny, Marc Leeman,
+Marc Solsona, Marc Wiblishauser, Marek Vasut, Marijn Suijten, Mark Hymers, Markus Ebner, Martin Nordholts, Martin Robinson, Mart
+Raudsepp, Marvin Schmidt, Mathieu Duponchelle, Matt Feury, Matthew Waters, Matthias Fuchs, Matthieu Volat, Maxim P. Dementyev,
+medithe, Mengkejiergeli Ba, Michael Bunk, Michael Gruner, Michael Grzeschik, Michael Olbrich, Michael Tretter, Michiel
+Westerbeek, Mihail Ivanchev, Ming Qian, Nader Mahdi, naglis, Nick Steel, Nicolas Beland, Nicolas Dufresne, Nirbheek Chauhan,
+Olivier Babasse, Olivier Blin, Olivier Crête, Omar Khlif, Onur Demiralay, Patricia Muscalu, Paul Fee, Pawel Stawicki, Peter
+Stensson, Philippe Normand, Philipp Zabel, PhoenixWorthVCD, Piotr Brzeziński, Priit Laes, Qian Hu, Rabindra Harlalka, Rafał
+Dzięgiel, Rahul T R, rajneeshksoni, Ratchanan Srirattanamet, renjun wang, Rhythm Narula, Robert Ayrapetyan, Robert Mader, Robert
+Rosengren, Robin Gustavsson, Roman Lebedev, Rouven Czerwinski, Ruben Gonzalez, Ruslan Khamidullin, Ryan Pavlik, Sanchayan Maity,
+Sangchul Lee, Scott Kanowitz, Scott Moreau, SeaDve, Sean DuBois, Sebastian Dröge, Sebastian Szczepaniak, Sergey Radionov,
+Seungha Yang, Shengqi Yu, Simon Himmelbauer, Slava Andrejev, Slawomir Pawlowski, soak, Stefan Brüns, Stéphane Cerveau, Stephan
+Seitz, Stijn Last, Talha Khan, Taruntej Kanakamalla, Jin Chung Teng, Théo Maillart, Thibault Saunier, Thomas Schneider,
+Tim-Philipp Müller, Tobias Rapp, Tong Wu, Tristan van Berkom, ttrigui, U. Artie Eoff, utzcoz, Víctor Manuel Jáquez Leal, Vivia
+Nikolaidou, Wang Chuan, William Manley, Wojciech Kapsa, Xabier Rodriguez Calvar, Xavier Claessens, Xuchen Yang, Yatin Maan,
+Yinhang Liu, Yorick Smilda, Yuri Fedoseev, Gang Zhao, Jack Zhou, …
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+Stable 1.24 branch
+
+After the 1.24.0 release there will be several 1.24.x bug-fix releases which will contain bug fixes which have been deemed
+suitable for a stable branch, but no new features or intrusive changes will be added to a bug-fix release usually. The 1.24.x
+bug-fix releases will be made from the git 1.24 branch, which will be a stable branch.
+
+1.24.0
+
+GStreamer 1.24.0 was released on 4 March 2024.
+
+1.24.1
+
+The first 1.24 bug-fix release (1.24.1) was released on 21 March 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.0.
+
+Highlighted bugfixes in 1.24.1
+
+-   Fix instant-EOS regression in audio sinks in some cases when volume is 0
+-   rtspsrc: server compatibility improvements and ONVIF trick mode fixes
+-   rtsp-server: fix issues if RTSP media was set to be both shared and reusable
+-   (uri)decodebin3 and playbin3 fixes
+-   adaptivdemux2/hlsdemux2: Fix issues with failure updating playlists
+-   mpeg123 audio decoder fixes
+-   v4l2codecs: DMA_DRM caps support for decoders
+-   va: various AV1 / H.264 / H.265 video encoder fixes
+-   vtdec: fix potential deadlock regression with ProRes playback
+-   gst-libav: fixes for video decoder frame handling, interlaced mode detection
+-   avenc_aac: support for 7.1 and 16 channel modes
+-   glimagesink: Fix the sink not always respecting preferred size on macOS
+-   gtk4paintablesink: Fix scaling of texture position
+-   webrtc: Allow resolution and framerate changes, and many other improvements
+-   webrtc: Add new LiveKit source element
+-   Fix usability of binary packages on arm64 iOS
+-   various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   registry, ptp: Canonicalize the library path returned by dladdr
+-   segment: Don’t use g_return_val_if_fail() in gst_segment_to_running_time_full()
+-   uri: Sort uri protocol sources/sinks by feature name to break a feature rank tie
+-   ptp: Initialize expected DELAY_REQ seqnum to an invalid value
+-   ptp: Don’t install test executable
+-   gst-inspect: fix –exists for plugins with versions other than GStreamer’s version, like the Rust plugins
+-   identity: Don’t refuse seeks unless single-segment=true
+
+gst-plugins-base
+
+-   audiobasesink: Don’t wait on gap events
+-   audioencoder: Avoid using temporarily mapped memory as base for input buffers
+-   decodebin3: Be more specific when sending missing plugin messages
+-   decodebin3: Fix re-usability issues
+-   decodebin3: Provide clear error message if no decoders present
+-   playbin3: Remove un-needed URI NULL check
+-   uridecodebin3: Don’t hold lock when posting messages or signals
+-   uridecodebin3: Handle potential double redirection errors
+-   glimagesink: Fix the sink not always respecting preferred size on macOS
+-   glupload: Do not propose allocators with sysmem, fixes warning when playing VP9 with alpha
+-   shmallocator: fix build on Illumos
+-   meson: Fix the condition to skip theoradec test
+
+gst-plugins-good
+
+-   adaptivdemux/hlsdemux2: Fix issues with failure updating playlists
+-   mpg123audiodec: Correctly handle the case of clipping all decoded samples
+-   mpg123audiodec: gst_audio_decoder_allocate_output_buffer: assertion ‘size > 0’ failed
+-   qt: Fix description in meson build options
+-   qtdemux: Do not set channel-mask to zero
+-   rtspsrc: remove ‘deprecated’ flag from the ‘push-backchannel-sample’ signal
+-   rtspsrc: Consider 503 Service Not Available when handling broken control urls
+-   rtspsrc, rtponviftimestamp: ONVIF mode fixes
+-   rtspsrc: Don’t invoke close when stopping if we’ve started cleanup, fixing potential crash on shutdown
+-   rtpgstpay: Delay pushing of event packets until the next buffer
+
+gst-plugins-bad
+
+-   asio: Fix {input,output}-channels property handling
+-   cuda,d3d11,d3d12bufferpool: Disable preallocation
+-   d3d11device: Fix adapter LUID comparison in wrapped device mode
+-   d3d12device: Fix IDXGIFactory2 leak
+-   d3d12: Fix SDK debug layer activation
+-   dvbsubenc: Fix bottom field size calculation
+-   dvdspu: avoid null dereference
+-   GstPlay: Fix a critical warning in error callback
+-   v4l2codecs: decoders: Add DMA_DRM caps support
+-   vaav1enc: Init the output_frame_num when resetting gf group
+-   vah264enc, vah265enc, vaav1enc: fix potential crash on devices without rate control
+-   vah265enc: checking surface alignment
+-   videoparsers: Don’t verbosely warn about CEA_708_PROCESS_EM_DATA_FLAG not being set
+-   vtdec: Fix a deadlock during ProRes playback, handle non-linked gracefully
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   gtk4paintablesink: Fix scaling of texture position
+-   janusvrwebrtcsink: Handle 64 bit numerical room ids
+-   janusvrwebrtcsink: Don’t include deprecated audio/video fields in publish messages
+-   janusvrwebrtcsink: Handle various other messages to avoid printing errors
+-   livekitwebrtc: Fix shutdown behaviour
+-   rtpgccbwe: Don’t forward buffer lists with buffers from different SSRCs to avoid breaking assumptions in rtpsession
+-   sccparse: Ignore invalid timecodes during seeking
+-   webrtcsink: Don’t try parsing audio caps as video caps
+-   webrtc: Allow resolution and framerate changes
+-   webrtcsrc: Make producer-peer-id optional
+-   livekitwebrtcsrc: Add new LiveKit source element
+-   regex: Add support for configuring regex behaviour
+-   spotifyaudiosrc: Document how to use with non-Facebook accounts
+-   webrtcsrc: Add do-retransmission property
+
+gst-libav
+
+-   avcodecmap: Increase max AAC channels to 16
+-   avviddec: Fix how we get back the codec frame
+-   avviddec: Fix interlaced mode detection
+-   avviddec: Double check if AV_CODEC_FLAG_COPY_OPAQUE port is safe for our scenario
+
+gst-rtsp-server
+
+-   media: gst_rtsp_media_set_reusable() and gst_rtsp_media_set_shared() have become incompatible
+-   rtsp-stream: clear sockets when leaving bin
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges: Fix critical warning
+
+gst-validate + gst-integration-testsuites
+
+-   No changes
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.1
+
+-   gstreamer: Enable ptp helper explicitly
+-   gst-plugins-bad: Package new insertbin plugin
+-   gst-plugins-rs: Adjust parallel architecture build blocks
+-   libnice: update to 0.1.22
+-   pixman: Bump to 0.43.4
+-   orc: disable JIT code generation on arm64 on iOS again, fixing crashes
+
+Contributors to 1.24.1
+
+Alexander Slobodeniuk, Antonio Larrosa, Edward Hervey, Elizabeth Figura, François Laignel, Guillaume Desmottes, He Junyan, Jan
+Schmidt, Jordan Yelloz, L. E. Segovia, Mark Nauwelaerts, Mathieu Duponchelle, Michael Tretter, Mikhail Rudenko, Nicolas
+Dufresne, Nirbheek Chauhan, Philippe Normand, Piotr Brzeziński, Robert Mader, Ruijing Dong, Sebastian Dröge, Seungha Yang,
+Thomas Goodwin, Thomas Klausner, Tim-Philipp Müller, Xi Ruoyao,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.1
+
+-   List of Merge Requests applied in 1.24.1
+-   List of Issues fixed in 1.24.1
+
+1.24.2
+
+The second 1.24 bug-fix release (1.24.2) was released on 9 April 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.2
+
+-   H.264 parsing regression fixes
+-   WavPack typefinding improvements
+-   Video4linux fixes and improvements
+-   Android build and runtime fixes
+-   macOS OpenGL memory leak and robustness fixes
+-   Qt/QML video sink fixes
+-   Package new analytics and mse libraries in binary packages
+-   Windows MSVC binary packages: fix libvpx avx/avx2/avx512 instruction set detection
+-   various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   clock: Block futex_time64 usage on Android API level < 30
+-   basesrc: Clear submitted buffer lists consistently with buffers
+-   ptpclock: fix double free of domain data during deinit
+-   clocksync: Proxy allocation queries
+-   inputselector: fix possible clock leak on shutdown
+-   typefind: Handle WavPack block sizes > 131072
+
+gst-plugins-base
+
+-   glcolorconvert: Ensure glcolorconvert does not miss supported RGB formats
+-   gl/macos: a couple of race/reference count fixes
+-   pbutils: descriptions: Don’t warn on MPEG-1 audio caps without layer field
+-   encodebin: Add the parser before timestamper to tosync list
+-   videorate: Reset last_ts when a new segment is received
+
+gst-plugins-good
+
+-   qml6glsink: fix destruction of underlying texture
+-   qt/qt6: Fixup for dummy textures
+-   rtpjitterbuffer: Don’t use estimated_dts to do default skew adjustment
+-   rtpjitterbuffer: Use an extended RTP timestamp for the clock-base
+-   rtpmp4adepay: Set duration on outgoing buffers
+-   tests: rtpred: fix out-of-bound writes
+-   v4l2: allocator: Fix unref log/trace on memory release
+-   v4l2: Also set max_width/max_width if enum framesize fail
+-   v4l2: enforce a pixel aspect ratio of 1/1 if no data are available
+-   v4l2: fix error in calculating padding bottom for tile format
+-   v4l2src: need maintain the caps order in caps compare when fixate
+-   vpxenc: Include vpx error details in errors and warnings
+
+gst-plugins-bad
+
+-   h264parse: element hangs with some video streams (regression)
+-   h264parse: Revert “AU boundary detection changes”
+-   alphadecodebin: Explicitly pass 64 bit integers as such through varargs
+-   atdec: Set a channel mask for channel counts greater than 2
+-   ccconverter: Fix caps leak and remove unnecessary code
+-   d3d11videosink: disconnect signals before releasing the window
+-   d3d11: meson: Add support for MinGW DirectXMath package and update directxmath wrap to 3.1.9
+-   d3d11: meson: Disable library build if DirectXMath header was not found
+-   dwrite: Fix crash on device update
+-   GstPlay: Update video_snapshot to support playbin3
+-   jpegparse: avi1 tag can be progressive
+-   jpegparse: turn some bus warnings into object ones
+-   qsvdecoder: Release too old frames
+-   ristsrc: Only free caps if needed
+-   va: av1enc: Correct the reference number and improve the reference setting
+-   va: {vp9, av1}enc: Avoid reopen encoder or renegotiate
+-   videoparsers: Demote CC warning message
+-   vkbufferpool: correct usage flags type
+-   vkh26xdec: a couple decoding fixes
+-   vtdec: Fix caps criticals during negotiation
+-   wpe: avoid crash with G_DEBUG=fatal_criticals and static build
+-   Sink missing floating references
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   aws: use fixed BehaviorVersion
+-   aws: improve error message logs
+-   fmp4: Update to dash-mpd 0.16
+-   fmp4mux: Require gstreamer-pbutils 1.20 for the examples
+-   onvifmetadataparse: Reset state in PAUSED->READY after pad deactivation, fixing occasional deadlock on shutdown
+-   reqwest: Update to reqwest 0.12
+-   webrtcsink: set perfect-timestamp=true on audio encoders
+-   webrtcsink: improve panic message on unexpected caps during discovery
+-   webrtchttp: Update to reqwest 0.12
+-   webrtc: fix inconsistencies in documentation of object names
+-   Fix clippy warnings after upgrade to Rust 1.77
+
+gst-libav
+
+-   avviddec: Fix AVPacket leak
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges: frame-composition-meta: Stop using keyword ‘operator’ for field in C++
+
+gst-validate + gst-integration-testsuites
+
+-   No changes
+
+gst-examples
+
+-   webrtc examples: set perfect-timestamp=true on opusenc for better Chrome interoperability
+
+Development build environment
+
+-   flac: Add subproject wrap and allow falling back to it in the flac plugin
+-   libnice: bump subproject wrap to v0.1.22 (needed for ICE consent freshness support in gstwebrtc)
+
+Cerbero build tool and packaging changes in 1.24.2
+
+-   glib: Block futex_time64 usage on Android API level < 30
+-   libvpx: Fix build with Python 3.8
+-   libvpx: Fix errors with avx* instruction set detection for x86* builds and MSVC
+-   openjpeg: Update to 2.5.2
+-   directxmath: Update to 3.1.9
+-   gst-plugins-rs: Fix superstripping for ELF breaking all plugins
+-   Rust-based plugin initialization hangs on Android with GStreamer 1.24.0
+
+Contributors to 1.24.2
+
+Alexander Slobodeniuk, Arnaud Vrac, Chao Guo, Chris Spencer, Daniel Morin, Edward Hervey, Elizabeth Figura, Elliot Chen, eri,
+François Laignel, Guillaume Desmottes, Haihua Hu, He Junyan, Hou Qi, Jan Schmidt, Jochen Henneberg, L. E. Segovia, Martin
+Nordholts, Matthew Waters, Nicolas Dufresne, Philippe Normand, Philipp Zabel, Piotr Brzeziński, Robert Guziolowski, Robert
+Mader, Ruben Gonzalez, Sebastian Dröge, Seungha Yang, Taruntej Kanakamalla, Thibault Saunier, Tim Blechmann, Tim-Philipp Müller,
+Víctor Manuel Jáquez Leal, Wojciech Kapsa, Xavier Claessens,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.2
+
+-   List of Merge Requests applied in 1.24.2
+-   List of Issues fixed in 1.24.2
+
+1.24.3
+
+The third 1.24 bug-fix release (1.24.3) was released on 30 April 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.3
+
+-   EXIF image tag parsing security fixes
+-   Subtitle handling improvements in parsebin
+-   Fix issues with HLS streams that contain VTT subtitles
+-   Qt6 QML sink re-render and re-sizing fixes
+-   unixfd ipc plugin timestamp and segment handling fixes
+-   vah264enc, vah265enc: Do not touch the PTS of the output frame
+-   vah264dec and vapostproc fixes and improvements
+-   v4l2: multiple fixes and improvements, incl. for mediatek JPEG decoder and v4l2 loopback
+-   v4l2: fix hang after seek with some v4l2 decoders
+-   Wayland sink fixes
+-   ximagesink: fix regression on RPi/aarch64
+-   fmp4mux, mp4mux gained FLAC audio support
+-   D3D11, D3D12: reliablity improvements and memory leak fixes
+-   Media Foundation device provider fixes
+-   GTK4 paintable sink improvements including support for directly importing dmabufs with GTK 4.14
+-   WebRTC sink/source fixes and improvements
+-   AWS s3sink, s3src, s3hlssink now support path-style addressing
+-   MPEG-TS demuxer fixes
+-   Python bindings fixes
+-   various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   ptp: Silence Rust compiler warning about some unused trait methods
+
+gst-plugins-base
+
+-   EXIF image tag parsing security fixes
+-   glcolorconvert: remove some dead code
+-   parsebin: Ensure non-time subtitle streams get “parsed”
+-   playbin3: Handle combiner update in case of errors
+-   ximagesink: initialize mask for XISelectEvents
+
+gst-plugins-good
+
+-   adaptivedemux2: Playback hangs with VTT fragments
+-   adaptivedemux2: Avoid double usage of parsebin
+-   pulsedeviceprovider: Add compare_device_type_name function and missing lock
+-   qml6glsink: Notify that the returned QSGNode node has changes
+-   qml6glsink: video content resizes to new item size
+-   qtdemux: fix wrong full_range offset when parsing colr box
+-   soup: fix thread name
+-   v4l2: add multiplane y42b (yuv422m) support (for mediatek v4l2 jpeg decoder)
+-   v4l2: bufferpool: Drop writable check on output pool process
+-   v4l2: bufferpool: Ensure freshly created buffers are not marked as queued, fixing issues with v4l2sink on a v4l2loopback
+    device
+-   v4l2: bufferpool: queue back the buffer flagged LAST but empty, fixes hangs after seek with some decoders
+-   v4l2: silence valgrind warning
+-   vpx: Include vpx error details in errors and warnings
+
+gst-plugins-bad
+
+-   d3d11device: protect device_lock vs device_new
+-   d3d11decoder, d3d12decoder: Fix potential use after free
+-   d3d11videosink: Fix rendering on keyed mutex enabled handle
+-   d3d12decoder: Fix d3d12 resource copy
+-   d3d12encoder: Fix buffer pool leak
+-   d3d12videosink: HWND event handling related fixes
+-   d3d12vp9dec: Fix Intel GPU crash occurred when decoding VP9 SVC
+-   dvbsubenc: fixed some memory leaks and a crash
+-   GstPlay: fix read duration failure issue for some type rtsp streams which have valid duration
+-   mediafoundation: Fix device enumeration
+-   mediafoundation: Fix infinite loop in device provider
+-   tests: fix possible libscpp build failure in gst-plugins-bad
+-   tsdemux, tsparse: Fix Program equality check
+-   tsdemux: Disable smart program update
+-   unixfdsink: Take segment into account when converting timestamps
+-   va: videoformat: use video library to get DRM fourcc
+-   va: radeonsi: DRM RGB formats doesn’t look correctly mapped to VA formats
+-   vah264enc, vah265enc: Do not touch the PTS of output frame
+-   vaav1enc: Change the alignment of output to “tu”
+-   vaallocator: disable derived all together for Mesa <23.3
+-   waylandsink: free staged buffer when do gst_wl_window_finalize
+-   wlwindow: clear configure mutex and cond when finalize
+-   waylandsink: config buffer pool with query size when propose_allocation
+-   v4l2codecs: Don’t unref allocation query caps
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+Fixed:
+
+-   hrtfrender: Use a bitmask instead of an int in the caps for the channel-mask
+-   rtpgccbwe: Don’t log an error when pushing a buffer list fails while stopping
+-   webrtcsink: Don’t panic in bitrate handling with unsupported encoders
+-   webrtcsink: Don’t panic if unsupported input caps are used
+-   webrtcsrc: Allow a None producer-id in request-encoded-filter signal
+
+Added:
+
+-   aws: New property to support path-style addressing
+-   fmp4mux / mp4mux: Support FLAC inside (f)MP4
+-   gtk4: Support directly importing dmabufs with GTK 4.14
+-   gtk4: Add force-aspect-ratio property similar to other video sinks
+
+gst-libav
+
+-   libav: guard some recently dropped APIs
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   Fix Python library name fetching and library searching on Windows
+-   Don’t link to python for the gi overrides module
+
+gst-editing-services
+
+-   ges-launcher: Fix for forcing container profiles
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   debug-viewer: Fix plugin loading machinery
+-   validate/flvdemux: Stop spamming audio/video on test
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.3
+
+-   No changes
+
+Contributors to 1.24.3
+
+Alexander Slobodeniuk, Edward Hervey, Elliot Chen, F. Duncanh, François Laignel, Haihua Hu, He Junyan, Hou Qi, Jan Schmidt,
+Jimmy Ohn, Maksym Khomenko, Matthew Waters, Nicolas Dufresne, Nirbheek Chauhan, Philippe Normand, Philipp Zabel, Qian Hu (胡骞),
+Sanchayan Maity, Sebastian Dröge, Seungha Yang, Simonas Kazlauskas, Taruntej Kanakamalla, Tim Blechmann, Tim-Philipp Müller, U.
+Artie Eoff, Víctor Manuel Jáquez Leal, William Wedler, Xavier Claessens,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.3
+
+-   List of Merge Requests applied in 1.24.3
+-   List of Issues fixed in 1.24.3
+
+1.24.4
+
+The fourth 1.24 bug-fix release (1.24.4) was released on 29 May 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.4
+
+-   audioconvert: support more than 64 audio channels
+-   avvidec: fix dropped frames when doing multi-threaded decoding of I-frame codecs such as DV Video
+-   mpegtsmux: Correctly time out in live pipelines, esp. for sparse streams like KLV and DVB subtitles
+-   vtdec deadlock fixes on shutdown and format/resolution changes (as might happen with e.g. HLS/DASH)
+-   fmp4mux, isomp4mux: Add support for adding AV1 header OBUs into the MP4 headers, and add language from tags
+-   gtk4paintablesink improvements: fullscreen mode and gst-play-1.0 support
+-   webrtcsink: add support for insecure TLS and improve error handling and VP9 handling
+-   v4l2codecs: decoder: Reorder caps to prefer DMA_DRM ones, fixes issues with playbin3
+-   vah264enc, vah265enc: timestamp handling fixes; generate IDR frames on force-keyunit-requests, not I frames
+-   Visualizer plugins fixes
+-   Avoid using private APIs on iOS
+-   Various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   clock: Fix 32 bit assertions in GST_TIME_TO_TIMEVAL and GST_TIME_TO_TIMESPEC
+-   systemclock: fix usage of __STDC_NO_ATOMICS__
+-   filesrc: Don’t abort on _get_osfhandle() on Windows
+-   tests: Avoid using “bool” for the variable name
+-   Various Solaris / Illumos fixes
+-   parse: Don’t assume that child proxy child objects are GstObjects
+
+gst-plugins-base
+
+-   audioconvert: Support converting >64 channels
+-   decodebin3: Fix caps and stream leaks
+-   playbin(3), streamsynchronizer: Fix deadlock when streams have been flushed before others start
+-   videotestsrc: fix race condition when clearing cached buffer
+-   Fix visualization plugins
+-   GstDiscoverer hangs when processing media file containing mebx on MacOS
+-   glmixer: Add GL_SYNC_META option to buffer pool
+-   typefinding: Fix ID_ODD_SIZE handling regression in WavPack typefinder
+
+gst-plugins-good
+
+-   osxaudio: Avoid using private APIs on iOS
+-   qtdemux: unit test fixes for 32-bit platforms
+
+gst-plugins-bad
+
+-   cudamemory: Fix offset of subsampled planar formats
+-   d3d11: Revert “d3d11device: protect device_lock vs device_new
+-   d3dshader: Fix gamma and primaries conversion pixel shader
+-   dtlsconnection: Fix overflow in timeout calculation on systems with 32 bit time_t
+-   GstPlay: Initialize debug category and error quark in class_init
+-   kmssink: Do not close the DRM prime handle twice
+-   mpegtsmux: Correctly time out and mux anyway in live pipelines
+-   nvcodec: Accept progressive-high profiles for h264
+-   nvencoder: Fix maximum QP value setting
+-   qsvh264dec, qsvh265dec: Fix nalu leaks
+-   v4l2codecs: decoder: Reorder caps to prefer DMA_DRM ones
+-   vah264enc, vah265enc: Let FORCE_KEYFRAME be IDR frame rather than just I frame
+-   vah264enc, vah265enc: Set DTS offset before PTS
+-   vkh26xdec: Fix stop memory leak
+-   vtdec: Fix deadlock when negotiating format change
+-   vtdec: Fix PAUSED->READY deadlock when output loop is running
+-   wayland: Use wl_display_create_queue_with_name
+-   webrtc: request-aux-sender, only sink floating refs
+-   webrtcbin: Allow session level setup attribute in SDP
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   fmp4mux, isomp4mux: Add support for adding AV1 header OBUs into the MP4 headers
+-   fmp4mux, isomp4mux: Add language from tags
+-   gtk4paintablesink: Also create own window for gst-play-1.0
+-   gtk4paintablesink: Add GST_GTK4_WINDOW_FULLSCREEN environment variable to fullscreen window for testing with
+    e.g. gst-launch-1.0
+-   gtk4: Fix plugin description and update python examples
+-   rtpgccbwe: Log effective bitrate in more places, and log delay and loss target bitrates separately
+-   webrtcsink: Improve error when no encoder was found or RTP plugins were missing
+-   webrtcsink: Add VP9 parser after the encoder for VP9 too
+-   webrtc: Add support for insecure TLS connections
+
+gst-libav
+
+-   avvidec: Fix dropping wrong “ghost” frames - fixing multi-threaded decoding of I-frame codecs such as DV Video
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges-pipeline: Configure encodebin before linking
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   No changes
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.4
+
+-   Add Fedora 40 support
+-   srt: bump version to 1.5.3
+-   pango: Fix leaks on Windows (textoverlay plugins)
+-   windows runtime installer has empty qt6 msm
+-   WiX: assorted fixes
+
+Contributors to 1.24.4
+
+Alexander Slobodeniuk, Bill Nottingham, Brad Reitmeyer, Chris Del Guercio, Daniel Stone, Edward Hervey, Emil Pettersson, He
+Junyan, Jordan Petridis, Joshua Breeden, L. E. Segovia, Martin Nordholts, Mathieu Duponchelle, Matthew Waters, Nirbheek Chauhan,
+Philippe Normand, Piotr Brzeziński, Rafael Carício, Robert Ayrapetyan, Robert Mader, Sebastian Dröge, Seungha Yang, Shengqi Yu,
+Stéphane Cerveau, Tim-Philipp Müller,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.4
+
+-   List of Merge Requests applied in 1.24.4
+-   List of Issues fixed in 1.24.4
+
+1.24.5
+
+The fifth 1.24 bug-fix release (1.24.5) was released on 20 June 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.5
+
+-   webrtcsink: Support for AV1 via nvav1enc, av1enc or rav1enc encoders
+-   AV1 RTP payloader/depayloader fixes to work correctly with Chrome and Pion WebRTC
+-   av1parse, av1dec error handling/robustness improvements
+-   av1enc: Handle force-keyunit events properly for WebRTC
+-   decodebin3: selection and collection handling improvements
+-   hlsdemux2: Various fixes for discontinuities, variant switching, playlist updates
+-   qml6glsink: fix RGB format support
+-   rtspsrc: more control URL handling fixes
+-   v4l2src: Interpret V4L2 report of sync loss as video signal loss
+-   d3d12 encoder, memory and videosink fixes
+-   vtdec: more robust error handling, fix regression
+-   ndi: support for NDI SDK v6
+-   Various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   promise: Don’t use g_return_* for internal checks
+-   debug: Add missing gst_debug_log_id_literal() dummy with gst_debug=false
+-   ptp-helper: Add GNU/Hurd support
+
+gst-plugins-base
+
+-   uridecodebin3: Don’t hold PLAY_ITEMS lock when activating them
+-   decodebin3: Always ensure we end up with parsebin or identity
+-   decodebin3: Properly support changing input collections
+-   decodebin3: Avoid usage of parsebin even more
+-   decodebin3: Fix dealing with input without caps
+-   video-info: Don’t crash in gst_video_info_is_equal() if one videoinfo is zero-initialized
+
+gst-plugins-good
+
+-   flacparse: fix buffer overflow
+-   hlsdemux2: Various fixes for discontinuities, variant switching, playlist updates
+-   qml6glsink: fix RGB format support
+-   rtpdtmfdepay: fix caps negotiation with audioconvert
+-   rtpdtmfsrc: fix leak when shutting down mid-event
+-   rtspsrc: fix invalid seqnum assertions
+-   rtspsrc: Various control URL handling fixes
+-   v4l2src: Interpret V4L2 report of sync loss as video signal loss
+
+gst-plugins-bad
+
+-   av1parse: Do not return error when expectedFrameId mismatch
+-   av1dec: Don’t treat decoding errors as fatal and print more error details
+-   av1enc: Handle force-keyunit events properly by requesting keyframes
+-   codectimestamper: never set DTS to NONE
+-   d3d12encoder: Do not print error log for not-supported feature
+-   d3d12memory: Fix staging buffer alignment
+-   d3d12videosink: Disconnect window signal handler on dispose as intended
+-   dtlssrtpenc: Don’t crash if no pad name is provided when requesting a new pad
+-   glcolorconvert: update existing sync meta if outbuf has one
+-   pcapparse: Parsing code assumes unaligned memory accesses are OK
+-   pcapparse: Avoid unaligned memory access
+-   tsdemux: Fix maximum PCR/DTS values
+-   vtdec: Improve error handling in edge cases
+-   vtdec, qtdemux: regression in 1.24.3 - internal data stream error
+-   uvcgadget: Use g_path_get_basename instead of libc basename
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   gtk4: update flatpak integration code
+-   ndi: Add support for loading NDI SDK v6
+-   reqwesthttpsrc: Fix race condition when unlocking
+-   rtp: Don’t restrict payload types for payloaders
+-   rtp: av1pay: Correctly use N flag for marking keyframes
+-   rtp: av1depay: Parse internal size fields of OBUs and handle them
+-   webrtcsink: Refactor value retrieval to avoid lock poisoning
+-   webrtcsink: Add support for AV1
+-   webrtc: Update to async-tungstenite 0.26
+-   Fix various new clippy 1.79 warnings
+-   meson: Fix various issues in dependency management, feature detection, some regressions, and add logging
+
+gst-libav
+
+-   No changes
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   No changes
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   No changes
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.5
+
+-   osxrelocator: Fix RPATHs in deeply nested libraries
+
+Contributors to 1.24.5
+
+Angelo Verlain, Chris Del Guercio, Corentin Damman, Edward Hervey, Francisco Javier Velázquez-García, He Junyan, Jakub Adam,
+Jakub Vaněk, Khem Raj, L. E. Segovia, Martin Nordholts, Mathieu Duponchelle, Nirbheek Chauhan, Piotr Brzeziński, Samuel
+Thibault, Sebastian Dröge, Sergey Krivohatskiy, Seungha Yang, Tim-Philipp Müller, Zach van Rijn,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.5
+
+-   List of Merge Requests applied in 1.24.5
+-   List of Issues fixed in 1.24.5
+
+1.24.6
+
+The sixth 1.24 bug-fix release (1.24.6) was released on 29 July 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.6
+
+-   Fix compatibility with FFmpeg 7.0
+-   qmlglsink: Fix failure to display content on recent Android devices
+-   adaptivedemux: Fix handling of closed caption streams
+-   cuda: Fix runtime compiler loading with old CUDA tookit
+-   decodebin3 stream selection handling fixes
+-   d3d11compositor, d3d12compositor: Fix transparent background mode with YUV output
+-   d3d12converter: Make gamma remap work as intended
+-   h264decoder: Update output frame duration for interlaced video when second field frame is discarded
+-   macOS audio device provider now listens to audio devices being added/removed at runtime
+-   Rust plugins: audioloudnorm, s3hlssink, gtk4paintablesink, livesync and webrtcsink fixes
+-   videoaggregator: preserve features in non-alpha caps for subclasses with non-system memory sink caps
+-   vtenc: Fix redistribute latency spam
+-   v4l2: fixes for complex video formats
+-   va: Fix strides when importing DMABUFs, dmabuf handle leaks, and blocklist unmaintained Intel i965 driver for encoding
+-   waylandsink: Fix surface cropping for rotated streams
+-   webrtcdsp: Enable multi_channel processing to fix handling of stereo streams
+-   Various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   downloadbuffer: fix push mode
+-   queue: queue2: multiqueue: Don’t work with segment.position if buffers have no timestamps
+-   gst-inspect-1.0: Fix leak of plugin/feature
+
+gst-plugins-base
+
+-   decodebin3: Fix detection of selection done
+-   glvideomixer: Fix critical when setting start-time-selection
+-   parsebin: accept-caps handling for elements with unusual sink pad names
+-   subparse: Don’t use jit for regular expressions when running in valgrind
+-   subparse: put valgrind header availability define into config.h for subparse
+-   videoaggregator: preserve features in non-alpha caps
+-   videoscale: correct classification error
+-   meson: Fix invalid include flag in uninstalled gl pc file
+-   Fix various memory leaks
+
+gst-plugins-good
+
+-   adaptivedemux: Fix handling closed caption streams
+-   qml/glsink: also support GLES2 needing shader ‘precision’ directives
+-   v4l2object: use v4l2 reported width for padded_width when complex video formats
+-   v4l2: meson: fix SIZEOF_OFF_T when cross-compiling with Meson >= 1.3.0
+
+gst-plugins-bad
+
+-   svtav1enc: Fix segfault when flushing
+-   avfdeviceprovider: Fix debug category initialization
+-   osxaudiodeviceprovider: Listen for audio devices being added/removed
+-   avtp: Fixed Linux/Alpine 3.20 build
+-   cuda: Fix runtime compiler loading with old CUDA tookit
+-   d3d11compositor, d3d12compositor: Fix transparent background mode with YUV output
+-   d3d11converter: Fix runtime compiled shader code
+-   d3d12converter: Make gamma remap work as intended
+-   h264decoder: Update output frame duration when second field frame is discarded
+-   isac: Work around upstream having no shared library support for MSVC
+-   lc3: remove bitstream comparison in the tests
+-   qroverlay: redraw overlay when caps changes
+-   qsv: Fix critical warnings
+-   rtmp2: guard against calling gst_amf_node_get_type() with NULL
+-   srtsrc: fix case fallthrough of authentication param
+-   va: Blocklist unmaintained i965 driver for encoding
+-   va: Fix strides when importing DMABUFs
+-   va: Fix dmabuf handle leaks
+-   vadisplay: fix minor version check
+-   vkh265dec: Fix H.264 ref in logs
+-   vulkan: fix wrong stages or access in barriers
+-   vtenc: Fix redistribute latency spam
+-   waylandsink: Fix surface cropping for rotated streams
+-   webrtcdsp: Enable multi_channel processing
+
+gst-plugins-ugly
+
+-   asfdemux: Be more lenient towards malformed header, fixes playback of files written by VLC
+
+GStreamer Rust plugins
+
+-   audioloudnorm: Fix limiter buffer index wraparound off-by-one for the last buffer
+-   aws: s3hlssink: Do not call abort before finishing uploads
+-   gtk4paintablesink: Support RGBx formats in SW paths
+-   gtk4paintablesink: default to force-aspect-ratio=false for Paintable
+-   livesync: Use the actual output buffer duration of gap filler buffers
+-   livesync: Allow queueing up to latency buffers, also sync on the first buffer and add sync property
+-   webrtcsink: fix property types for rav1enc AV1 encoder
+
+gst-libav
+
+-   Fix compatibility with ffmpeg 7
+-   avauddec: Fix crash on stop()
+-   avmux: Fix crash when muxer doesn’t get codecid
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges: Various leak fixes
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   validate: Remove G_REGEX_OPTIMIZE usage, helps avoid valgrind issues
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   libgudev wrap: add fallback uri
+-   liblc3 wrap: update to v1.1.1
+
+Cerbero build tool and packaging changes in 1.24.6
+
+-   meson: Backport fix for Glib including a GCC-only flag in the pkg-config file
+-   libsoup: Workaround build error with GCC 14
+-   libltc: Fix Windows build
+-   webrtc-audio-processing: Fix MinGW build
+-   libvpx: Also build a shared lib on macOS
+-   glib: Fix Windows build
+-   osxrelocator: Fix framework entrypoints being unable to load dylibs
+-   gobject-introspection, recipe: Fix more fallout from the Meson dylib ID fixes
+-   cargo-c.recipe: Ensure that we can change the id and rpath
+
+Contributors to 1.24.6
+
+Chris Spoelstra, Edward Hervey, François Laignel, Guillaume Desmottes, Jakub Adam, Jan Schmidt, Jordan Petridis, L. E. Segovia,
+Loïc Yhuel, Matthew Waters, Nirbheek Chauhan, Piotr Brzeziński, Robert Mader, Ruben Gonzalez, Sanchayan Maity, Sebastian Dröge,
+Sebastian Gross, Seungha Yang, Shengqi Yu, Taruntej Kanakamalla, Tim-Philipp Müller, tomaszmi, Víctor Manuel Jáquez Leal,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.6
+
+-   List of Merge Requests applied in 1.24.6
+-   List of Issues fixed in 1.24.6
+
+1.24.7
+
+The seventh 1.24 bug-fix release (1.24.7) was released on 21 August 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.7
+
+-   Fix APE and Musepack audio file and GIF playback with FFmpeg 7.0
+-   playbin3: Fix potential deadlock with multiple playbin3s with glimagesink used in parallel
+-   qt6: various qmlgl6src and qmlgl6sink fixes and improvements
+-   rtspsrc: expose property to force usage of non-compliant setup URLs for RTSP servers where the automatic fallback doesn’t
+    work
+-   urisourcebin: gapless playback and program switching fixes
+-   v4l2: various fixes
+-   va: Fix potential deadlock with multiple va elements used in parallel
+-   meson: option to disable gst-full for static-library build configurations that do not need this
+-   cerbero: libvpx updated to 1.14.1; map 2022Server to Windows11; disable rust variant on Linux if binutils is too old
+-   Various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   bin: Don’t keep the object lock while setting a GstContext when handling NEED_CONTEXT
+-   core: Log pad name, not just the pointer
+
+gst-plugins-base
+
+-   pbutils: descriptions: use subsampling factor to get YUV subsampling
+-   rtspconnection: Handle invalid argument properly
+-   urisourcebin: Actually drop EOS on old-school pad switch
+-   urisourcebin: Don’t hold lock when emitting about-to-finish
+-   gst-launch deadlock with two playbin3s
+-   xvimagesink: Fix crash in pool on error
+
+gst-plugins-good
+
+-   qmlgl6src: Fix crash when use-default-fbo is not set
+-   qt6glwindow: Fallback to GL_RGB on CopyTexImage2D error, fixing usage with eglfs backend
+-   qt6glwindow: Only use GL_READ_FRAMEBUFFER when we do blits
+-   qt6: glwindow: Don’t leak previously rendered buffer
+-   rtspsrc: expose property for forcing usage of non-compliant URLs
+-   v4l2object: fix ARIB_STD_B67 colorimetry unmatch issue
+-   v4l2: Fix colorimetry mismatch for encoded format with RGB color-matrix
+
+gst-plugins-bad
+
+-   aom: av1enc: restrict allowed input width and height
+-   h264parse: bypass check for length_size_minus_one
+-   h265parse: Reject FD received before SPS
+-   msdk: replace strcmp with g_strcmp0
+-   msdkvc1dec crashes (segfault)
+-   rsvgoverlay: add debug category
+-   va: don’t use GST_ELEMENT_WARNING in set_context() vmethod to fix potential deadlock
+-   va: deadlock when playing two videos at once
+-   webrtc: Add missing G_BEGIN/END_DECLS in header for C++
+-   wpe: initialize threading.ready before reading it
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   gtk4: Move the dmabuf cfg to the correct bracket level
+
+gst-libav
+
+-   avdemux: Fix deadlock with FFmpeg 7.x when serialized events are received from upstream while opening, such as e.g. APE
+    files with tags
+-   libav: return EOF when stream is out of data
+-   avdemux: Never return 0 from read function, which would lead to infinite loops
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   vaapi: Fix sps_max_dec_pic_buffering_minus1 value in h265 decoder
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   nlecomposition: Don’t leak QoS events
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   validate: Fix copying of action name
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   Add a meson option to disable gst-full for static-library build configurations that do not need this
+
+Cerbero build tool and packaging changes in 1.24.7
+
+-   Disable rust variant on Linux if binutils is too old
+-   Added 2022Server to the Windows platform distro map as Windows11
+-   libvpx: Update to 1.14.1
+
+Contributors to 1.24.7
+
+David Rosca, Edward Hervey, Guillaume Desmottes, Hou Qi, Jan Schmidt, Jesper Jensen, Jordan Petridis, Jordan Yelloz, L. E.
+Segovia, Lyra McMillan, Mathieu Duponchelle, Max Romanov, Nicolas Dufresne, Nirbheek Chauhan, Qian Hu (胡骞), Sebastian Dröge,
+Tim-Philipp Müller, Víctor Manuel Jáquez Leal,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.7
+
+-   List of Merge Requests applied in 1.24.7
+-   List of Issues fixed in 1.24.7
+
+1.24.8
+
+The eigth 1.24 bug-fix release (1.24.8) was released on 19 September 2024.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.8
+
+-   decodebin3: collection handling fixes
+-   encodebin: Fix pad removal (and smart rendering in gst-editing-services)
+-   glimagesink: Fix cannot resize viewport when video size changed in caps
+-   matroskamux, webmmux: fix firefox compatibility issue with Opus audio streams
+-   mpegtsmux: Wait for data on all pads before deciding on a best pad unless timing out
+-   splitmuxsink: Override LATENCY query to pretend to downstream that we’re not live
+-   video: QoS event handling improvements
+-   voamrwbenc: fix list of bitrates
+-   vtenc: Restart encoding session when certain errors are detected
+-   wayland: Fix ABI break in WL context type name
+-   webrtcbin: Prevent crash when attempting to set answer on invalid SDP
+-   cerbero: ship vp8/vp9 software encoders again, which went missing in 1.24.7; ship transcode plugin
+-   Various bug fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   clock: Fix unchecked overflows in linear regression code
+-   meta: Add missing include of gststructure.h
+-   pad: Check data NULL-ness when probes are stopped
+-   aggregator: Immediately return NONE from simple_get_next_time() on non-TIME segments
+
+gst-plugins-base
+
+-   decodebin3: Fix collection identity check
+-   encodebin: Fix pad removal
+-   glimagesink: Fix cannot resize viewport when video size changed in caps
+-   video: Don’t overshoot QoS earliest time by a factor of 2
+-   meson: gst-play: link to libm
+
+gst-plugins-good
+
+-   jackaudiosrc: actually use the queried ports from JACK
+-   matroskamux: Include end padding in the block duration for Opus streams, fixing firefox compatibility
+-   osxaudio: Avoid dangling pointer on shutdown
+-   splitmuxsink: Override LATENCY query to pretend to downstream that we’re not live
+-   v4l2bufferpool: actually queue back the empty buffer flagged LAST
+-   v4l2videoenc: unref buffer pool after usage properly
+-   v4l2: encoder: Add dynamic framerate support
+
+gst-plugins-bad
+
+-   GstPlay: Name the different bus
+-   GstPlay: check whether stream is seekable before seeking when state change
+-   GstPlayer: Check GstPlayerSignalDispatcher type
+-   mpegtsmux: Wait for data on all pads before deciding on a best pad unless timing out
+-   mpegtsmux: Fix refcounting issue when selecting the best pad
+-   uvcsink: fix caps event handling
+-   v4l2codecs: h265: Minimize memory allocation
+-   voamrwbenc: fix list of bitrates
+-   vtenc: Restart encoding session when certain errors are detected
+-   wayland: Fix ABI break in WL context type name
+-   webrtcbin: Prevent crash when attempting to set answer on invalid SDP
+-   wpe: fix gst-launch example
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   No changes
+
+gst-libav
+
+-   No changes
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   discoverer-manager: Fix race leading to assertion when stopping
+-   structured-interface: Fix memory leak of invalid fields GList
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   pad-monitor: Fix remaining pad function data handling
+-   pad-monitor: Fix pad function data properly
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   meson: Update openjpeg wrap to 2.5.2, fixes a warning
+
+Cerbero build tool and packaging changes in 1.24.8
+
+-   No vp8 / vp9 encoders packaged (regression)
+-   libvpx: Fix codec detection to fix vp8enc/vp9enc elements not being shipped
+-   gst-plugins-bad: Add missing transcode plugin
+
+Contributors to 1.24.8
+
+Andoni Morales Alastruey, Arun Raghavan, Benjamin Gaignard, Carlos Bentzen, Chao Guo, Edward Hervey, Francis Quiers, Guillaume
+Desmottes, Hou Qi, Jan Schmidt,, L. E. Segovia, Michael Tretter, Nicolas Dufresne, Nirbheek Chauhan, Peter Kjellerstedt,
+Philippe Normand, Piotr Brzeziński, Randy Li (ayaka), Sebastian Dröge, Thibault Saunier, Tim-Philipp Müller, Wim Taymans,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.8
+
+-   List of Merge Requests applied in 1.24.8
+-   List of Issues fixed in 1.24.8
+
+1.24.9
+
+The ninth 1.24 bug-fix release (1.24.9) was released on 30 October 2024.
+
+This release only contains bugfixes and a security fix and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.9
+
+-   gst-rtsp-server security fix
+-   GstAggregator start time selection and latency query fixes for force-live mode
+-   audioconvert: fix dynamic handling of mix matrix, and accept custom upstream event for setting one
+-   encodebin: fix parser selection for encoders that support multiple codecs
+-   flvmux improvments for pipelines where timestamps don’t start at 0
+-   glcontext: egl: Unrestrict the support base DRM formats
+-   kms: Add IMX-DCSS auto-detection in sink and fix stride with planar formats in allocator
+-   macOS main application event loop fixes
+-   mpegtsdemux: Handle PTS/DTS wraparound with ignore-pcr=true
+-   playbin3, decodebin3, parsebin, urisourcebin: fix races, and improve stability and stream-collection handling
+-   rtpmanager: fix early RTCP SR generation for sparse streams like metadata
+-   qml6glsrc: Reduce capture delay
+-   qtdemux: fix parsing of rotation matrix with 180 degree rotation
+-   rtpav1depay: added wait-for-keyframe and request-keyframe properties
+-   srt: make work with newer libsrt versions and don’t re-connect on authentication failure
+-   v4l2 fixes and improvement
+-   webrtcsink, webrtcbin and whepsrc fixes
+-   cerbero: fix Python 3.13 compatibility, g-i with newer setuptools, bootstrap on Arch Linux; iOS build fixes
+-   Ship qroverlay plugin in binary packages - Various bug fixes, memory leak fixes, and other stability and reliability
+    improvements
+-   Various bug fixes, build fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   aggregator: fix start time selection first with force-live
+-   aggregator: fix live query when force-live is TRUE
+-   parse-launch: Make sure children are bins before recursing in
+-   macos: Fix race conditions in cocoa/application main event loop
+-   multiqueue: Do not unref the query we get in pad->query
+
+gst-plugins-base
+
+-   audioconvert: fix dynamic handling of mix matrix, accept custom upstream event for setting one
+-   playback: Fix a variety of decodebin3/parsebin/urisourcebin races
+-   playbin3: prevent crashing trying to play a corrupted mp4 file (WARNING : HIGH PITCHED CORRUPTED SOUND)
+-   urisourcebin: Ensure all stream-start are handled
+-   urisourcebin: Allow more cases for posting stream-collection
+-   decodebin3: Make update/posting of collection messages atomic
+-   decodebin3: send selected stream message as long as not all the tracks can’t select decoders
+-   urisourcebin/parsebin: Improve collection creation and handling
+-   encodebasebin: Miscellaneous fixes
+-   allocators: drmdumb: Fix bpp value for P010
+-   gldownload: use gst_gl_sync_meta_wait_cpu()
+-   Revert “meson: Fix invalid include flag in uninstalled gl pc file”
+-   gl: Fix configure error when libdrm is a subproject
+-   glcontext: egl: Unrestrict the support base DRM formats
+-   exiftag: Check the result of gst_date_time_new_local_time(), fixes criticals with malformed EXIF tags
+
+gst-plugins-good
+
+-   flvmux: Use first running time on the initial header instead of 0
+-   rtpmanager: skip RTPSources which are not ready in the RTCP generation
+-   rtppassthroughpay: Fix reading clock-rate and payload type from caps
+-   qml6glsrc: Reduce capture delay
+-   qtdemux: fix parsing of matrix with 180 rotation
+-   qtdemux: Check fourcc of a second CEA608 atom instead of assuming it’s cdt2
+-   qtdemux: Skip zero-sized boxes instead of stopping to look at further boxes
+-   twcc: Handle wrapping of reference time
+-   v4l2object: append non colorimetry structure to probed caps
+-   v4l2: Various fixes and improvement
+
+gst-plugins-bad
+
+-   avfdeviceprovider: Fix leak from the GstCaps
+-   codecparsers: add debug categories to bitwriters
+-   codectimestamper: Fix gint wraparound in pts_compare_func
+-   dvxa: Explicitly use cpp_std=c++11
+-   GstPlay: message parsing and documentation improvements
+-   h26xbitwriter: false have_space if aligning fails on aud
+-   kmsallocator: fix stride with planar formats
+-   kmssink: Add IMX-DCSS auto-detection
+-   mpegtsdemux: Handle PTS/DTS wraparound with ignore-pcr=true
+-   rtmp2sink: Initialize base_ts / last_ts with the actual first observed timestamp
+-   scenechange: fix memory leak
+-   srtsink: Register SRT listen callback before binding socket
+-   srt: Don’t attempt to reconnect on authentication failures
+-   tests: va: fix vapostproc test for DMABuf
+-   tests: lc3: Allocate the same size for the buffer and the data
+-   va: Fix libdrm include, plus meson and wrap changes
+-   vaav1enc: Do not enable palette mode by default
+-   vp8decoder: Fix resolution change handling
+-   vtdec: add support for level 6 6.1 and 6.2
+-   wayland: Add NV15 support
+-   webrtcbin: Clean up bin elements when datachannel is removed
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   Build: turn lto off for dev profile for faster dev builds
+-   fmp4 hls_live example: Don’t set header-update-mode=update, no need to update the header in live mode
+-   gtk4paintablesink: Don’t check for a GL context when filtering dmabuf caps
+-   livesync: Log latency query results when handling latency query too
+-   onvifmetadatapay: Set output caps earlier, so upstream can send gap events earlier
+-   rtpav1depay: Add wait-for-keyframe and request-keyframe properties
+-   spotify: tweak dependencies
+-   transcriberbin: fix panic during gst-inspect-1.0 -u
+-   webrtcsink: fix segment format mismatch with remote offer
+-   webrtcsink: fix assertions when finalizing
+-   webrtcsink: Fix typo in “turn-servers” property description
+-   whepsrc: Fix incorrect default caps
+
+gst-libav
+
+-   avviddec: Unlock video decoder stream lock temporarily while finishing frames
+
+gst-rtsp-server
+
+-   rtsp-server: Remove pointless assertions that can happen if client provides invalid rates (security fix)
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges: Fix name of GESFrameCompositionMeta API type (which caused gobject-introspection failures at build time)
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   validate: Ignore flaky dash playbin3 issue
+-   validate: Blacklist more netsim tests
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.9
+
+-   Fix Python 3.13 compatibility
+-   gobject-introspection: Import patch to build against newer setuptools
+-   Switch from wget to curl on Fedora 40 and newer
+-   bootstrap: Add missing dependencies on Arch Linux
+-   harfbuzz: Add CXXFLAGS to fix broken build on iOS
+-   openssl.recipe: Stop using non-existent domain in primary tarball url
+-   gst-plugins-bad: ship qroverlay plugin
+
+Contributors to 1.24.9
+
+Andoni Morales Alastruey, Arun Raghavan, Benjamin Gaignard, Corentin Damman, Dave Lucia, Edward Hervey, Elliot Chen, eri,
+Francisco Javier Velázquez-García, Guillaume Desmottes, He Junyan, Hugues Fruchet, Jakub Adam, James Cowgill, Jan Alexander
+Steffens (heftig), Jan Schmidt, Johan Sternerup, Jordan Petridis, L. E. Segovia, Mathieu Duponchelle, Nick Steel, Nicolas
+Dufresne, Nirbheek Chauhan, Ognyan Tonchev, Olivier Crête, Peter Stensson, Philippe Normand, Piotr Brzeziński, Sanchayan Maity,
+Sebastian Dröge, Shengqi Yu, Stéphane Cerveau, Théo Maillart, Thibault Saunier, Tim-Philipp Müller, Víctor Manuel Jáquez Leal,
+Weijian Pan, Xavier Claessens,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.9
+
+-   List of Merge Requests applied in 1.24.9
+-   List of Issues fixed in 1.24.9
+
+1.24.10
+
+The tenth 1.24 bug-fix release (1.24.10) was released on 03 December 2024.
+
+This release only contains bugfixes and security fixes. It should be safe to update from 1.24.x and we would recommend you
+update at your earliest convenience.
+
+Highlighted bugfixes in 1.24.10
+
+-   More than 40 security fixes across a wide range of elements following an audit by the GitHub Security Lab, including the
+    MP4, Matroska, Ogg and WAV demuxers, subtitle parsers, image decoders, audio decoders and the id3v2 tag parser.
+-   avviddec: Fix regression that could trigger assertions about width/height mismatches
+-   appsink and appsrc fixes
+-   closed caption handling fixes
+-   decodebin3 and urisourcebin fixes
+-   glupload: dmabuf: Fix emulated tiled import
+-   level: fix LevelMeta values outside of the stated range
+-   mpegtsmux, flvmux: fix potential busy looping with high cpu usage in live mode
+-   pipeline dot file graph generation improvements
+-   qt(6): fix criticals with multiple qml(6)gl{src,sink}
+-   rtspsrc: Optionally timestamp RTP packets with their receive times in TCP/HTTP mode to enable clock drift handling
+-   splitmuxsrc: reduce number of file descriptors used
+-   systemclock: locking order fixes
+-   v4l2: fix possible v4l2videodec deadlock on shutdown; 8-bit bayer format fixes
+-   x265: Fix build with libx265 version >= 4.1 after masteringDisplayColorVolume API change
+-   macOS: fix rendering artifacts in retina displays, plus ptp clock fixes
+-   cargo: Default to thin lto for the release profile (for faster builds with lower memory requirements)
+-   Various bug fixes, build fixes, memory leak fixes, and other stability and reliability improvements
+-   Translation updates
+
+gstreamer
+
+-   allocator: Avoid integer overflow when allocating sysmem and avoid integer overflow in qtdemux theora extension parsing
+-   deviceprovider: fix leaking hidden providers
+-   gstreamer: prefix debug dot node names to prevent splitting
+-   pad: Never push sticky events in response to a FLUSH_STOP
+-   systemclock: Fix lock order violation and some cleanup
+-   utils: improve gst_util_ceil_log2()
+-   ptp: use ip_mreq instead of ip_mreqn for macos
+-   tracers: unlock leaks tracer if already tracking
+
+gst-plugins-base
+
+-   appsink: fix timeout logic for gst_app_sink_try_pull_sample()
+-   appsrc: Fix use-after-free when making buffer / buffer-lists writable
+-   audiostreamalign: Don’t report disconts for every buffer if alignment-threshold is too small
+-   decodebin3: Unify collection switching checks
+-   discoverer: Don’t print channel layout for more than 64 channels
+-   discoverer: Make sure the missing elements details array is NULL-terminated in a thread-safe way
+-   discoverer: fix segfault in race condition adding a new uri
+-   id3v2: Don’t try parsing extended header if not enough data is available
+-   glupload: dmabuf: Fix emulated tiled import
+-   gl: cocoa: fix rendering artifacts in retina displays
+-   gl: meson: Don’t use libdrm_dep in cc.has_header()
+-   oggstream: fix invalid ogg_packet->packet accesses, address invalid writes CVE
+-   opusdec: Set at most 64 channels to NONE position
+-   playbin: Fix caps leak in get_n_common_capsfeatures()
+-   playbin3: ERROR when setting new HLS URI with instant-uri=true
+-   sdp: Add debug categories for message and mikey modules
+-   ssaparse: Search for closing brace after opening brace
+-   splitmuxsrc: Convert part reader to a bin with a non-async bus
+-   subparse: Check for NULL return of strchr() when parsing LRC subtitles
+-   streamsynchronizer: Only send GAP events out of source pads
+-   urisourcebin: Also use event probe for HLS use-cases
+-   video-converter: Set TIME segment format on appsrc
+-   vorbisdec: Set at most 64 channels to NONE position
+-   Translation for gst-plugins-base 1.24.0 not sync-ed with Translation Project
+-   Update translations
+
+gst-plugins-good
+
+-   avisubtitle: Fix size checks and avoid overflows when checking sizes
+-   flvmux: Don’t time out in live mode if no timestamped next buffer is available
+-   gdkpixbufdec: Check if initializing the video info actually succeeded
+-   jpegdec: Directly error out on negotiation failures
+-   level: Fix integer overflow when filling LevelMeta
+-   level: produces level value outside of Stated Range
+-   matroskademux: header parsing fixes
+-   qtdemux: header and sample table parsing fixes
+-   qtdemux: avoid integer overflow in theora extension parsing
+-   qt(6)/material: ensure that we always update the context in setBuffer()
+-   rtspsrc: Optionally timestamp RTP packets with their receive times in TCP/HTTP mode
+-   rtp: Fix precision loss in gst_rtcp_ntp_to_unix()
+-   rtpfunnel: Ensure segment events are forwarded after flushs
+-   rtpmanager: don’t map READWRITE in twcc header ext
+-   rtph264depay, rtph265depay: Fix various OOB reads / NULL pointer dereferences in parameter-set string handling
+-   shout2send: Unref event at the end of the event function
+-   udpsrc: protect cancellable from unlock/unlock_stop race
+-   v4l2object: Fixed incorrect maximum value for int range
+-   v4l2object: Remove little endian marker on 8 bit bayer format names
+-   v4l2videodec: fix freeze race condition
+-   wavparse: Fix various (missing) size checks and other parsing problems
+
+gst-plugins-bad
+
+-   ccconverter: Don’t override in_fps_entry when trying to take output
+-   ccutils fixes
+-   kmssink: Add mediatek auto-detection
+-   mpegtsmux: Don’t time out in live mode if no timestamped next buffer is available (fixes busy loop with high cpu usage)
+-   mpegvideoparse: do not set delta unit flag on unknown frame type
+-   mxfmux: Fix off-by-one in the month when generating a timestamp for now
+-   timecodestamper: Don’t fail the latency query in LTC mode if we have no framerate
+-   webrtc: don’t crash on invalid bundle id
+-   x265: Allow building with x265-4.1 (after masteringDisplayColorVolume API change)
+-   meson: Don’t unconditionally invoke the libsoup subproject for tests
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   cargo: Default to thin lto for the release profile (for faster builds with lower memory requirements)
+
+gst-libav
+
+-   avcodecmap: Use avcodec_get_supported_config() instead of struct fields
+-   libav: viddec: provide details if meta has the wrong resolution
+-   avviddec: Unlock video decoder stream lock temporarily while finishing frames
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   No changes
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   validate: Fix leaks in ssim components
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   meson: Fix failing libva wrap file build
+
+Cerbero build tool and packaging changes in 1.24.10
+
+-   shell: fix TemporaryDirectory error with the with statement when ZSH
+-   ci: update macos CI to 15 Sequoia
+
+Contributors to 1.24.10
+
+Albert Sjolund, Alicia Boya García, Andoni Morales Alastruey, Antonio Morales, Edward Hervey, Guillaume Desmottes, Jan Alexander
+Steffens (heftig), Jan Schmidt, Jonas Rebmann, Jordan Petridis, Mathieu Duponchelle, Matthew Waters, Nicolas Dufresne, Nirbheek
+Chauhan, Pablo Sun, Philippe Normand, Robert Rosengren, Ruben Gonzalez, Sebastian Dröge, Seungmin Kim, Stefan Riedmüller,
+Stéphane Cerveau, Taruntej Kanakamalla, Théo Maillart, Thibault Saunier, Tim-Philipp Müller, Tomáš Polomský, Wilhelm Bartel, Xi
+Ruoyao,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.10
+
+-   List of Merge Requests applied in 1.24.10
+-   List of Issues fixed in 1.24.10
+
+1.24.11
+
+The eleventh 1.24 bug-fix release (1.24.11) was released on 06 January 2025.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.11
+
+-   playback: Fix SSA/ASS subtitles with embedded fonts
+-   decklink: add missing video modes and fix 8K video modes
+-   matroskamux: spec compliance fixes for audio-only files
+-   onnx: disable onnxruntime telemetry
+-   qtdemux: Fix base offset update when doing segment seeks
+-   srtpdec: Fix a use-after-free issue
+-   (uri)decodebin3: Fix stream change scenarios, possible deadlock on shutdown
+-   video: fix missing alpha flag in AV12 format description
+-   avcodecmap: Add some more channel position mappings
+-   cerbero bootstrap fixes for Windows 11
+-   Various bug fixes, build fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   No changes
+
+gst-plugins-base
+
+-   appsrc: Decrease log level for item drop
+-   gl: raise WARNING instead of ERROR when no connector is connected
+-   decodebin3: Free main input even if it is not part of the list of inputs
+-   urisourcebin: Avoid deadlock on shutdown
+-   urisourcebin: Only rewrite stream-start event once
+-   urisourcebin/(uri)decodebin3: Fix stream change scenarios
+-   urisourcebin: Reference counting leak
+-   playbin3: leak detected with A/V playback and window closed
+-   videodecoder: Gracefully handle missing data without prior input segment
+-   videodecoder: set decode only flag by decode only buffer
+-   video: fix AV12 format lacking the GST_VIDEO_FORMAT_FLAG_ALPHA flag.
+-   Fix SSA/ASS subtitles with embedded fonts
+
+gst-plugins-good
+
+-   matroskamux: Fix audio-only stream conditions and consider audio buffers as keyframes when writing out simpleblocks
+-   qtdemux: fix accumulated base offset in segment seeks
+-   rtppassthroughpay: ensure buffer is writable before mapping writable
+-   rtpsession: Fix twcc stats structure leak
+-   v4l2: object: Add P010 format
+-   v4l2videodec: release decode only frame in input list
+
+gst-plugins-bad
+
+-   decklink: add missing video modes, fix 8K video modes
+-   onnx: disable onnxruntime telemetry
+-   srtpdec: fix build when libsrtp1 is being used
+-   srtpdec: Fix a use-after-free buffer issue
+-   va: display: Optimize out some property indirection
+-   vp9parse/av1parse: Add video codec tag to the tag list
+-   webrtc: Simplify fmtp handling in codec stats
+-   webrtcbin: Fix potential deadlock on bin elements cleanup
+-   zxing: Replace deprecated DecodeHints with ReaderOptions
+-   meson: Also disable drm on GNU/Hurd
+
+gst-plugins-ugly
+
+-   No changes
+
+GStreamer Rust plugins
+
+-   No changes
+
+gst-libav
+
+-   avcodecmap: Add some more channel position mappings
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   meson: Re-added required: lines accidentally removed
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges: Fix some reference counting and error handling
+-   ges-meta-container: Fix the GET_INTERFACE macro
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   No changes
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.11
+
+-   Fix bootstrap on Windows 11: Install WMIC when missing
+
+Contributors to 1.24.11
+
+Armin Begovic, Benjamin Gräf, Cheung Yik Pang, Christian Meissl, Daniel Morin, Dean Zhang (张安迪), Edward Hervey, Emil
+Ljungdahl, Francisco Javier Velázquez-García, Guillaume Desmottes, Jan Alexander Steffens (heftig), L. E. Segovia, Matthew
+Waters, Max Romanov, Nicolas Dufresne, Philippe Normand, Robert Mader, Samuel Thibault, Sebastian Dröge, Stéphane Cerveau,
+Thibault Saunier, Tim-Philipp Müller,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.11
+
+-   List of Merge Requests applied in 1.24.11
+-   List of Issues fixed in 1.24.11
+
+1.24.12
+
+The twelfth 1.24 bug-fix release (1.24.12) was released on 29 January 2025.
+
+This release only contains bugfixes and it should be safe to update from 1.24.x.
+
+Highlighted bugfixes in 1.24.12
+
+-   d3d12: Fix shaders failing to compile with newer dxc versions
+-   decklinkvideosink: Fix handling of caps framerate in auto mode; also a decklinkaudiosink fix
+-   devicemonitor: Fix potential crash macOS when a device is unplugged
+-   gst-libav: Fix crash in audio encoders like avenc_ac3 if input data has insufficient alignment
+-   gst-libav: Fix build against FFmpeg 4.2 as in Ubuntu 20.04
+-   gst-editing-services: Fix Python library name fetching on Windows
+-   netclientclock: Don’t store failed internal clocks in the cache, so applications can re-try later
+-   oggdemux: Seeking and duration handling fixes
+-   osxaudiosrc: Fixes for failing init/no output on recent iOS versions
+-   qtdemux: Use mvhd transform matrix and support for flipping
+-   rtpvp9pay: Fix profile parsing
+-   splitmuxsrc: Fix use with decodebin3 which would occasionally fail with an assertion when seeking
+-   tsdemux: Fix backwards PTS wraparound detection with ignore-pcr=true
+-   video-overlay-composition: Declare the video/size/orientation tags for the meta and implement scale transformations
+-   vtdec: Fix seeks occasionally hanging on macOS due to a race condition when draining
+-   webrtc: Fix duplicate payload types with RTX and multiple video codecs
+-   win32-pluginloader: Make sure not to create any windows when inspecting plugins
+-   wpe: Various fixes for re-negotiation, latency reporting, progress messages on startup
+-   x264enc: Add missing data to AvcDecoderConfigurationRecord in codec_data for high profile variants
+-   cerbero: Support using ccache with cmake if enabled
+-   Various bug fixes, build fixes, memory leak fixes, and other stability and reliability improvements
+
+gstreamer
+
+-   device: Fix racy nullptr deref on macOS when a device is unplugged
+-   iterator: Added error handling to filtered iterators
+-   netclientclock: Don’t ever store failed internal clocks in the cache
+-   netclock-replay: use gst_c_args when building, fixing build failure on Solaris
+-   pluginloader-win32: create no window
+-   pluginloader-win32: fix use after free in find_helper_bin_location
+-   sparsefile: ensure error is set when read_buffer() returns 0
+-   basetransform: fix incorrect logging inside gst_base_transform_query_caps
+
+gst-plugins-base
+
+-   oggdemux: fixes seeking in some cases by not overwriting a valid duration with CLOCK_TIME_NONE
+-   video-overlay-composition: Declare the video/size/orientation tags for the meta & implement scale transformation
+-   Various fixes found from adding extra warning flags
+
+gst-plugins-good
+
+-   osxaudiosrc: Fixes for failing init/no output on recent iOS versions
+-   qtdemux: Use mvhd transform matrix and support for flipping
+-   qtmux: fix critical warnings on negotiation error
+-   rtpvp9pay: fix profile parsing
+-   splitmuxsrc: Ensure only a single stream-start event is pushed
+-   splitmuxsrc: decodebin3 Fails with assertion in mq_slot_handle_stream_start when seeking
+-   Various fixes found from adding extra warning flags
+
+gst-plugins-bad
+
+-   decklinkvideosink: Fix handling of caps framerate in auto mode
+-   decklinkaudiosink: Don’t crash if started without corresponding video sink
+-   d3d12: Fix shaders failing to compile with newer dxc versions
+-   tsdemux: Fix backwards PTS wraparound detection with ignore-pcr=true
+-   vtdec: fix seeks hangs due to a race condition draining
+-   vtdec: seeks freeze the pipeline
+-   wayland: Print table split when DMABuf format changes
+-   webrtc: fix duplicate payload types with RTX and multiple video codecs
+-   wpevideosrc: Clear cached SHM buffers after caps re-negotiation
+-   wpe: Report latency and start-up progress messages
+-   wpe: remove glFlush() when filling buffer
+-   Fix build with gtk3 but not wayland
+-   Various fixes found from adding extra warning flags
+
+gst-plugins-ugly
+
+-   x264enc: add missing data to AvcDecoderConfigurationRecord, and switch to GstByteWriter
+
+GStreamer Rust plugins
+
+-   No changes
+
+gst-libav
+
+-   avaudenc: fix crash in avenc_ac3 if input buffers are insufficiently aligned
+-   avcodecmap: Only use new channel positions when compiling against new enough ffmpeg
+-   gst-libav: 1.24.11: Fails to build with minimum supported ffmpeg version
+
+gst-rtsp-server
+
+-   No changes
+
+gstreamer-vaapi
+
+-   No changes
+
+gstreamer-sharp
+
+-   No changes
+
+gst-omx
+
+-   No changes
+
+gst-python
+
+-   No changes
+
+gst-editing-services
+
+-   ges: Fix Python library name fetching on Windows
+
+gst-devtools, gst-validate + gst-integration-testsuites
+
+-   No changes
+
+gst-examples
+
+-   No changes
+
+Development build environment
+
+-   No changes
+
+Cerbero build tool and packaging changes in 1.24.12
+
+-   cmake: Support using ccache if enabled
+
+Contributors to 1.24.12
+
+Adrian Perez de Castro, Alan Coopersmith, Alexander Slobodeniuk, Andoni Morales Alastruey, Andrew Yooeun Chun, Brad Hards,
+Carlos Bentzen, Colin Kinloch, Edward Hervey, François Laignel, Guillaume Desmottes, Jochen Henneberg, Jordan Yelloz, L. E.
+Segovia, Monty C, Nirbheek Chauhan, Philippe Normand, Piotr Brzeziński, Rares Branici, Samuel Thibault, Sebastian Dröge, Silvio
+Lazzeretti, Tim-Philipp Müller, Tomas Granath, Will Miller,
+
+… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all!
+
+List of merge requests and issues fixed in 1.24.12
+
+-   List of Merge Requests applied in 1.24.12
+-   List of Issues fixed in 1.24.12
+
+Schedule for 1.26
+
+Our next major feature release will be 1.26, and 1.25 will be the unstable development version leading up to the stable 1.26
+release. The development of 1.25/1.25 will happen in the git main branch of the GStreamer mono repository.
+
+The schedule for 1.26 is yet to be decided.
+
+1.26 will be backwards-compatible to the stable 1.24, 1.22, 1.20, 1.18, 1.16, 1.14, 1.12, 1.10, 1.8, 1.6, 1.4, 1.2 and 1.0
+release series.
+
+--------------------------------------------------------------------------------------------------------------------------------
+
+These release notes have been prepared by Tim-Philipp Müller with contributions from Edward Hervey, Matthew Waters, Nicolas
+Dufresne, Nirbheek Chauhan, Olivier Crête, Sebastian Dröge, Seungha Yang, Thibault Saunier, and Víctor Manuel Jáquez Leal.
 
 License: CC BY-SA 4.0
diff --git a/README.md b/README.md
index 79e1942292e555615b007bd3371b11d6aaa1cc6f..eb6c080630cd813cc4cbd123391487151e6a68d3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-GStreamer 1.20.x stable series
+GStreamer 1.24.x stable series
 
 WHAT IT IS
 ----------
@@ -16,17 +16,17 @@ Our documentation, including tutorials, API reference and FAQ can be found at
 
   https://gstreamer.freedesktop.org/documentation/
 
-You can subscribe to our mailing lists:
+You can ask questions on the GStreamer Discourse at
 
-  https://lists.freedesktop.org/mailman/listinfo/gstreamer-announce
-
-  https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
+  https://discourse.gstreamer.org/
 
 We track bugs, feature requests and merge requests (patches) in GitLab at
 
   https://gitlab.freedesktop.org/gstreamer/
 
-You can join us on IRC - #gstreamer on irc.oftc.net
+You can join us on our Matrix room at
+
+  https://matrix.to/#/#gstreamer:gstreamer.org
 
 GStreamer 1.0 series
 --------------------
diff --git a/RELEASE b/RELEASE
index 378b7ffb0324145a0c7e67b1abde170937ec2bbc..60fa97f76661db3a83a98ec8a7335513e06649d4 100644
--- a/RELEASE
+++ b/RELEASE
@@ -1,17 +1,17 @@
-This is GStreamer gst-plugins-good 1.22.0.
+This is GStreamer gst-plugins-good 1.24.12.
 
 The GStreamer team is thrilled to announce a new major feature release
 of your favourite cross-platform multimedia framework!
 
 As always, this release is again packed with new features, bug fixes and
 other improvements.
-
-The 1.22 release series adds new features on top of the 1.20 series and is
+ 
+The 1.24 release series adds new features on top of the 1.22 series and is
 part of the API and ABI-stable 1.x release series.
 
 Full release notes can be found at:
 
-  https://gstreamer.freedesktop.org/releases/1.22/
+  https://gstreamer.freedesktop.org/releases/1.24/
 
 Binaries for Android, iOS, Mac OS X and Windows will usually be provided
 shortly after the release.
@@ -44,10 +44,7 @@ with other GStreamer modules for a complete multimedia experience.
 
  - gstreamer-vaapi: hardware-accelerated video decoding and encoding using
                     VA-API on Linux. Primarily for Intel graphics hardware.
-
- - gst-omx: hardware-accelerated video decoding and encoding, primarily for
-                    embedded Linux systems that provide an OpenMax
-                    implementation layer such as the Raspberry Pi.
+                    (Deprecated, use the new "va" plugin instead)
 
  - gst-rtsp-server: library to serve files or streaming pipelines via RTSP
 
@@ -80,14 +77,18 @@ Please submit patches via GitLab as well, in form of Merge Requests. See
 
 for more details.
 
-For help and support, please subscribe to and send questions to the
-gstreamer-devel mailing list (see below for details).
+For help and support, please head over to our Discourse forum at
 
-There is also a #gstreamer IRC channel on the OFTC IRC network, which is
-also bridged into the Matrix network.
+  https://discourse.gstreamer.org/
 
-Please do not submit support requests in GitLab, we only use it
-for bug tracking and merge requests review.
+or pop into one of our Matrix chat rooms, see
+
+  https://discourse.gstreamer.org/t/new-gstreamer-matrix-chat-space/675
+
+for more details.
+
+Please do not submit support requests in GitLab, we only use it for
+bug tracking and merge requests review. Use the Discourse forum instead.
 
 ==== Developers ====
 
@@ -99,6 +100,9 @@ and can also be cloned from there and this is also where you can submit
 Merge Requests or file issues for bugs or feature requests.
 
 Interested developers of the core library, plugins, and applications should
-subscribe to the gstreamer-devel list:
+join us on Matrix for chat and the Discourse forum for announcements, help
+and discussions.
+
+There is also a gstreamer-devel mailing list, but Discourse is preferred:
 
   https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
diff --git a/REQUIREMENTS b/REQUIREMENTS
index ee00920be4679e399619d843c1e4e73565a8eb8f..f16431046e05ff0dab1b2d091baccba7d930ab5e 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -51,6 +51,10 @@ This file lists supporting libraries for which gst-plugins-good contains
 plugins, as well as their minimum required version. You can find the
 corresponding plugins in ext/(library)
 
+Package:        opencore-amr (for the AMR-NB decoder and encoder and the AMR-WB decoder)
+URL:            http://sourceforge.net/projects/opencore-amr/
+DebianPackage:  libopencore-amrnb-dev
+
 Package:        Orc
 Version:        >= 0.4.17
 Recommended:    Latest 0.4.x
diff --git a/debian/apertis/copyright b/debian/apertis/copyright
index b94f3668719adfb4d3d19842a0f4438800600096..c19289b045745b8412e09defda497007110702d0 100644
--- a/debian/apertis/copyright
+++ b/debian/apertis/copyright
@@ -9,27 +9,22 @@ Copyright: no-info-found
 License: CC-BY-SA-4.0
 
 Files: ext/*
-Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+Copyright: 2012, 2019, Collabora Ltd. <tim.muller@collabora.co.uk>
+License: LGPL-2.1+
+
+Files: ext/aalib/*
+Copyright: <2019> Eric Marks <bigmarkslp@gmail.com>
 License: LGPL-2+
 
 Files: ext/aalib/gstaaplugin.c
 Copyright: 2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
 
-Files: ext/aalib/gstaatv.c
- ext/aalib/gstaatv.h
-Copyright: <2019> Eric Marks <bigmarkslp@gmail.com>
-License: LGPL-2+
-
-Files: ext/adaptivedemux2/*
-Copyright: 2021, 2022, Centricular Ltd
- 2014, Samsung Electronics.
+Files: ext/aalib/gstaasink.c
+ ext/aalib/gstaasink.h
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
-Files: ext/adaptivedemux2/dash/*
-Copyright: 2012, 2019, Collabora Ltd.
-License: LGPL-2.1+
-
 Files: ext/adaptivedemux2/dash/gstdashdemux.c
 Copyright: 2012, Orange
 License: LGPL-2.1+
@@ -54,7 +49,7 @@ Files: ext/adaptivedemux2/downloadhelper.c
  ext/adaptivedemux2/gstadaptivedemuxelement.c
  ext/adaptivedemux2/gstadaptivedemuxelements.h
  ext/adaptivedemux2/plugin.c
-Copyright: 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
+Copyright: 2011, 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
 
 Files: ext/adaptivedemux2/downloadrequest.c
@@ -67,6 +62,17 @@ Files: ext/adaptivedemux2/gstadaptivedemux-period.c
 Copyright: 2017, 2021, 2022, Centricular Ltd
 License: LGPL-2+
 
+Files: ext/adaptivedemux2/gstadaptivedemux-private.h
+ ext/adaptivedemux2/gstadaptivedemux-stream.c
+ ext/adaptivedemux2/gstadaptivedemux-stream.h
+ ext/adaptivedemux2/gstadaptivedemux-track.c
+ ext/adaptivedemux2/gstadaptivedemux-types.h
+ ext/adaptivedemux2/gstadaptivedemux.c
+ ext/adaptivedemux2/gstadaptivedemux.h
+Copyright: 2021, 2022, Centricular Ltd
+ 2014, Samsung Electronics.
+License: LGPL-2+
+
 Files: ext/adaptivedemux2/gstadaptivedemuxutils.c
  ext/adaptivedemux2/gstadaptivedemuxutils.h
 Copyright: 2021, 2022, Jan Schmidt <jan@centricular.com>
@@ -78,12 +84,19 @@ Files: ext/adaptivedemux2/gstisoff.c
 Copyright: 2015, Samsung Electronics.
 License: LGPL-2.1+
 
-Files: ext/adaptivedemux2/hls/*
-Copyright: 2016, Tim-Philipp Müller <tim@centricular.com>
- 2016, Jan Schmidt <jan@centricular.com>
+Files: ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c
+ ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h
+Copyright: 2021, 2022, Centricular Ltd
+ 2014, Samsung Electronics.
 License: LGPL-2+
 
-Files: ext/adaptivedemux2/hls/gsthlsdemux.c
+Files: ext/adaptivedemux2/hls/gsthlsdemux-preloader.c
+ ext/adaptivedemux2/hls/gsthlsdemux-preloader.h
+Copyright: 2011, 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
+License: LGPL-2+
+
+Files: ext/adaptivedemux2/hls/gsthlsdemux-stream.c
+ ext/adaptivedemux2/hls/gsthlsdemux.c
 Copyright: 2021, 2022, Centricular Ltd
  2015, Tim-Philipp Müller <tim@centricular.com>
  2014, Sebastian Dröge <sebastian@centricular.com>
@@ -92,12 +105,18 @@ Copyright: 2021, 2022, Centricular Ltd
  2010, Andoni Morales Alastruey <ylatuya@gmail.com>
 License: LGPL-2+
 
-Files: ext/adaptivedemux2/hls/gsthlsdemux.h
+Files: ext/adaptivedemux2/hls/gsthlsdemux-stream.h
+ ext/adaptivedemux2/hls/gsthlsdemux.h
 Copyright: 2015, Tim-Philipp Müller <tim@centricular.com>
  2010, Marc-Andre Lureau <marcandre.lureau@gmail.com>
  2010, Andoni Morales Alastruey <ylatuya@gmail.com>
 License: LGPL-2+
 
+Files: ext/adaptivedemux2/hls/gsthlsdemux-util.c
+Copyright: 2016, Tim-Philipp Müller <tim@centricular.com>
+ 2016, Jan Schmidt <jan@centricular.com>
+License: LGPL-2+
+
 Files: ext/adaptivedemux2/hls/m3u8.c
 Copyright: 2021, 2022, Centricular Ltd
  2015, Tim-Philipp Müller <tim@centricular.com>
@@ -131,6 +150,19 @@ Copyright: 2016, Metrological
  2012, Smart TV Alliance
 License: LGPL-2+
 
+Files: ext/amrnb/*
+Copyright: 2001-2004, Ronald Bultje <rbultje@ronald.bitfreak.net>
+License: LGPL-2+
+
+Files: ext/amrwbdec/amrwb.c
+ ext/amrwbdec/amrwbdec.h
+Copyright: 2006, Edgard Lima <edgard.lima@gmail.com>
+License: LGPL-2+
+
+Files: ext/amrwbdec/amrwbdec.c
+Copyright: 2001-2004, Ronald Bultje <rbultje@ronald.bitfreak.net>
+License: LGPL-2+
+
 Files: ext/cairo/*
 Copyright: <2011> Jon Nordby <jononor@gmail.com>
 License: LGPL-2+
@@ -140,6 +172,10 @@ Copyright: <2003,2004> David Schleef <ds@schleef.org>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
+Files: ext/dv/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: ext/dv/gstdvelements.h
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
  2020, Huawei Technologies Co., Ltd.
@@ -150,6 +186,10 @@ Files: ext/dv/gstsmptetimecode.c
 Copyright: 2006, 2007, 2009, David A. Schleef <ds@schleef.org>
 License: LGPL-2+
 
+Files: ext/flac/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: ext/flac/gstflacdec.c
 Copyright: <2006> Jan Schmidt <thaytan at mad scientist com>
  <2006,2011> Tim-Philipp Müller <tim centricular net>
@@ -167,7 +207,7 @@ License: LGPL-2+
 
 Files: ext/flac/gstflactag.c
  ext/flac/gstflactag.h
-Copyright: 2008, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2008, Sebastian Dröge <slomo@circular-chaos.org>
  2008, Jonathan Matthew <jonathan@d14n.org>
  2003, Christophe Fergeau <teuf@gnome.org>
 License: LGPL-2+
@@ -179,7 +219,7 @@ License: LGPL-2+
 
 Files: ext/gdk_pixbuf/gstgdkanimation.c
  ext/gdk_pixbuf/gstgdkanimation.h
-Copyright: 2003, Benjamin Otte <in7y118@public.uni-hamburg.de>
+Copyright: 2003, 2004, Benjamin Otte <otte@gnome.org>
 License: LGPL-2+
 
 Files: ext/gdk_pixbuf/gstgdkpixbufelements.h
@@ -196,45 +236,58 @@ Copyright: 2006-2010, 2012-2014, 2020, Tim-Philipp Müller <tim centricular net>
 License: LGPL-2+
 
 Files: ext/gtk/*
-Copyright: 2014, 2015, 2020-2022, Matthew Waters <matthew@centricular.com>
+Copyright: 2014, 2015, 2020-2023, Matthew Waters <matthew@centricular.com>
 License: LGPL-2+
 
 Files: ext/gtk/gstgtkutils.c
  ext/gtk/gstgtkutils.h
-Copyright: 2015, Thibault Saunier <tsaunier@gnome.org>
+Copyright: 2015, Thibault Saunier <tsaunier@igalia.com>
  2015, Matthew Waters <matthew@centricular.com>
 License: LGPL-2+
 
 Files: ext/jack/*
-Copyright: 2004-2006, Wim Taymans <wim@fluendo.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: ext/jack/gstjackaudiosrc.c
  ext/jack/gstjackaudiosrc.h
 Copyright: 2008, Tristan Matthews <tristan@sat.qc.ca>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
+
+Files: ext/jack/gstjackloader.c
+Copyright: 2023, Jordan Petridis <jordan@centricular.com>
+License: LGPL-2+
+
+Files: ext/jack/gstjackloader.h
+Copyright: 2003, 2004, Jack O'Quin
+ 2001, Paul Davis
+License: LGPL-2.1+
 
 Files: ext/jack/gstjackringbuffer.h
 Copyright: 2008, Tristan Matthews <tristan@sat.qc.ca>
- 2006, Wim Taymans <wim@fluendo.com>
-License: Expat and/or LGPL-2+
+ 2006, Wim Taymans <wim.taymans@gmail.com>
+License: Expat or LGPL-2+
 
 Files: ext/jack/gstjackutil.c
  ext/jack/gstjackutil.h
 Copyright: 2010, Tristan Matthews <tristan@sat.qc.ca>
 License: LGPL-2+
 
+Files: ext/jpeg/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: ext/jpeg/gstjpegdec.c
 Copyright: <2009> Tim-Philipp Müller <tim centricular net>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2012, Collabora Ltd.
+ 2012, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: ext/jpeg/gstjpegdec.h
  ext/jpeg/gstjpegenc.c
  ext/jpeg/gstjpegenc.h
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2012, Collabora Ltd.
+ 2012, 2013, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: ext/jpeg/gstjpegelements.h
@@ -248,7 +301,7 @@ Files: ext/jpeg/smokecodec.c
 Copyright: <2004> Wim Taymans <wim@fluendo.com>
 License: LGPL-2+
 
-Files: ext/lame/*
+Files: ext/lame/gstlamemp3enc.c
 Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
  <2005> Thomas Vander Stichele <thomas at apestaart dot org>
  <2004> Wim Taymans <wim@fluendo.com>
@@ -264,6 +317,10 @@ Files: ext/lame/plugin.c
 Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
 License: LGPL-2+
 
+Files: ext/libcaca/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: ext/libcaca/gstcacatv.c
  ext/libcaca/gstcacatv.h
 Copyright: <2019> Eric Marks <bigmarkslp@gmail.com>
@@ -271,7 +328,7 @@ License: LGPL-2+
 
 Files: ext/libpng/*
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2012, Collabora Ltd.
+ 2012, 2013, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: ext/libpng/gstpng.c
@@ -280,19 +337,14 @@ Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
  2000, Donald A. Graft
 License: LGPL-2+
 
-Files: ext/libpng/gstpngdec.c
-Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2012, 2013, Collabora Ltd.
-License: LGPL-2+
-
 Files: ext/libpng/gstpngenc.c
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2012, Collabora Ltd.
+ 2012, Collabora Ltd. <tim.muller@collabora.co.uk>
  2000, Donald A. Graft
 License: LGPL-2+
 
 Files: ext/mpg123/*
-Copyright: 2012, Carlos Rafael Giani
+Copyright: 2012, Carlos Rafael Giani <dv@pseudoterminal.org>
 License: LGPL-2.1+
 
 Files: ext/pulse/*
@@ -310,12 +362,12 @@ Copyright: 2012, Olivier Crete <olivier.crete@collabora.com>
 License: LGPL-2+
 
 Files: ext/pulse/pulsesink.c
-Copyright: 2009, Wim Taymans
+Copyright: 2009, Wim Taymans <wim.taymans@gmail.com>
  2004-2008, Lennart Poettering
 License: LGPL-2.1+
 
 Files: ext/qt/*
-Copyright: 2014, 2015, 2020-2022, Matthew Waters <matthew@centricular.com>
+Copyright: 2014, 2015, 2020-2023, Matthew Waters <matthew@centricular.com>
 License: LGPL-2+
 
 Files: ext/qt/gstqtelements.h
@@ -331,26 +383,34 @@ Files: ext/qt/gstqtglutility.cc
 Copyright: 2016, Freescale Semiconductor, Inc.
 License: LGPL-2+
 
-Files: ext/qt/qtglrenderer.h
-Copyright: 2020, Matthew Waters <matthew@cenricular.com>
+Files: ext/qt6/*
+Copyright: 2014, 2015, 2020-2023, Matthew Waters <matthew@centricular.com>
 License: LGPL-2+
 
-Files: ext/qt6/*
-Copyright: 2014, 2015, 2020-2022, Matthew Waters <matthew@centricular.com>
+Files: ext/qt6/gstqml6glsrc.cc
+ ext/qt6/qt6glwindow.cc
+ ext/qt6/qt6glwindow.h
+Copyright: 2022, Matthew Waters <matthew@centricular.com>
+ 2016, Freescale Semiconductor, Inc.
+License: LGPL-2+
+
+Files: ext/qt6/gstqml6glsrc.h
+ ext/qt6/gstqt6glutility.cc
+ ext/qt6/gstqt6glutility.h
+Copyright: 2016, Freescale Semiconductor, Inc.
 License: LGPL-2+
 
 Files: ext/qt6/gstqt6elements.h
 Copyright: 2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
 
-Files: ext/qt6/gstqt6glutility.cc
- ext/qt6/gstqt6glutility.h
-Copyright: 2016, Freescale Semiconductor, Inc.
+Files: ext/raw1394/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
 Files: ext/raw1394/gst1394clock.c
  ext/raw1394/gst1394clock.h
-Copyright: 2009, David Schleef <ds@schleef.org>
+Copyright: 2009, David Schleef <ds@entropywave.com>
  1999, 2000, Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
@@ -370,21 +430,15 @@ Copyright: <2012> Ralph Giles <giles@mozilla.com>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
-Files: ext/soup/*
-Copyright: 2019, 2021, Igalia S.L.
+Files: ext/shout2/gstshout2.h
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
-Files: ext/soup/gstsoup.c
- ext/soup/gstsouphttpsrc.c
- ext/soup/gstsouphttpsrc.h
+Files: ext/soup/*
 Copyright: 2021, Igalia S.L.
  2007, 2008, Wouter Cloetens <wouter@mind.be>
 License: LGPL-2+
 
-Files: ext/soup/gstsoupelement.c
-Copyright: 2007, 2008, Wouter Cloetens <wouter@mind.be>
-License: LGPL-2+
-
 Files: ext/soup/gstsoupelements.h
 Copyright: 2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
@@ -395,15 +449,28 @@ Copyright: 2021, Igalia S.L.
  2011, David Schleef <ds@entropywave.com>
 License: LGPL-2+
 
+Files: ext/soup/gstsouploader.c
+ ext/soup/gstsouploader.h
+Copyright: 2019, 2021, Igalia S.L.
+License: LGPL-2+
+
 Files: ext/soup/gstsouputils.c
  ext/soup/gstsouputils.h
 Copyright: 2021, Igalia S.L.
  2014, Samsung Electronics.
 License: LGPL-2+
 
+Files: ext/soup/stub/*
+Copyright: 2019, 2021, Igalia S.L.
+License: LGPL-2+
+
+Files: ext/speex/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: ext/speex/gstspeexdec.c
 Copyright: 2006, Tim-Philipp Müller <tim centricular net>
- 2004, Wim Taymans <wim@fluendo.com>
+ 2004, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: ext/speex/gstspeexelements.h
@@ -416,17 +483,11 @@ Copyright: 2006, Tim-Philipp Müller <tim centricular net>
  2006, Christophe Fergeau <teuf@gnome.org>
 License: LGPL-2+
 
-Files: ext/taglib/gstid3v2mux.cc
- ext/taglib/gstid3v2mux.h
-Copyright: 2006, Tim-Philipp Müller <tim centricular net>
- 2006, Christophe Fergeau <teuf@gnome.org>
-License: LGPL-2+
-
 Files: ext/taglib/gsttaglibelements.h
 Copyright: 2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
 
-Files: ext/twolame/*
+Files: ext/twolame/gsttwolamemp2enc.c
 Copyright: <2008> Sebastian Dröge <sebastian.droege@collabora.co.uk>
  <2005> Thomas Vander Stichele <thomas at apestaart dot org>
  <2004> Wim Taymans <wim@fluendo.com>
@@ -439,40 +500,25 @@ Copyright: <2008> Sebastian Dröge <sebastian.droege@collabora.co.uk>
 License: LGPL-2+
 
 Files: ext/vpx/*
-Copyright: 2010-2012, Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2008-2010, Entropy Wave Inc
- 2006, David Schleef <ds@schleef.org>
-License: LGPL-2+
-
-Files: ext/vpx/gstvp9dec.c
- ext/vpx/gstvp9dec.h
- ext/vpx/gstvp9enc.c
- ext/vpx/gstvp9enc.h
 Copyright: 2010-2013, Sebastian Dröge <slomo@circular-chaos.org>
  2008-2010, Entropy Wave Inc
- 2006, David Schleef <ds@schleef.org>
+ 2006, David Schleef <ds@entropywave.com>
 License: LGPL-2+
 
 Files: ext/vpx/gstvpxcompat.h
 Copyright: 2022, Seungha Yang <seungha@centricular.com>
 License: LGPL-2+
 
-Files: ext/vpx/gstvpxelement.c
- ext/vpx/plugin.c
-Copyright: 2010, Entropy Wave Inc
- 2006, David Schleef <ds@schleef.org>
-License: LGPL-2+
-
 Files: ext/vpx/gstvpxelements.h
 Copyright: 2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
 
 Files: ext/vpx/gstvpxenums.h
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: ext/wavpack/*
-Copyright: 2006-2008, Sebastian Dröge <slomo@circular-chaos.org>
+Copyright: 2006-2010, Sebastian Dröge <slomo@circular-chaos.org>
 License: LGPL-2+
 
 Files: ext/wavpack/gstwavpack.c
@@ -503,11 +549,7 @@ Copyright: 2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
 
 Files: gst-libs/*
-Copyright: 2005, 2010, David Schleef <ds@schleef.org>
-License: LGPL-2+
-
-Files: gst/*
-Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+Copyright: 2005, 2010, David Schleef <ds@entropywave.com>
 License: LGPL-2+
 
 Files: gst/alpha/*
@@ -517,12 +559,8 @@ Copyright: <2007> Wim Taymans <wim.taymans@collabora.co.uk>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
-Files: gst/alpha/gstalphacolor.c
-Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
-License: LGPL-2+
-
 Files: gst/alpha/gstalphacolor.h
-Copyright: 2004-2006, Wim Taymans <wim@fluendo.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/apetag/*
@@ -531,26 +569,21 @@ Copyright: 2006, Tim-Philipp Müller <tim centricular net>
 License: LGPL-2+
 
 Files: gst/audiofx/*
-Copyright: 2007-2009, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2006-2010, Sebastian Dröge <slomo@circular-chaos.org>
 License: LGPL-2+
 
 Files: gst/audiofx/audioamplify.c
  gst/audiofx/audioamplify.h
  gst/audiofx/audioinvert.c
  gst/audiofx/audioinvert.h
-Copyright: 2007, Sebastian Dröge <slomo@circular-chaos.org>
- 2006, Stefan Kost <ensonic@users.sf.net>
-License: LGPL-2+
-
-Files: gst/audiofx/audiodynamic.c
- gst/audiofx/audiodynamic.h
- gst/audiofx/math_compat.h
-Copyright: 2006-2008, Sebastian Dröge <slomo@circular-chaos.org>
+ gst/audiofx/audiopanorama.c
+Copyright: 2006, Stefan Kost <stefan.kost@nokia.com>
+ 2006, 2007, Sebastian Dröge <slomo@circular-chaos.org>
 License: LGPL-2+
 
 Files: gst/audiofx/audiofx.c
  gst/audiofx/audiopanorama.h
-Copyright: 2006, 2008, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2006, 2008, 2010, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
 Files: gst/audiofx/audiofxbasefirfilter.c
@@ -564,12 +597,7 @@ License: LGPL-2+
 
 Files: gst/audiofx/audiokaraoke.c
  gst/audiofx/audiokaraoke.h
-Copyright: 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
-License: LGPL-2+
-
-Files: gst/audiofx/audiopanorama.c
-Copyright: 2006, Stefan Kost <ensonic@users.sf.net>
- 2006, Sebastian Dröge <slomo@circular-chaos.org>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/audiofx/gstscaletempo.c
@@ -579,7 +607,7 @@ License: LGPL-2+
 
 Files: gst/audiofx/gstscaletempoplugin.c
 Copyright: 2008, Rov Juvano <rovjuvano@users.sourceforge.net>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/audiofx/gststereo.c
  gst/audiofx/gststereo.h
@@ -599,7 +627,7 @@ Files: gst/audioparsers/gstac3parse.c
  gst/audioparsers/gstac3parse.h
 Copyright: 2009, Tim-Philipp Müller <tim centricular net>
  2009, Nokia Corporation.
- 2009, Mark Nauwelaerts <mnauw users sf net>
+ 2009, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
 License: LGPL-2+
 
 Files: gst/audioparsers/gstamrparse.c
@@ -628,12 +656,12 @@ Files: gst/audioparsers/gstmpegaudioparse.c
  gst/audioparsers/gstmpegaudioparse.h
 Copyright: 2010, Nokia Corporation.
  2010, Mark Nauwelaerts <mnauw users sf net>
- 2006, 2007, Jan Schmidt <thaytan@mad.scientist.com>
+ 2006, 2007, Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
 
 Files: gst/audioparsers/gstsbcparse.c
  gst/audioparsers/gstsbcparse.h
-Copyright: 2011, 2012, Collabora Ltd. <tim.muller@collabora.co.uk>
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: gst/audioparsers/gstwavpackparse.c
@@ -653,20 +681,20 @@ License: LGPL-2+
 
 Files: gst/autodetect/gstautoaudiosink.c
  gst/autodetect/gstautovideosink.c
-Copyright: 2006, Jan Schmidt <thaytan@noraisin.net>
+Copyright: 2006, Jan Schmidt <jan@centricular.com>
  2005, Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/autodetect/gstautoaudiosrc.c
  gst/autodetect/gstautovideosrc.c
 Copyright: 2008, Stefan Kost <ensonic@users.sf.net>
- 2006, Jan Schmidt <thaytan@noraisin.net>
+ 2006, Jan Schmidt <jan@centricular.com>
  2005, Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/autodetect/gstautoaudiosrc.h
  gst/autodetect/gstautovideosrc.h
-Copyright: 2008, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2008, Stefan Kost <stefan.kost@nokia.com>
  2005, Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
@@ -675,11 +703,6 @@ Copyright: 2020, Huawei Technologies Co., Ltd.
  2005, Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
-Files: gst/avi/*
-Copyright: <1999> Erik Walthinsen <omega@temple-baptist.com>
- 2020, Huawei Technologies Co., Ltd.
-License: LGPL-2+
-
 Files: gst/avi/avi-ids.h
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
@@ -699,13 +722,19 @@ Copyright: <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
+Files: gst/avi/gstavielement.c
+ gst/avi/gstavielements.h
+Copyright: <1999> Erik Walthinsen <omega@temple-baptist.com>
+ 2020, Huawei Technologies Co., Ltd.
+License: LGPL-2+
+
 Files: gst/avi/gstavimux.c
-Copyright: 2006, Mark Nauwelaerts <manauw@skynet.be>
+Copyright: 2006, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
  2002, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/avi/gstavimux.h
-Copyright: 2001-2003, Ronald Bultje <rbultje@ronald.bitfreak.net>
+Copyright: 2001-2004, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/avi/gstavisubtitle.c
@@ -713,9 +742,9 @@ Copyright: <2007> Tim-Philipp Müller <tim centricular net>
  <2007> Thijs Vermeir <thijsvermeir@gmail.com>
 License: LGPL-2+
 
-Files: gst/cutter/*
+Files: gst/cutter/gstcutter.c
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2002, 2003, 2005, Thomas Vander Stichele <thomas at apestaart dot org>
+ 2000-2003, 2005, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
 Files: gst/cutter/gstcutter.h
@@ -723,7 +752,7 @@ Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
 Files: gst/debugutils/*
-Copyright: 2004, Benjamin Otte <otte@gnome.org>
+Copyright: 2003, 2004, Benjamin Otte <otte@gnome.org>
 License: LGPL-2+
 
 Files: gst/debugutils/cpureport.c
@@ -732,7 +761,7 @@ Copyright: <2010> Zaheer Abbas Merali <zaheerabbas merali org>
 License: LGPL-2+
 
 Files: gst/debugutils/gstcapsdebug.c
-Copyright: 2005, 2010, David Schleef <ds@schleef.org>
+Copyright: 2005, 2010, David Schleef <ds@entropywave.com>
 License: LGPL-2+
 
 Files: gst/debugutils/gstcapsdebug.h
@@ -741,7 +770,7 @@ License: LGPL-2+
 
 Files: gst/debugutils/gstcapssetter.c
  gst/debugutils/gstcapssetter.h
-Copyright: 2006-2009, Mark Nauwelaerts <mnauw@users.sourceforge.net>
+Copyright: 2006-2009, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
 License: LGPL-2+
 
 Files: gst/debugutils/gstdebug.c
@@ -768,7 +797,7 @@ License: LGPL-2+
 
 Files: gst/debugutils/gsttaginject.c
  gst/debugutils/gsttaginject.h
-Copyright: 2006, 2008, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2006, 2008, 2010, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
 Files: gst/debugutils/progressreport.c
@@ -788,41 +817,37 @@ Copyright: 2004, Benjamin Otte <otte@gnome.org>
  1995-1997, 1999, 2000, Free Software Foundation, Inc.
 License: LGPL-2+
 
-Files: gst/deinterlace/*
-Copyright: 2008-2010, Sebastian Dröge <slomo@collabora.co.uk>
-License: LGPL-2+
-
 Files: gst/deinterlace/gstdeinterlace.c
 Copyright: 2011, Robert Swain <robert.swain@collabora.co.uk>
- 2008-2010, Sebastian Dröge <slomo@collabora.co.uk>
+ 2008-2010, Sebastian Dröge <slomo@circular-chaos.org>
  2005, Martin Eikermann <meiker@upb.de>
 License: LGPL-2+
 
 Files: gst/deinterlace/gstdeinterlace.h
-Copyright: 2008-2010, Sebastian Dröge <slomo@collabora.co.uk>
+Copyright: 2008-2010, Sebastian Dröge <slomo@circular-chaos.org>
  2005, Martin Eikermann <meiker@upb.de>
 License: LGPL-2+
 
-Files: gst/deinterlace/tvtime/*
-Copyright: 2008, 2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2002, 2003, Billy Biggs <vektor@dumbterm.net>.
+Files: gst/deinterlace/gstdeinterlacemethod.c
+ gst/deinterlace/gstdeinterlacemethod.h
+Copyright: 2006-2010, Sebastian Dröge <slomo@circular-chaos.org>
 License: LGPL-2+
 
 Files: gst/deinterlace/tvtime/greedy.c
-Copyright: 2008, 2010, Sebastian Dröge <slomo@collabora.co.uk>
+Copyright: 2008, 2010, Sebastian Dröge <sebastian@centricular.com>
  2002, Billy Biggs <vektor@dumbterm.net>.
  2000, Tom Barry
 License: LGPL-2+
 
 Files: gst/deinterlace/tvtime/greedyh.asm
-Copyright: 2008, 2010, Sebastian Dröge <slomo@collabora.co.uk>
+Copyright: 2008, 2010, Sebastian Dröge <sebastian@centricular.com>
  2001, Tom Barry.
 License: LGPL-2+
 
 Files: gst/deinterlace/tvtime/greedyh.c
  gst/deinterlace/tvtime/plugins.h
  gst/deinterlace/tvtime/tomsmocomp.c
-Copyright: 2008, 2010, Sebastian Dröge <slomo@collabora.co.uk>
+Copyright: 2008, 2010, Sebastian Dröge <sebastian@centricular.com>
  2004, Billy Biggs <vektor@dumbterm.net>
 License: LGPL-2+
 
@@ -832,20 +857,23 @@ License: LGPL-2+
 
 Files: gst/deinterlace/tvtime/linear.c
  gst/deinterlace/tvtime/linearblend.c
-Copyright: 2008, 2010, Sebastian Dröge <slomo@collabora.co.uk>
- 2002, Billy Biggs <vektor@dumbterm.net>.
+ gst/deinterlace/tvtime/weave.c
+ gst/deinterlace/tvtime/weavebff.c
+ gst/deinterlace/tvtime/weavetff.c
+Copyright: 2008, 2010, Sebastian Dröge <sebastian@centricular.com>
+ 2002, 2003, Billy Biggs <vektor@dumbterm.net>.
 License: LGPL-2+
 
 Files: gst/deinterlace/tvtime/scalerbob.c
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
-Files: gst/deinterlace/tvtime/tomsmocomp/*
+Files: gst/deinterlace/tvtime/tomsmocomp/TomsMoCompAll.inc
 Copyright: 2001, 2002, Tom Barry
 License: LGPL-2+
 
 Files: gst/deinterlace/tvtime/vfir.c
-Copyright: 2008, 2010, Sebastian Dröge <slomo@collabora.co.uk>
+Copyright: 2008, 2010, Sebastian Dröge <sebastian@centricular.com>
  2004, Billy Biggs <vektor@dumbterm.net>
  2001-2003, Fabrice Bellard.
 License: LGPL-2+
@@ -855,22 +883,22 @@ Copyright: 2004, Dirk Ziegelmeier <dziegel@gmx.de>
 License: LGPL-2+
 
 Files: gst/deinterlace/x86/*
-Copyright: 2005-2018, x264 project
-License: ISC
-
-Files: gst/deinterlace/x86/yadif.asm
 Copyright: 2020, Vivia Nikolaidou <vivia.nikolaidou@ltnglobal.com>
  2013, Daniel Kang <daniel.d.kang@gmail.com>
  2006, Michael Niedermayer <michaelni@gmx.at>
 License: LGPL-2.1+
 
+Files: gst/deinterlace/x86/x86inc.asm
+Copyright: 2005-2018, x264 project
+License: ISC
+
 Files: gst/deinterlace/yadif.c
 Copyright: 2019, Jan Schmidt <jan@centricular.com>
  2006, Michael Niedermayer <michaelni@gmx.at>
 License: LGPL-2+
 
 Files: gst/deinterlace/yadif.h
-Copyright: 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
+Copyright: 2011, 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
 
 Files: gst/dtmf/*
@@ -891,7 +919,7 @@ License: LGPL-2+
 
 Files: gst/dtmf/gstrtpdtmfdepay.c
  gst/dtmf/gstrtpdtmfdepay.h
-Copyright: 2008, Nokia Corporation
+Copyright: 2008, Nokia Corporation <kai.vehmanen@nokia.com>
  2008, Collabora Limited
 License: LGPL-2+
 
@@ -906,8 +934,8 @@ Copyright: <2007> Nokia Corporation.
 License: LGPL-2+
 
 Files: gst/effectv/*
-Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2001, FUKUCHI Kentarou
+Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ 2001-2006, FUKUCHI Kentaro
 License: LGPL-2+
 
 Files: gst/effectv/gstaging.c
@@ -934,14 +962,14 @@ Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
  2001, 2002, FUKUCHI Kentarou
 License: LGPL-2+
 
-Files: gst/effectv/gstop.c
- gst/effectv/gstop.h
- gst/effectv/gstripple.c
- gst/effectv/gstripple.h
- gst/effectv/gststreak.c
- gst/effectv/gststreak.h
-Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2001-2006, FUKUCHI Kentaro
+Files: gst/effectv/gsteffectv.c
+ gst/effectv/gsteffectv.h
+ gst/effectv/gstshagadelic.c
+ gst/effectv/gstshagadelic.h
+ gst/effectv/gstvertigo.c
+ gst/effectv/gstvertigo.h
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ 2001, FUKUCHI Kentarou
 License: LGPL-2+
 
 Files: gst/effectv/gstradioac.c
@@ -976,25 +1004,14 @@ Copyright: <2007> Stefan Kost <ensonic@users.sf.net>
 License: LGPL-2+
 
 Files: gst/flv/*
-Copyright: <2007> Julien Moutte <julien@moutte.net>
-License: LGPL-2+
-
-Files: gst/flv/amfdefs.h
-Copyright: 2011, Jan Schmidt <thaytan@noraisin.net>
-License: LGPL-2+
-
-Files: gst/flv/gstflvelement.c
- gst/flv/gstflvelements.h
 Copyright: <2007> Julien Moutte <julien@moutte.net>
  2020, Huawei Technologies Co., Ltd.
  2008-2017, Collabora Ltd
- 2008, 2009, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ 2008, 2009, Sebastian Dröge <slomo@circular-chaos.org>
 License: LGPL-2+
 
-Files: gst/flv/gstflvmux.c
- gst/flv/gstflvmux.h
-Copyright: 2008-2017, Collabora Ltd
- 2008, 2009, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Files: gst/flv/amfdefs.h
+Copyright: 2011, 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
 
 Files: gst/flv/gstflvplugin.c
@@ -1013,6 +1030,10 @@ Files: gst/flv/gstmemindex.c
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
+Files: gst/flx/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: gst/flx/flx_color.c
 Copyright: <1999> Erik Walthinsen <omega@temple-baptist.com>
 License: LGPL-2+
@@ -1022,10 +1043,6 @@ Copyright: <2016> Matthew Waters <matthew@centricular.com>
  <1999> Erik Walthinsen <omega@temple-baptist.com>
 License: LGPL-2+
 
-Files: gst/goom/*
-Copyright: no-info-found
-License: LGPL
-
 Files: gst/goom/config_param.c
  gst/goom/goom_core.c
  gst/goom/goom_visual_fx.h
@@ -1101,10 +1118,6 @@ Files: gst/goom/ppc_drawings.s
 Copyright: 2001-2003, Guillaume Borios
 License: LGPL-2+
 
-Files: gst/goom2k1/*
-Copyright: no-info-found
-License: LGPL
-
 Files: gst/goom2k1/gstgoom.c
  gst/goom2k1/gstgoom.h
 Copyright: <2015> Luis de Bethencourt <luis@debethencourt.com>
@@ -1121,23 +1134,22 @@ Copyright: 2001, ios.
 License: Public Domain
 
 Files: gst/icydemux/*
-Copyright: 2005, Jan Schmidt <thaytan@mad.scientist.com>
+Copyright: 2005, Jan Schmidt <jan@centricular.com>
  2003, 2004, Benjamin Otte <otte@gnome.org>
 License: LGPL-2+
 
 Files: gst/id3demux/*
-Copyright: 2005, Jan Schmidt <thaytan@mad.scientist.com>
+Copyright: 2005, Jan Schmidt <jan@centricular.com>
  2003, 2004, Benjamin Otte <otte@gnome.org>
 License: LGPL-2+
 
-Files: gst/imagefreeze/*
-Copyright: 2020, Sebastian Dröge <sebastian@centricular.com>
- 2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2005, Edward Hervey <bilboed@bilboed.com>
+Files: gst/imagefreeze/gstimagefreeze.c
+Copyright: 2010, 2020, Sebastian Dröge <sebastian@centricular.com>
+ 2005, Edward Hervey <bilboed@gmail.com>
 License: LGPL-2+
 
 Files: gst/imagefreeze/gstimagefreeze.h
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: gst/interleave/*
@@ -1146,33 +1158,28 @@ License: LGPL-2+
 
 Files: gst/interleave/gstinterleaveelements.h
  gst/interleave/plugin.c
-Copyright: 2004, 2007, Andy Wingo <wingo at pobox.com>
+Copyright: 2004, 2005, 2007, Andy Wingo <wingo at pobox.com>
 License: LGPL-2+
 
 Files: gst/isomp4/*
-Copyright: 2010, Thiago Santos <thiago.sousa.santos@collabora.co.uk>
-License: Expat and/or LGPL-2+
-
-Files: gst/isomp4/atoms.c
 Copyright: 2008-2010, Thiago Santos <thiagoss@embedded.ufcg.edu.br>
- 2008, Mark Nauwelaerts <mnauw@users.sf.net>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
-Files: gst/isomp4/atoms.h
- gst/isomp4/gstqtmux.h
+Files: gst/isomp4/atoms.c
 Copyright: 2008-2010, Thiago Santos <thiagoss@embedded.ufcg.edu.br>
-License: Expat and/or LGPL-2+
+ 2008, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+License: Expat or LGPL-2+
 
 Files: gst/isomp4/descriptors.c
  gst/isomp4/descriptors.h
  gst/isomp4/properties.c
  gst/isomp4/properties.h
 Copyright: 2008, Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/isomp4/fourcc.h
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/isomp4/gstisoff.c
  gst/isomp4/gstisoff.h
@@ -1197,21 +1204,21 @@ Files: gst/isomp4/gstqtmux-doc.c
  gst/isomp4/gstqtmux-doc.h
 Copyright: 2010, Nokia Corporation.
  2008-2010, Thiago Santos <thiagoss@embedded.ufcg.edu.br>
- 2008, Mark Nauwelaerts <mnauw@users.sf.net>
-License: Expat and/or LGPL-2+
+ 2008, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+License: Expat or LGPL-2+
 
 Files: gst/isomp4/gstqtmux.c
 Copyright: 2014, Jan Schmidt <jan@centricular.com>
  2010, Nokia Corporation.
  2008-2010, Thiago Santos <thiagoss@embedded.ufcg.edu.br>
- 2008, Mark Nauwelaerts <mnauw@users.sf.net>
-License: Expat and/or LGPL-2+
+ 2008, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+License: Expat or LGPL-2+
 
 Files: gst/isomp4/gstqtmuxmap.c
  gst/isomp4/gstqtmuxmap.h
 Copyright: 2008, Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>
- 2008, Mark Nauwelaerts <mnauw@users.sf.net>
-License: Expat and/or LGPL-2+
+ 2008, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
+License: Expat or LGPL-2+
 
 Files: gst/isomp4/gstrtpxqtdepay.c
 Copyright: <2006> Wim Taymans <wim@fluendo.com>
@@ -1223,7 +1230,7 @@ License: LGPL-2+
 
 Files: gst/isomp4/qtatomparser.h
 Copyright: <2009> STEricsson <benjamin.gaignard@stericsson.com>
- 2009, Tim-Philipp Müller <tim centricular net>
+ 2009, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: gst/isomp4/qtdemux-webvtt.c
@@ -1263,7 +1270,7 @@ License: LGPL-2+
 Files: gst/isomp4/qtdemux_dump.c
 Copyright: <2009> STEricsson <benjamin.gaignard@stericsson.com>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2009, Tim-Philipp Müller <tim centricular net>
+ 2009, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: gst/isomp4/qtdemux_dump.h
@@ -1274,7 +1281,11 @@ License: LGPL-2+
 
 Files: gst/isomp4/qtdemux_lang.c
  gst/isomp4/qtdemux_lang.h
-Copyright: 2006-2008, 2010, 2011, Tim-Philipp Müller <tim centricular net>
+Copyright: 2006-2008, 2010, 2011, 2014, 2015, 2023, Tim-Philipp Müller <tim@centricular.com>
+License: LGPL-2+
+
+Files: gst/law/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
 Files: gst/law/alaw-decode.c
@@ -1292,7 +1303,7 @@ Files: gst/law/alaw.c
 Copyright: no-info-found
 License: LGPL-2.1+
 
-Files: gst/level/*
+Files: gst/level/gstlevel.c
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
  2000-2003, 2005, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
@@ -1303,7 +1314,7 @@ Copyright: 2000-2003, 2005, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
 Files: gst/matroska/*
-Copyright: 2001-2003, Ronald Bultje <rbultje@ronald.bitfreak.net>
+Copyright: 2001-2004, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/matroska/ebml-write.c
@@ -1327,8 +1338,8 @@ Files: gst/matroska/matroska-demux.c
  gst/matroska/matroska-parse.c
  gst/matroska/matroska-read-common.c
 Copyright: 2011, Debarshi Ray <rishi@gnu.org>
- 2008, Sebastian Dröge <slomo@circular-chaos.org>
- 2006, Tim-Philipp Müller <tim centricular net>
+ 2008, Sebastian Dröge <sebastian@centricular.com>
+ 2006, Tim-Philipp Müller <tim@centricular.com>
  2003, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
@@ -1340,25 +1351,26 @@ Copyright: 2011, Debarshi Ray <rishi@gnu.org>
 License: LGPL-2+
 
 Files: gst/matroska/matroska-ids.c
-Copyright: 2006, Tim-Philipp Müller <tim centricular net>
+Copyright: 2006, Tim-Philipp Müller <tim@centricular.com>
  2003, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/matroska/matroska-mux.c
 Copyright: 2011, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
- 2008, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ 2008, Sebastian Dröge <sebastian@centricular.com>
  2005, Michal Benes <michal.benes@xeris.cz>
  2003, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: gst/matroska/webm-mux.c
  gst/matroska/webm-mux.h
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
-Files: gst/monoscope/*
+Files: gst/monoscope/convolve.c
+ gst/monoscope/convolve.h
 Copyright: 1999, Ralph Loader <suckfish@ihug.co.nz>
-License: GPL and/or LGPL-2+
+License: GPL or LGPL-2+
 
 Files: gst/monoscope/gstmonoscope.c
  gst/monoscope/gstmonoscope.h
@@ -1373,7 +1385,7 @@ Copyright: 2002, Richard Boulton <richard@tartarus.org>
 License: BSD-3-clause
 
 Files: gst/multifile/*
-Copyright: 1999-2001, Erik Walthinsen <omega@cse.ogi.edu>
+Copyright: 2011, 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
 
 Files: gst/multifile/gstimagesequencesrc.c
@@ -1387,6 +1399,12 @@ Copyright: 2020, Thibault Saunier <tsaunier@igalia.com>
  2019, Cesar Fabian Orccon Chipana
 License: LGPL-2+
 
+Files: gst/multifile/gstmultifile.c
+ gst/multifile/gstmultifilesink.c
+ gst/multifile/gstmultifilesink.h
+Copyright: 1999-2001, Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: gst/multifile/gstmultifilesrc.c
  gst/multifile/gstmultifilesrc.h
 Copyright: 2006, 2007, 2009, David A. Schleef <ds@schleef.org>
@@ -1394,7 +1412,7 @@ License: LGPL-2+
 
 Files: gst/multifile/gstsplitfilesrc.c
  gst/multifile/gstsplitfilesrc.h
-Copyright: 2011, 2012, Collabora Ltd. <tim.muller@collabora.co.uk>
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: gst/multifile/gstsplitmuxpartreader.c
@@ -1402,12 +1420,6 @@ Files: gst/multifile/gstsplitmuxpartreader.c
 Copyright: <2014> Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
 
-Files: gst/multifile/gstsplitmuxpartreader.h
- gst/multifile/gstsplitmuxsink.h
- gst/multifile/gstsplitmuxsrc.h
-Copyright: 2014-2019, 2021, 2022, Jan Schmidt <jan@centricular.com>
-License: LGPL-2+
-
 Files: gst/multifile/gstsplitmuxsink.c
 Copyright: <2014-2019> Jan Schmidt <jan@centricular.com>
 License: LGPL-2+
@@ -1425,7 +1437,7 @@ License: LGPL-2+
 
 Files: gst/multipart/*
 Copyright: 2006, Sjoerd Simons <sjoerd@luon.net>
- 2004, Wim Taymans <wim@fluendo.com>
+ 2004, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/multipart/multipart.c
@@ -1434,7 +1446,7 @@ License: LGPL-2+
 
 Files: gst/multipart/multipartmux.c
  gst/multipart/multipartmux.h
-Copyright: 2004-2006, Wim Taymans <wim@fluendo.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/replaygain/*
@@ -1462,7 +1474,7 @@ License: BSD
 
 Files: gst/rtp/fnv1hash.c
  gst/rtp/fnv1hash.h
-Copyright: 2000-2003, 2005, 2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
+Copyright: 2000-2003, 2005-2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
 Files: gst/rtp/gstasteriskh263.c
@@ -1656,7 +1668,7 @@ License: LGPL-2+
 
 Files: gst/rtp/gstrtph263depay.c
 Copyright: <2005> Wim Taymans <wim.taymans@gmail.com>
- 2007, Nokia Corporation
+ 2007, Nokia Corporation <kai.vehmanen@nokia.com>
  2007, Collabora Ltd
 License: LGPL-2+
 
@@ -1698,12 +1710,7 @@ License: LGPL-2+
 
 Files: gst/rtp/gstrtphdrext-colorspace.c
  gst/rtp/gstrtphdrext-colorspace.h
- gst/rtp/gstrtpisacdepay.c
- gst/rtp/gstrtpisacdepay.h
- gst/rtp/gstrtpisacpay.c
- gst/rtp/gstrtpisacpay.h
- gst/rtp/gstrtpsbcdepay.c
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
+Copyright: 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: gst/rtp/gstrtpilbcdepay.c
@@ -1713,10 +1720,18 @@ Files: gst/rtp/gstrtpilbcdepay.c
 Copyright: <2006> Philippe Khalaf <burger@speedy.org>
 License: LGPL-2+
 
+Files: gst/rtp/gstrtpisacdepay.c
+ gst/rtp/gstrtpisacdepay.h
+ gst/rtp/gstrtpisacpay.c
+ gst/rtp/gstrtpisacpay.h
+ gst/rtp/gstrtpsbcdepay.c
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
+License: LGPL-2+
+
 Files: gst/rtp/gstrtpj2kcommon.h
  gst/rtp/gstrtpj2kpay.c
  gst/rtp/gstrtpj2kpay.h
-Copyright: 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/rtp/gstrtpjpegpay.c
@@ -1753,6 +1768,12 @@ Files: gst/rtp/gstrtpmpvpay.c
 Copyright: <2007> Thijs Vermeir <thijsvermeir@gmail.com>
 License: LGPL-2+
 
+Files: gst/rtp/gstrtppassthroughpay.c
+ gst/rtp/gstrtppassthroughpay.h
+Copyright: 2023, Sebastian Dröge <sebastian@centricular.com>
+ 2023, Jonas Danielsson <jonas.danielsson@spiideo.com>
+License: LGPL-2+
+
 Files: gst/rtp/gstrtppcmadepay.c
  gst/rtp/gstrtppcmudepay.c
 Copyright: <2005> Zeeshan Ali <zeenix@gmail.com>
@@ -1805,7 +1826,7 @@ Copyright: 2017, Pexip
 License: LGPL-2.1+
 
 Files: gst/rtp/gstrtpsbcdepay.h
-Copyright: 2012, 2019, Collabora Ltd.
+Copyright: 2012, 2019, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2.1+
 
 Files: gst/rtp/gstrtpsbcpay.c
@@ -1819,13 +1840,13 @@ Files: gst/rtp/gstrtpstreamdepay.c
  gst/rtp/gstrtpstreampay.h
  gst/rtp/gstrtputils.c
  gst/rtp/gstrtputils.h
-Copyright: 2013, 2015, 2018, 2022, Sebastian Dröge <sebastian@centricular.com>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: gst/rtp/gstrtpvp8depay.c
  gst/rtp/gstrtpvp8pay.c
 Copyright: 2011, Sjoerd Simons <sjoerd@luon.net>
- 2011, Collabora Ltd.
+ 2011, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2.1+
 
 Files: gst/rtp/gstrtpvp8depay.h
@@ -1837,7 +1858,7 @@ Files: gst/rtp/gstrtpvp9depay.c
  gst/rtp/gstrtpvp9pay.c
 Copyright: 2015, Stian Selnes <stian@pexip.com>
  2011, Sjoerd Simons <sjoerd@luon.net>
- 2011, Collabora Ltd.
+ 2011, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2.1+
 
 Files: gst/rtp/gstrtpvp9depay.h
@@ -1885,7 +1906,7 @@ Files: gst/rtpmanager/gstrtphdrext-mid.h
  gst/rtpmanager/gstrtphdrext-repairedstreamid.h
  gst/rtpmanager/gstrtphdrext-streamid.h
  gst/rtpmanager/gstrtphdrext-twcc.h
-Copyright: 2014, 2015, 2020-2022, Matthew Waters <matthew@centricular.com>
+Copyright: 2014, 2015, 2020-2023, Matthew Waters <matthew@centricular.com>
 License: LGPL-2+
 
 Files: gst/rtpmanager/gstrtphdrext-ntp.c
@@ -1901,13 +1922,13 @@ Files: gst/rtpmanager/gstrtpjitterbuffer.c
 Copyright: 2016, Pexip AS
  2015, Kurento (http://kurento.org/)
  2007, Wim Taymans <wim.taymans@gmail.com>
- 2007, Nokia Corporation
+ 2007, Nokia Corporation <kai.vehmanen@nokia.com>
  2007, Collabora Ltd
 License: LGPL-2+
 
 Files: gst/rtpmanager/gstrtpjitterbuffer.h
 Copyright: 2007, Wim Taymans <wim.taymans@gmail.com>
- 2007, Nokia Corporation
+ 2007, Nokia Corporation <kai.vehmanen@nokia.com>
  2007, Collabora Ltd
 License: LGPL-2+
 
@@ -1917,14 +1938,14 @@ Copyright: <2004> Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/rtpmanager/gstrtprtxqueue.c
-Copyright: 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/rtpmanager/gstrtprtxreceive.c
  gst/rtpmanager/gstrtprtxreceive.h
  gst/rtpmanager/gstrtprtxsend.c
  gst/rtpmanager/gstrtprtxsend.h
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: gst/rtpmanager/gstrtpst2022-1-fecdec.c
@@ -1936,7 +1957,7 @@ License: LGPL-2+
 
 Files: gst/rtpmanager/gstrtputils.c
  gst/rtpmanager/gstrtputils.h
-Copyright: 2013, 2015, 2018, 2022, Sebastian Dröge <sebastian@centricular.com>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: gst/rtpmanager/meson.build
@@ -1960,7 +1981,7 @@ Copyright: 2019, Net Insight AB
  2016, Pexip AS
  2015, Kurento (http://kurento.org/)
  2007, Wim Taymans <wim.taymans@gmail.com>
- 2007, Nokia Corporation
+ 2007, Nokia Corporation <kai.vehmanen@nokia.com>
  2007, Collabora Ltd
 License: LGPL-2+
 
@@ -1975,34 +1996,38 @@ License: Expat
 
 Files: gst/rtsp/gstrtpdec.c
 Copyright: <2005,2006> Wim Taymans <wim.taymans@gmail.com>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/rtsp/gstrtpdec.h
 Copyright: <2005,2006> Wim Taymans <wim@fluendo.com>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/rtsp/gstrtsp.c
  gst/rtsp/gstrtspelements.h
  gst/rtsp/gstrtspsrc.h
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/rtsp/gstrtspelement.c
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
  2020, Huawei Technologies Co., Ltd.
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/rtsp/gstrtspext.c
  gst/rtsp/gstrtspext.h
 Copyright: <2006> Wim Taymans <wim@fluendo.com>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/rtsp/gstrtspsrc.c
 Copyright: <2005,2006> Wim Taymans <wim at fluendo dot com>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: gst/shapewipe/*
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
+License: LGPL-2+
+
+Files: gst/smpte/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
 Files: gst/smpte/gstsmptealpha.c
@@ -2013,12 +2038,16 @@ Files: gst/smpte/plugin.c
 Copyright: <2008> Wim Taymans <wim.taymans@google.com>
 License: LGPL-2+
 
-Files: gst/spectrum/*
+Files: gst/spectrum/gstspectrum.c
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
+Files: gst/spectrum/gstspectrum.h
 Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
-Files: gst/spectrum/gstspectrum.c
+Files: gst/udp/*
 Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
@@ -2051,12 +2080,12 @@ License: LGPL-2+
 
 Files: gst/udp/gstudpnetutils.c
 Copyright: 2009, Jarkko Palviainen <jarkko.palviainen@sesca.com>
- 2006, Tim-Philipp Müller <tim centricular net>
+ 2006, Tim-Philipp Müller <tim@centricular.com>
  2006, Joni Valtanen <joni.valtanen@movial.fi>
 License: LGPL-2+
 
 Files: gst/udp/gstudpnetutils.h
-Copyright: 2006, Tim-Philipp Müller <tim centricular net>
+Copyright: 2006, Tim-Philipp Müller <tim@centricular.com>
  2006, Joni Valtanen <joni.valtanen@movial.fi>
 License: LGPL-2+
 
@@ -2073,9 +2102,9 @@ Copyright: <2012> Collabora Ltd.
  2014, Centricular Ltd
 License: LGPL-2+
 
-Files: gst/videobox/*
-Copyright: 2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2006, Tim-Philipp Müller <tim centricular net>
+Files: gst/videobox/gstvideobox.c
+Copyright: 2010, Sebastian Dröge <sebastian@centricular.com>
+ 2006, Tim-Philipp Müller <tim@centricular.com>
  1999, Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
@@ -2084,7 +2113,7 @@ Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
 Files: gst/videocrop/*
-Copyright: 2006-2008, 2010, 2011, Tim-Philipp Müller <tim centricular net>
+Copyright: 2006-2008, 2010, 2011, 2014, 2015, 2023, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: gst/videocrop/gstaspectratiocrop.c
@@ -2092,18 +2121,22 @@ Files: gst/videocrop/gstaspectratiocrop.c
 Copyright: 2009, Thijs Vermeir <thijsvermeir@gmail.com>
 License: LGPL-2+
 
+Files: gst/videofilter/*
+Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
+License: LGPL-2+
+
 Files: gst/videofilter/gstgamma.c
 Copyright: <2003> David Schleef <ds@schleef.org>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2006, Mark Nauwelaerts <manauw@skynet.be>
+ 2010, Sebastian Dröge <sebastian@centricular.com>
+ 2006, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
  2003, Arwed v. Merkatz <v.merkatz@gmx.net>
 License: LGPL-2+
 
 Files: gst/videofilter/gstgamma.h
 Copyright: <2003> David Schleef <ds@schleef.org>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
- 2006, Mark Nauwelaerts <manauw@skynet.be>
+ 2006, Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
  2003, Arwed v. Merkatz <v.merkatz@gmx.net>
 License: LGPL-2+
 
@@ -2126,26 +2159,28 @@ Copyright: <2003> David Schleef <ds@schleef.org>
 License: LGPL-2+
 
 Files: gst/videofilter/plugin.c
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
-License: LGPL-2+
-
-Files: gst/videomixer/*
-Copyright: 2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
- 2004, 2008, Wim Taymans <wim@fluendo.com>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: gst/videomixer/blend.c
-Copyright: 2009, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2009, Sebastian Dröge <sebastian@centricular.com>
  2009, Alex Ugarte <augarte@vicomtech.org>
  2006, Mindfruit Bv.
- 2004, Wim Taymans <wim@fluendo.com>
+ 2004, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: gst/videomixer/blend.h
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
+License: LGPL-2+
+
+Files: gst/videomixer/videomixer2.c
+ gst/videomixer/videomixer2.h
+ gst/videomixer/videomixer2pad.h
+Copyright: 2010, Sebastian Dröge <sebastian@centricular.com>
+ 2004, 2008, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
-Files: gst/wavenc/*
+Files: gst/wavenc/gstwavenc.c
 Copyright: <2006> Tim-Philipp Müller <tim centricular net>
  <2002> Iain Holmes <iain@prettypeople.org>
 License: LGPL-2+
@@ -2160,7 +2195,7 @@ Copyright: <2006> Nokia Corporation, Stefan Kost <stefan.kost@nokia.com>.
 License: LGPL-2+
 
 Files: gst/xingmux/*
-Copyright: 2008, Sebastian Dröge <slomo@circular-chaos.org>
+Copyright: 2008, Sebastian Dröge <sebastian@centricular.com>
  2006, Christophe Fergeau <teuf@gnome.org>
 License: LGPL-2+
 
@@ -2168,7 +2203,7 @@ Files: gst/xingmux/plugin.c
 Copyright: <2008> Jan Schmidt <jan.schmidt@sun.com>
 License: LGPL-2+
 
-Files: gst/y4m/*
+Files: gst/y4m/gsty4mencode.c
 Copyright: <2006> Mark Nauwelaerts <mnauw@users.sourceforge.net>
  <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
@@ -2178,22 +2213,15 @@ Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
 
 Files: scripts/*
-Copyright: 2020, Tim-Philipp Müller <tim centricular com>
-License: LGPL-2+
-
-Files: scripts/dist-translations.py
 Copyright: 2006-2010, 2012-2014, 2020, Tim-Philipp Müller <tim centricular net>
 License: LGPL-2+
 
 Files: sys/*
-Copyright: 2010, Fluendo S.A. <support@fluendo.com>
- 2007, Pioneers of the Inevitable <songbird@songbirdnest.com>
- 2005, Sebastien Moutte <sebastien@moutte.net>
+Copyright: 2017, 2018, Collabora Inc.
 License: LGPL-2+
 
-Files: sys/directsound/gstdirectsounddevice.c
- sys/directsound/gstdirectsounddevice.h
-Copyright: 2013, 2015, 2018, 2022, Sebastian Dröge <sebastian@centricular.com>
+Files: sys/directsound/*
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: sys/directsound/gstdirectsoundplugin.c
@@ -2201,6 +2229,13 @@ Copyright: 2007, Pioneers of the Inevitable <songbird@songbirdnest.com>
  2005, Sebastien Moutte <sebastien@moutte.net>
 License: LGPL-2+
 
+Files: sys/directsound/gstdirectsoundsink.c
+ sys/directsound/gstdirectsoundsink.h
+Copyright: 2010, Fluendo S.A. <support@fluendo.com>
+ 2007, Pioneers of the Inevitable <songbird@songbirdnest.com>
+ 2005, Sebastien Moutte <sebastien@moutte.net>
+License: LGPL-2+
+
 Files: sys/oss/*
 Copyright: 1999-2001, Erik Walthinsen <omega@cse.ogi.edu>
 License: LGPL-2+
@@ -2215,8 +2250,13 @@ Copyright: <1999> Erik Walthinsen <omega@cse.ogi.edu>
  2020, Huawei Technologies Co., Ltd.
 License: LGPL-2+
 
+Files: sys/oss/gstossdeviceprovider.c
+ sys/oss/gstossdeviceprovider.h
+Copyright: 2023, Matthieu Volat <mathieu.volat@ensimag.fr>
+License: LGPL-2+
+
 Files: sys/oss4/*
-Copyright: 2006-2008, 2010, 2011, Tim-Philipp Müller <tim centricular net>
+Copyright: 2006-2008, 2010, 2011, 2014, 2015, 2023, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: sys/oss4/oss4-soundcard.h
@@ -2242,7 +2282,7 @@ Files: sys/osxaudio/gstosxaudioelement.c
  sys/osxaudio/gstosxaudiosrc.c
 Copyright: 2007, 2008, Pioneers of the Inevitable <songbird@songbirdnest.com>
  2005, 2006, Zaheer Abbas Merali <zaheerabbas at merali dot org>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: sys/osxaudio/gstosxaudioringbuffer.c
  sys/osxaudio/gstosxaudiosink.c
@@ -2250,16 +2290,16 @@ Files: sys/osxaudio/gstosxaudioringbuffer.c
 Copyright: 2012, Fluendo S.A. <support@fluendo.com>
  2007, 2008, Pioneers of the Inevitable <songbird@songbirdnest.com>
  2005, 2006, Zaheer Abbas Merali <zaheerabbas at merali dot org>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: sys/osxaudio/gstosxaudioringbuffer.h
 Copyright: 2012, Fluendo S.A. <support@fluendo.com>
  2006, Zaheer Abbas Merali <zaheerabbas at merali dot org>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: sys/osxaudio/gstosxaudiosrc.h
 Copyright: 2005, 2006, Zaheer Abbas Merali <zaheerabbas at merali dot org>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: sys/osxvideo/*
 Copyright: 2007-2009, Pioneers of the Inevitable <songbird@songbirdnest.com>
@@ -2272,39 +2312,32 @@ Copyright: 2013-2016, Jan Schmidt <jan@centricular.com>
  2013, Broadcom Europe Ltd
 License: BSD-3-clause
 
-Files: sys/rpicamsrc/RaspiCLI.c
- sys/rpicamsrc/RaspiCLI.h
- sys/rpicamsrc/RaspiPreview.h
-Copyright: 2013, James Hughes
- 2013, Broadcom Europe Ltd
-License: BSD-3-clause
-
 Files: sys/rpicamsrc/RaspiCapture.h
  sys/rpicamsrc/gstrpicamsrc.c
  sys/rpicamsrc/gstrpicamsrc.h
 Copyright: 2013-2015, Jan Schmidt <jan@centricular.com>
-License: Expat and/or LGPL-2+
+License: Expat or LGPL-2+
 
 Files: sys/rpicamsrc/gstrpicamsrcdeviceprovider.c
  sys/rpicamsrc/gstrpicamsrcdeviceprovider.h
-Copyright: 2014, 2015, Tim-Philipp Müller <tim@centricular.com>
-License: LGPL-2+
-
-Files: sys/v4l2/*
-Copyright: 2017, 2018, Collabora Inc.
+Copyright: 2006-2008, 2010, 2011, 2014, 2015, 2023, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: sys/v4l2/ext/*
 Copyright: 1999-2012, the contributors
-License: BSD-3-clause and/or GPL-2+
+License: (GPL-2+ WITH Linux-syscall-note) OR BSD-3-Clause
 
 Files: sys/v4l2/ext/types-compat.h
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: sys/v4l2/ext/v4l2-common.h
-Copyright: 2012, Nokia Corporation
-License: BSD-3-clause and/or GPL-2+
+Copyright: 2012, Nokia Corporation <kai.vehmanen@nokia.com>
+License: (GPL-2+ WITH Linux-syscall-note) OR BSD-3-Clause
+
+Files: sys/v4l2/ext/videodev2.h
+Copyright: 1999-2012, the contributors
+License: BSD-3-clause or GPL-2+
 
 Files: sys/v4l2/gstv4l2.c
  sys/v4l2/gstv4l2bufferpool.c
@@ -2325,7 +2358,7 @@ Files: sys/v4l2/gstv4l2.c
  sys/v4l2/tunernorm.c
  sys/v4l2/tunernorm.h
  sys/v4l2/v4l2_calls.c
-Copyright: 2001-2003, Ronald Bultje <rbultje@ronald.bitfreak.net>
+Copyright: 2001-2004, Ronald Bultje <rbultje@ronald.bitfreak.net>
 License: LGPL-2+
 
 Files: sys/v4l2/gstv4l2allocator.c
@@ -2336,7 +2369,7 @@ Files: sys/v4l2/gstv4l2allocator.c
  sys/v4l2/gstv4l2videodec.h
  sys/v4l2/v4l2-utils.c
  sys/v4l2/v4l2-utils.h
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: sys/v4l2/gstv4l2codec.c
@@ -2385,7 +2418,7 @@ Copyright: 2009, Texas Instruments, Inc - http://www.ti.com
 License: LGPL-2+
 
 Files: sys/v4l2/gstv4l2videoenc.c
-Copyright: 2017, Collabora Ltd.
+Copyright: 2017, Collabora Ltd. <tim.muller@collabora.co.uk>
  2014-2017, SUMOMO Computer Association
 License: LGPL-2+
 
@@ -2398,23 +2431,26 @@ Files: sys/waveform/*
 Copyright: 2005, Sebastien Moutte <sebastien@moutte.net>
 License: LGPL-2+
 
-Files: sys/ximage/*
-Copyright: <2005> Luca Ognibene <luogni@tin.it>
-License: LGPL-2+
-
 Files: sys/ximage/gstximagesrc.c
 Copyright: 2006, Zaheer Merali <zaheerabbas at merali dot org>
 License: LGPL-2+
 
-Files: sys/ximage/gstximagesrc.h
-Copyright: no-info-found
+Files: sys/ximage/ximageutil.c
+ sys/ximage/ximageutil.h
+Copyright: <2005> Luca Ognibene <luogni@tin.it>
 License: LGPL-2+
 
-Files: tests/*
-Copyright: <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+Files: tests/check/*
+Copyright: 2006-2008, 2010, 2011, 2014, 2015, 2023, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
-Files: tests/check/elements/*
+Files: tests/check/elements/aacparse.c
+ tests/check/elements/ac3parse.c
+ tests/check/elements/amrparse.c
+ tests/check/elements/flacparse.c
+ tests/check/elements/mpegaudioparse.c
+ tests/check/elements/parser.c
+ tests/check/elements/parser.h
 Copyright: 2008, 2010, Nokia Corporation.
 License: LGPL-2+
 
@@ -2423,19 +2459,14 @@ Files: tests/check/elements/alpha.c
 Copyright: 2006, 2007, Ravi Kiran K N <ravi.kiran@samsung.com>
 License: LGPL-2+
 
-Files: tests/check/elements/alphacolor.c
- tests/check/elements/gdkpixbufsink.c
- tests/check/elements/id3demux.c
- tests/check/elements/id3v2mux.c
- tests/check/elements/matroskaparse.c
- tests/check/elements/udpsrc.c
- tests/check/elements/videocrop.c
-Copyright: 2006-2008, 2010, 2011, Tim-Philipp Müller <tim centricular net>
+Files: tests/check/elements/amrnbenc.c
+Copyright: 2000-2003, 2005-2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
 Files: tests/check/elements/apev2mux.c
-Copyright: 2006, Tim-Philipp Müller <tim centricular net>
- 2006, Sebastian Dröge <slomo@circular-chaos.org>
+ tests/check/elements/interleave.c
+Copyright: 2006, 2008, Sebastian Dröge <sebastian@centricular.com>
+ 2006, 2007, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: tests/check/elements/aspectratiocrop.c
@@ -2445,33 +2476,35 @@ License: LGPL-2+
 Files: tests/check/elements/audioamplify.c
  tests/check/elements/audiodynamic.c
  tests/check/elements/audioinvert.c
-Copyright: 2007, Sebastian Dröge <slomo@circular-chaos.org>
- 2006, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2007, Sebastian Dröge <sebastian@centricular.com>
+ 2006, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
 Files: tests/check/elements/audiochebband.c
  tests/check/elements/audiocheblimit.c
+ tests/check/elements/audiofirfilter.c
+ tests/check/elements/audioiirfilter.c
  tests/check/elements/audiowsincband.c
  tests/check/elements/audiowsinclimit.c
  tests/check/elements/equalizer.c
  tests/check/elements/xingmux.c
-Copyright: 2007, 2008, Sebastian Dröge <slomo@circular-chaos.org>
+Copyright: 2007-2009, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2.1+
 
 Files: tests/check/elements/audioecho.c
+ tests/check/elements/deinterleave.c
+ tests/check/elements/imagefreeze.c
  tests/check/elements/shapewipe.c
  tests/check/elements/vp8dec.c
  tests/check/elements/vp8enc.c
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ tests/check/elements/wavpackdec.c
+ tests/check/elements/wavpackenc.c
+ tests/check/elements/wavpackparse.c
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
-Files: tests/check/elements/audiofirfilter.c
- tests/check/elements/audioiirfilter.c
-Copyright: 2009, Sebastian Dröge <sebastian.droege@collabora.co.uk>
-License: LGPL-2.1+
-
 Files: tests/check/elements/audiopanorama.c
-Copyright: 2006, 2008, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2006, 2008, 2010, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
 Files: tests/check/elements/autodetect.c
@@ -2501,30 +2534,19 @@ Files: tests/check/elements/deinterlace.c
 Copyright: 2010, Thiago Santos <thiago.sousa.santos@collabora.co.uk>
 License: LGPL-2+
 
-Files: tests/check/elements/deinterleave.c
- tests/check/elements/wavpackdec.c
- tests/check/elements/wavpackenc.c
- tests/check/elements/wavpackparse.c
-Copyright: 2006-2008, Sebastian Dröge <slomo@circular-chaos.org>
-License: LGPL-2+
-
 Files: tests/check/elements/dtmf.c
 Copyright: 2013, Collabora Ltd
 License: LGPL-2+
 
 Files: tests/check/elements/flvdemux.c
 Copyright: 2016, Havard Graff <havard@pexip.com>
- 2009, Tim-Philipp Müller <tim centricular net>
+ 2009, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: tests/check/elements/flvmux.c
 Copyright: 2016, Havard Graff <havard@pexip.com>
  2016, David Buchmann <david@pexip.com>
- 2009, Tim-Philipp Müller <tim centricular net>
-License: LGPL-2+
-
-Files: tests/check/elements/gdkpixbufoverlay.c
-Copyright: 2014, 2015, Tim-Philipp Müller <tim centricular com>
+ 2009, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: tests/check/elements/hlsdemux_m3u8.c
@@ -2536,16 +2558,6 @@ Files: tests/check/elements/icydemux.c
 Copyright: 2006, Michael Smith <msmith@fluendo.com>
 License: LGPL-2+
 
-Files: tests/check/elements/imagefreeze.c
-Copyright: 2020, Sebastian Dröge <sebastian@centricular.com>
- 2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
-License: LGPL-2+
-
-Files: tests/check/elements/interleave.c
-Copyright: 2008, Sebastian Dröge <slomo@circular-chaos.org>
- 2007, Tim-Philipp Müller <tim centricular net>
-License: LGPL-2+
-
 Files: tests/check/elements/jpegdec.c
 Copyright: <2012> Mathias Hasselmann <mathias@openismus.com>
  <2010> Thiago Santos <thiago.sousa.santos@collabora.co.uk>
@@ -2559,10 +2571,6 @@ Files: tests/check/elements/level.c
 Copyright: <2005> Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
-Files: tests/check/elements/matroskademux.c
-Copyright: 2014, 2015, Tim-Philipp Müller <tim@centricular.com>
-License: LGPL-2+
-
 Files: tests/check/elements/matroskamux.c
 Copyright: <2005> Michal Benes <michal.benes@xeris.cz>
 License: LGPL-2+
@@ -2571,12 +2579,6 @@ Files: tests/check/elements/mpg123audiodec.c
 Copyright: 2012, Carlos Rafael Giani <dv@pseudoterminal.org>
 License: LGPL-2+
 
-Files: tests/check/elements/mulawdec.c
- tests/check/elements/mulawenc.c
- tests/check/elements/wavparse.c
-Copyright: no-info-found
-License: LGPL-2+
-
 Files: tests/check/elements/multifile.c
 Copyright: 2006, 2007, 2009, David A. Schleef <ds@schleef.org>
 License: LGPL-2+
@@ -2589,7 +2591,6 @@ Copyright: 2017, Pexip
 License: LGPL-2.1+
 
 Files: tests/check/elements/qtdemux.c
- tests/check/elements/qtdemux.h
 Copyright: <2016> Edward Hervey <edward@centricular.com>
 License: LGPL-2+
 
@@ -2612,14 +2613,13 @@ Copyright: <2009> Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: tests/check/elements/rtpbin_buffer_list.c
-Copyright: 2019, Collabora Ltd.
+Copyright: 2019, Collabora Ltd. <tim.muller@collabora.co.uk>
  2009, Branko Subasic <branko dot subasic at axis dot com>
 License: LGPL-2+
 
 Files: tests/check/elements/rtpcollision.c
- tests/check/elements/rtphdrext-colorspace.c
  tests/check/elements/rtprtx.c
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: tests/check/elements/rtpfunnel.c
@@ -2642,6 +2642,10 @@ Copyright: 2018, Collabora Ltd
  2017, Centricular Ltd
 License: LGPL-2+
 
+Files: tests/check/elements/rtphdrext-colorspace.c
+Copyright: 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
+License: LGPL-2+
+
 Files: tests/check/elements/rtphdrextclientaudiolevel.c
  tests/check/elements/rtphdrextsdes.c
 Copyright: <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.com>
@@ -2659,7 +2663,11 @@ License: LGPL-2+
 
 Files: tests/check/elements/rtpmux.c
 Copyright: 2009, Nokia Corp.
- 2009, Collabora Ltd.
+ 2009, Collabora Ltd. <tim.muller@collabora.co.uk>
+License: LGPL-2+
+
+Files: tests/check/elements/rtppassthrough.c
+Copyright: 2023, Jonas Danielsson <jonas.danielsson@spiideo.com>
 License: LGPL-2+
 
 Files: tests/check/elements/rtpptdemux.c
@@ -2668,12 +2676,12 @@ License: LGPL-2+
 
 Files: tests/check/elements/rtpsession.c
 Copyright: <2009> Wim Taymans <wim.taymans@gmail.com>
- 2013, Collabora Ltd.
+ 2013, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: tests/check/elements/rtpssrcdemux.c
 Copyright: 2019, Pexip
- 2018, Collabora Ltd.
+ 2018, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
 Files: tests/check/elements/rtpst2022-1-fecdec.c
@@ -2688,7 +2696,7 @@ License: LGPL-2+
 Files: tests/check/elements/souphttpsrc.c
 Copyright: 2021, Igalia S.L.
  2008, Wouter Cloetens <wouter@mind.be>
- 2006, 2007, Tim-Philipp Müller <tim centricular net>
+ 2006, 2007, Tim-Philipp Müller <tim@centricular.com>
  2001-2003, Ximian, Inc.
 License: LGPL-2+
 
@@ -2716,8 +2724,12 @@ Files: tests/check/elements/vp9enc.c
 Copyright: 2016, Stian Selnes <stian@pexip.com>
 License: LGPL-2+
 
+Files: tests/check/generic/*
+Copyright: <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+License: LGPL-2+
+
 Files: tests/check/pipelines/*
-Copyright: 2000-2003, 2005, 2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
+Copyright: 2000-2003, 2005-2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
 Files: tests/check/pipelines/effectv.c
@@ -2725,7 +2737,7 @@ Copyright: <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
 License: LGPL-2+
 
 Files: tests/check/pipelines/simple-launch-lines.c
-Copyright: 2005, Andy Wingo <wingo@pobox.com>
+Copyright: 2004, 2005, 2007, Andy Wingo <wingo at pobox.com>
 License: LGPL-2+
 
 Files: tests/check/pipelines/tagschecking.c
@@ -2738,57 +2750,63 @@ Copyright: <2010> Stefan Kost <ensonic@users.sf.net>
 License: LGPL-2+
 
 Files: tests/check/pipelines/wavpack.c
-Copyright: 2006-2008, Sebastian Dröge <slomo@circular-chaos.org>
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
-Files: tests/examples/*
+Files: tests/examples/audiofx/*
 Copyright: 2009, Sebastian Droege <sebastian.droege@collabora.co.uk>
 License: LGPL-2+
 
-Files: tests/examples/cairo/*
+Files: tests/examples/cairo/cairo_overlay.c
 Copyright: 2011, Jon Nordby <jononor@gmail.com>
 License: LGPL-2+
 
-Files: tests/examples/equalizer/*
-Copyright: 2006-2008, Sebastian Dröge <slomo@circular-chaos.org>
+Files: tests/examples/equalizer/demo.c
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: tests/examples/gtk/*
-Copyright: 2014, 2015, 2020-2022, Matthew Waters <matthew@centricular.com>
+Copyright: 2014, 2015, 2020-2023, Matthew Waters <matthew@centricular.com>
+License: LGPL-2+
+
+Files: tests/examples/gtk/gtkhttpsrc.c
+Copyright: 2023, Arnaud Rebillout <elboulangero@gmail.com>
 License: LGPL-2+
 
-Files: tests/examples/level/*
-Copyright: 2000-2003, 2005, 2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
+Files: tests/examples/level/level-example.c
+Copyright: 2000-2003, 2005-2007, 2009, Thomas Vander Stichele <thomas at apestaart dot org>
 License: LGPL-2+
 
-Files: tests/examples/rpicamsrc/*
+Files: tests/examples/rpicamsrc/test_color_balance.c
+ tests/examples/rpicamsrc/test_orientation.c
 Copyright: 2015, Igalia S.L
 License: LGPL
 
-Files: tests/examples/rtp/*
-Copyright: 2012-2014, 2020, 2021, Collabora Ltd.
-License: LGPL-2+
-
 Files: tests/examples/rtp/client-PCMA.c
  tests/examples/rtp/server-alsasrc-PCMA.c
-Copyright: 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
+License: LGPL-2+
+
+Files: tests/examples/rtp/client-rtpaux.c
+ tests/examples/rtp/server-rtpaux.c
+Copyright: 2011-2014, 2020, 2021, Collabora Ltd. <tim.muller@collabora.co.uk>
 License: LGPL-2+
 
-Files: tests/examples/shapewipe/*
-Copyright: 2008-2010, Sebastian Dröge <sebastian.droege@collabora.co.uk>
+Files: tests/examples/shapewipe/shapewipe-example.c
+Copyright: 2006-2010, 2013, 2015, 2018, 2020, 2022, Sebastian Dröge <sebastian@centricular.com>
 License: LGPL-2+
 
 Files: tests/examples/spectrum/*
-Copyright: 2006, 2008, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2006, 2008, 2010, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
 Files: tests/examples/spectrum/spectrum-example.c
-Copyright: 2008, Jan Schmidt <jan.schmidt@sun.com>
- 2006, Stefan Kost <ensonic@users.sf.net>
+Copyright: 2008, Jan Schmidt <jan@centricular.com>
+ 2006, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
-Files: tests/examples/v4l2/*
-Copyright: 2010, Stefan Kost <stefan.kost@nokia.com>
+Files: tests/examples/v4l2/camctrl.c
+Copyright: 2006, 2008, 2010, Stefan Kost <stefan.kost@nokia.com>
 License: LGPL-2+
 
 Files: tests/examples/v4l2/v4l2src-crop.c
@@ -2799,59 +2817,95 @@ Files: tests/examples/v4l2/v4l2src-renegotiate.c
 Copyright: 2015, Samsung Electronics.
 License: LGPL-2+
 
-Files: tests/files/*
-Copyright: UNKNOWN
-License: UNKNOWN
-
 Files: tests/files/audiotestsrc.flac
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/audiotestsrc.wav
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/gradient.j2k
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/id3-577468-unsynced-tag.tag
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/id3-588148-unsynced-v24.tag
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
+
+Files: tests/files/mss-fragment.m4f
+Copyright: no-info-found
+License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/pcm16sine.flv
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/pinknoise-vorbis.mkv
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
+
+Files: tests/files/qtdemux-test-audio-seg1.m4f
+Copyright: no-info-found
+License: UNKNOWN
+ Please fill license text from header of files
 
 Files: tests/files/sine-1009ms-1ch-32000hz-gapless-with-lame-tag.mp3
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
-Files: tests/files/vbr_stream.mp3
+Files: tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-itunes.m4a
 Copyright: no-info-found
 License: UNKNOWN
+ Please fill license text from header of files
 
-Files: tests/interactive/*
-Copyright: 2006-2008, 2010, 2011, Tim-Philipp Müller <tim centricular net>
-License: LGPL-2+
+Files: tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-with-itunsmpb.m4a
+Copyright: no-info-found
+License: UNKNOWN
+ Please fill license text from header of files
 
-Files: tests/interactive/gdkpixbufoverlay-test.c
- tests/interactive/test-accurate-seek.c
- tests/interactive/test-segment-seeks.c
-Copyright: 2014, 2015, Tim-Philipp Müller <tim centricular com>
+Files: tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-without-itunsmpb.m4a
+Copyright: no-info-found
+License: UNKNOWN
+ Please fill license text from header of files
+
+Files: tests/files/splitvideo00.ogg
+ tests/files/splitvideo01.ogg
+ tests/files/splitvideo02.ogg
+Copyright: .fWbìB
+License: UNKNOWN
+ Please fill license text from header of files
+
+Files: tests/files/stream.mp2
+Copyright: no-info-found
+License: UNKNOWN
+ Please fill license text from header of files
+
+Files: tests/files/vbr_stream.mp3
+Copyright: 5zØà.ÈIjàyÄ
+License: UNKNOWN
+ Please fill license text from header of files
+
+Files: tests/interactive/*
+Copyright: 2006-2008, 2010, 2011, 2014, 2015, 2023, Tim-Philipp Müller <tim@centricular.com>
 License: LGPL-2+
 
 Files: tests/interactive/videobox-test.c
  tests/interactive/videocrop2-test.c
-Copyright: 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
+Copyright: 2004-2006, 2008, 2009, 2013, Wim Taymans <wim.taymans@gmail.com>
 License: LGPL-2+
 
 Files: tests/interactive/ximagesrc-test.c
diff --git a/debian/apertis/copyright.yml b/debian/apertis/copyright.yml
index 0b34784c9c7ebf781702f79fc8970103bcbc3cc6..ee6ffe7d41ab015398331b9cfde959fe0dfcfa44 100644
--- a/debian/apertis/copyright.yml
+++ b/debian/apertis/copyright.yml
@@ -15,3 +15,5 @@ sys/oss4/oss4-soundcard.h:
   license: BSD
   copyright: 4Front Technologies
 
+sys/v4l2/ext/*:
+  license: (GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause
diff --git a/debian/apertis/gitlab-ci.yml b/debian/apertis/gitlab-ci.yml
deleted file mode 100644
index 272299caeacd6bd17188a49269267b4df886e36f..0000000000000000000000000000000000000000
--- a/debian/apertis/gitlab-ci.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-include:
-  - project: 'infrastructure/ci-package-builder'
-    file: '/ci-package-builder.yml'
-
diff --git a/debian/changelog b/debian/changelog
index 6483e8d7c4d7b09d998285303f3e930736cf5acd..cd0836d964785e41c49d2bb3008a11a8a30d1781 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,231 @@
+gst-plugins-good1.0 (1.24.12-1+apertis1) apertis; urgency=medium
+
+  * Sync from debian/trixie.
+  * Remaining Apertis specific changes:
+    - Disable JACK support.
+    - Disable mp3 related plugins.
+    - Check dh_auto_test results only for amd64 since it fails for arm64 on OBS
+  * copyright.yml: add license of sys/v4l2/ext/*
+
+ -- Dylan Aïssi <dylan.aissi@collabora.com>  Tue, 18 Mar 2025 16:01:28 +0100
+
+gst-plugins-good1.0 (1.24.12-1) unstable; urgency=medium
+
+  * New upstream version 1.24.12
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Thu, 30 Jan 2025 08:50:17 +0100
+
+gst-plugins-good1.0 (1.24.11-5) unstable; urgency=medium
+
+  * d: build fixes for hurd-amd64 (Closes: #1072337)
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Mon, 20 Jan 2025 09:44:58 +0100
+
+gst-plugins-good1.0 (1.24.11-4) unstable; urgency=medium
+
+  * d/rules: soup only when not hurd
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Fri, 17 Jan 2025 17:33:53 +0100
+
+gst-plugins-good1.0 (1.24.11-3) unstable; urgency=medium
+
+  * d/watch: pin on 1.24.x
+  * d/rules: disable soup plugins on hurd
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Fri, 17 Jan 2025 14:32:21 +0100
+
+gst-plugins-good1.0 (1.24.11-2) unstable; urgency=medium
+
+  * d/rules: disable adaptivedemux2 on hurd
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Thu, 09 Jan 2025 16:35:04 +0100
+
+gst-plugins-good1.0 (1.24.11-1) unstable; urgency=medium
+
+  * New upstream version 1.24.11
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Tue, 07 Jan 2025 09:14:04 +0100
+
+gst-plugins-good1.0 (1.24.10-1) unstable; urgency=medium
+
+  * New upstream version 1.24.10
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Wed, 04 Dec 2024 09:35:51 +0100
+
+gst-plugins-good1.0 (1.24.9-1) unstable; urgency=medium
+
+  * New upstream version 1.24.9
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Thu, 31 Oct 2024 09:00:28 +0100
+
+gst-plugins-good1.0 (1.24.8-1) unstable; urgency=medium
+
+  * New upstream version 1.24.8
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Thu, 19 Sep 2024 14:21:32 +0200
+
+gst-plugins-good1.0 (1.24.7-1) unstable; urgency=medium
+
+  * d/rules: debian-rules-parses-dpkg-parsechangelog
+  * New upstream version 1.24.7
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Wed, 21 Aug 2024 17:23:43 +0200
+
+gst-plugins-good1.0 (1.24.6-1) unstable; urgency=medium
+
+  * New upstream version 1.24.6
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Mon, 29 Jul 2024 18:54:27 +0200
+
+gst-plugins-good1.0 (1.24.5-1) unstable; urgency=medium
+
+  * New upstream version 1.24.5
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Thu, 20 Jun 2024 16:24:50 +0200
+
+gst-plugins-good1.0 (1.24.4-1) unstable; urgency=medium
+
+  * New upstream version 1.24.4
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Wed, 29 May 2024 14:12:56 +0200
+
+gst-plugins-good1.0 (1.24.3-1) unstable; urgency=medium
+
+  * New upstream version 1.24.3
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Tue, 30 Apr 2024 11:18:48 +0200
+
+gst-plugins-good1.0 (1.24.2-1) unstable; urgency=medium
+
+  * New upstream version 1.24.2
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Wed, 10 Apr 2024 09:30:53 +0200
+
+gst-plugins-good1.0 (1.24.1-2) unstable; urgency=medium
+
+  * Team upload
+  * Add Breaks for pre-t64 versions of libv4l-0 and libv4lconvert0
+    to help apt calculate upgrades. Thanks to Julian Klode.
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Fri, 05 Apr 2024 12:07:12 -0400
+
+gst-plugins-good1.0 (1.24.1-1) unstable; urgency=medium
+
+  * d/control: drop dependency to libsoup2.4-1
+  * New upstream version 1.24.1
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Fri, 22 Mar 2024 10:19:41 +0100
+
+gst-plugins-good1.0 (1.24.0-1) unstable; urgency=medium
+
+  * Revert "d/watch: pin master releases on 1.22.x"
+  * New upstream version 1.24.0
+  * d/control: bump gstreamer dependencies to 1.24.0
+  * d/control: build-depend on libopencore-amrnb-dev
+  * d/control: build-depend on libopencore-amrwb-dev
+  * d/install: libgstamrnb.so and libgstamrwbdec.so
+  * d/control: add qt6-shader-baker to Build-Depends
+  * d/patches: disable flvmux test
+  * d/control: update Replaces/Breaks ugly to 1.24
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Tue, 05 Mar 2024 14:07:57 +0100
+
+gst-plugins-good1.0 (1.22.10-1) unstable; urgency=medium
+
+  * d/watch: pin master releases on 1.22.x
+  * New upstream version 1.22.10
+  * d/control: pkg-config is transitional
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Wed, 14 Feb 2024 11:49:20 +0100
+
+gst-plugins-good1.0 (1.22.9-1) unstable; urgency=medium
+
+  * New upstream version 1.22.9
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Fri, 02 Feb 2024 08:48:52 +0100
+
+gst-plugins-good1.0 (1.22.8-3) unstable; urgency=medium
+
+  * Team upload
+  * Disable qt-egl on hurd since it is Linux-only
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 21 Dec 2023 19:28:48 -0500
+
+gst-plugins-good1.0 (1.22.8-2) unstable; urgency=medium
+
+  * Team upload
+  * Stop building gstreamer1.0-qt6 on i386
+  * Don't require qt5-wayland or qt6-wayland on i386 and hurd-i386
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Thu, 21 Dec 2023 17:50:37 -0500
+
+gst-plugins-good1.0 (1.22.8-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream version 1.22.8
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Mon, 18 Dec 2023 16:36:06 +0100
+
+gst-plugins-good1.0 (1.22.7-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream version 1.22.7
+
+ -- Marc Leeman <marc.leeman@gmail.com>  Tue, 14 Nov 2023 14:07:01 +0100
+
+gst-plugins-good1.0 (1.22.6-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream bugfix release
+  * Drop Qt patch: applied in new release
+  * Build-Depend on qttools5-dev
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 25 Sep 2023 15:04:32 -0400
+
+gst-plugins-good1.0 (1.22.5-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream release
+  * Cherry-pick patch to fix Qt build
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 16 Aug 2023 08:02:48 -0400
+
+gst-plugins-good1.0 (1.22.4-1) unstable; urgency=high
+
+  * Team upload
+  * SECURITY UPDATE: New upstream release
+    - Integer overflow leading to heap overwrite in FLAC image tag handling
+      (GStreamer-SA-2023-0001, ZDI-CAN-20775)
+    - Heap overwrite in PGS subtitle overlay decoder
+      (GStreamer-SA-2023-0003, ZDI-CAN-20994)
+  * debian/control: Update qt6 Build-Depends
+  * debian/control: Update libshout-dev Build-Depends
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Wed, 28 Jun 2023 15:58:32 -0400
+
+gst-plugins-good1.0 (1.22.3-2) unstable; urgency=medium
+
+  * Team upload
+  * Release to unstable
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Mon, 12 Jun 2023 14:42:30 -0400
+
+gst-plugins-good1.0 (1.22.3-1) experimental; urgency=medium
+
+  * Team upload
+  * New upstream release
+  * debian/rules: Enable all hardening flags
+  * Try dropping patch that skips some failing build tests
+
+ -- Jeremy Bícha <jbicha@ubuntu.com>  Sun, 21 May 2023 08:14:46 -0400
+
+gst-plugins-good1.0 (1.22.1-1) experimental; urgency=medium
+
+  * Team upload
+  * New upstream bugfix release
+
+ -- Jeremy Bicha <jbicha@ubuntu.com>  Mon, 03 Apr 2023 16:35:02 -0400
+
 gst-plugins-good1.0 (1.22.0-5+deb12u2+apertis1) apertis; urgency=medium
 
   * Sync from debian/bookworm-security.
diff --git a/debian/control b/debian/control
index 4c2d5b1dc24d4f2d0b7ed3ceb3fabb3db07491cf..684237c0543f4f796e4ec834ea1fe96d338a52de 100644
--- a/debian/control
+++ b/debian/control
@@ -3,25 +3,26 @@ Section: libs
 Priority: optional
 Maintainer: Maintainers of GStreamer packages <gst-plugins-good1.0@packages.debian.org>
 Uploaders: Sebastian Dröge <slomo@debian.org>,
-           Sjoerd Simons <sjoerd@debian.org>
+           Sjoerd Simons <sjoerd@debian.org>,
+           Marc Leeman <marc.leeman@gmail.com>,
 Build-Depends: debhelper,
                debhelper-compat (= 13),
                dpkg-dev (>= 1.15.1),
                meson (>= 0.62),
-               pkg-config (>= 0.11.0),
+               pkgconf (>= 0.11.0),
                xvfb,
                xauth,
                libgirepository1.0-dev,
                libglib2.0-dev (>= 2.62),
-               libgstreamer1.0-dev (>= 1.22.0),
-               libgstreamer-plugins-base1.0-dev (>= 1.22.0),
-               gstreamer1.0-plugins-base (>= 1.22.0),
+               libgstreamer1.0-dev (>= 1.24.0),
+               libgstreamer-plugins-base1.0-dev (>= 1.24.0),
+               gstreamer1.0-plugins-base (>= 1.24.0),
                liborc-0.4-dev (>= 1:0.4.17),
                libcairo2-dev (>= 1.10.0),
                libcaca-dev,
                libspeex-dev (>= 1.1.6),
                libpng-dev,
-               libshout3-dev,
+               libshout-dev,
                libjpeg-dev,
                libaa1-dev (>= 1.4p5),
                libflac-dev (>= 1.1.4),
@@ -38,14 +39,19 @@ Build-Depends: debhelper,
                libpulse-dev (>= 2.0),
                libbz2-dev,
                libvpx-dev (>= 1.7),
-               qtbase5-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               qtbase5-private-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               qtdeclarative5-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               qt6-base-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               qt6-declarative-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               qt6-tools-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               libqt5x11extras5-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64],
-               libqt5waylandclient5-dev [amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x powerpc ppc64 riscv64 sparc64],
+               libopencore-amrnb-dev,
+               libopencore-amrwb-dev,
+               qtbase5-dev [amd64 arm64 armel armhf i386 mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qtbase5-private-dev [amd64 arm64 armel armhf i386 mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qtdeclarative5-dev [amd64 arm64 armel armhf i386 mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qttools5-dev [amd64 arm64 armel armhf i386 mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qt6-base-private-dev [amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qt6-declarative-dev [amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qt6-shader-baker [amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qt6-tools-dev [amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               qt6-wayland-dev [amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x powerpc ppc64 sparc64],
+               libqt5x11extras5-dev [amd64 arm64 armel armhf i386 mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64],
+               libqt5waylandclient5-dev [amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x powerpc ppc64 sparc64],
                libraw1394-dev (>= 2.0.0) [linux-any],
                libiec61883-dev (>= 1.0.0) [linux-any],
                libavc1394-dev [linux-any],
@@ -61,7 +67,7 @@ Package: gstreamer1.0-pulseaudio
 Architecture: any
 Multi-Arch: same
 Section: oldlibs
-Depends: gstreamer1.0-plugins-good (>= 1.22.0), ${misc:Depends}
+Depends: gstreamer1.0-plugins-good (>= 1.24.0), ${misc:Depends}
 Description: GStreamer plugin for PulseAudio (transitional package)
  GStreamer is a streaming media framework, based on graphs of filters
  which operate on media data.  Applications using this library can do
@@ -76,7 +82,7 @@ Description: GStreamer plugin for PulseAudio (transitional package)
  This is a transitional package.
 
 Package: gstreamer1.0-qt5
-Architecture: amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64
+Architecture: amd64 arm64 armel armhf i386 mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64
 Multi-Arch: same
 Section: graphics
 Depends: ${misc:Depends},
@@ -101,7 +107,7 @@ Description: GStreamer plugin for Qt5
  This package contains the GStreamer plugin for Qt5.
 
 Package: gstreamer1.0-qt6
-Architecture: amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x hurd-i386 powerpc ppc64 riscv64 sparc64
+Architecture: amd64 arm64 armel armhf mips64el ppc64el riscv64 s390x hurd-amd64 hurd-i386 powerpc ppc64 sparc64
 Multi-Arch: same
 Section: graphics
 Depends: ${misc:Depends},
@@ -154,15 +160,17 @@ Multi-Arch: same
 Depends: ${misc:Depends},
          ${shlibs:Depends},
          gstreamer1.0-audiosink,
-         gstreamer1.0-plugins-base (>= 1.22.0),
-         libsoup2.4-1 (>= 2.48) | libsoup-3.0-0
+         gstreamer1.0-plugins-base (>= 1.24.0),
+         libsoup-3.0-0
 Recommends: gstreamer1.0-x
 Replaces: gstreamer1.0-plugins-bad (<< 1.15.1),
-          gstreamer1.0-plugins-ugly (<< 1.22),
+          gstreamer1.0-plugins-ugly (<< 1.24),
           gstreamer1.0-pulseaudio (<< 1.18.0)
 Breaks: gstreamer1.0-plugins-bad (<< 1.15.1),
-        gstreamer1.0-plugins-ugly (<< 1.22),
-        gstreamer1.0-pulseaudio (<< 1.18.0)
+        gstreamer1.0-plugins-ugly (<< 1.24),
+        gstreamer1.0-pulseaudio (<< 1.18.0),
+        libv4l-0 (<< 1.26.1-4~),
+        libv4lconvert0 (<< 1.26.1-4~)
 XB-GStreamer-Version: ${gstreamer:Version}
 XB-GStreamer-Elements: ${gstreamer:Elements}
 XB-GStreamer-URI-Sources: ${gstreamer:URISources}
diff --git a/debian/gstreamer1.0-plugins-good.install b/debian/gstreamer1.0-plugins-good.install
index a86f838269052dd8383dabf7cdafb668f692b967..99005fe41203e669bfd1b7b765eb66ee43cdb7f4 100644
--- a/debian/gstreamer1.0-plugins-good.install
+++ b/debian/gstreamer1.0-plugins-good.install
@@ -1,6 +1,5 @@
 usr/share/gstreamer-1.0/presets/
 usr/lib/*/gstreamer-1.0/libgstaasink.so
-usr/lib/*/gstreamer-1.0/libgstadaptivedemux2.so
 usr/lib/*/gstreamer-1.0/libgstalaw.so
 usr/lib/*/gstreamer-1.0/libgstalphacolor.so
 usr/lib/*/gstreamer-1.0/libgstalpha.so
@@ -48,7 +47,6 @@ usr/lib/*/gstreamer-1.0/libgstrtsp.so
 usr/lib/*/gstreamer-1.0/libgstshapewipe.so
 usr/lib/*/gstreamer-1.0/libgstshout2.so
 usr/lib/*/gstreamer-1.0/libgstsmpte.so
-usr/lib/*/gstreamer-1.0/libgstsoup.so
 usr/lib/*/gstreamer-1.0/libgstspectrum.so
 usr/lib/*/gstreamer-1.0/libgstspeex.so
 usr/lib/*/gstreamer-1.0/libgsttaglib.so
@@ -64,4 +62,6 @@ usr/lib/*/gstreamer-1.0/libgstwavparse.so
 usr/lib/*/gstreamer-1.0/libgstximagesrc.so
 usr/lib/*/gstreamer-1.0/libgstxingmux.so
 usr/lib/*/gstreamer-1.0/libgsty4menc.so
+usr/lib/*/gstreamer-1.0/libgstamrnb.so
+usr/lib/*/gstreamer-1.0/libgstamrwbdec.so
 usr/share/locale
diff --git a/debian/patches/0000_remove-flv-test.patch b/debian/patches/0000_remove-flv-test.patch
new file mode 100644
index 0000000000000000000000000000000000000000..cb4a559b08c159ab66130dcdb64c30943a133978
--- /dev/null
+++ b/debian/patches/0000_remove-flv-test.patch
@@ -0,0 +1,18 @@
+Description: disable unstable test for flvmux
+ This test fails during build; disable for the time being.
+Author: Marc Leeman <marc.leeman@gmail.com>
+Last-Update: 2024-03-04
+
+Index: gst-plugins-good1.0/tests/check/meson.build
+===================================================================
+--- gst-plugins-good1.0.orig/tests/check/meson.build
++++ gst-plugins-good1.0/tests/check/meson.build
+@@ -40,7 +40,7 @@ good_tests = [
+   [ 'elements/deinterlace', get_option('deinterlace').disabled()],
+   [ 'elements/dtmf', get_option('dtmf').disabled()],
+   [ 'elements/flvdemux', get_option('flv').disabled()],
+-  [ 'elements/flvmux', get_option('flv').disabled()],
++  [ 'elements/flvmux', true, ],
+   [ 'elements/hlsdemux_m3u8' , not hls_dep.found() or not adaptivedemux2_dep.found(), [hls_dep, adaptivedemux2_dep] ],
+   [ 'elements/mulawdec', get_option('law').disabled()],
+   [ 'elements/mulawenc', get_option('law').disabled()],
diff --git a/debian/patches/GST-2023-0001.patch b/debian/patches/GST-2023-0001.patch
deleted file mode 100644
index e29feae89536e5aa699b41de0ccd3a5485c86e5f..0000000000000000000000000000000000000000
--- a/debian/patches/GST-2023-0001.patch
+++ /dev/null
@@ -1,29 +0,0 @@
---- gst-plugins-good1.0-1.22.0.orig/gst/audioparsers/gstflacparse.c
-+++ gst-plugins-good1.0-1.22.0/gst/audioparsers/gstflacparse.c
-@@ -1111,6 +1111,7 @@ gst_flac_parse_handle_picture (GstFlacPa
-   GstMapInfo map;
-   guint32 img_len = 0, img_type = 0;
-   guint32 img_mimetype_len = 0, img_description_len = 0;
-+  const guint8 *img_data;
- 
-   gst_buffer_map (buffer, &map, GST_MAP_READ);
-   gst_byte_reader_init (&reader, map.data, map.size);
-@@ -1137,7 +1138,7 @@ gst_flac_parse_handle_picture (GstFlacPa
-   if (!gst_byte_reader_get_uint32_be (&reader, &img_len))
-     goto error;
- 
--  if (gst_byte_reader_get_pos (&reader) + img_len > map.size)
-+  if (!gst_byte_reader_get_data (&reader, img_len, &img_data))
-     goto error;
- 
-   GST_INFO_OBJECT (flacparse, "Got image of %d bytes", img_len);
-@@ -1146,8 +1147,7 @@ gst_flac_parse_handle_picture (GstFlacPa
-     if (flacparse->tags == NULL)
-       flacparse->tags = gst_tag_list_new_empty ();
- 
--    gst_tag_list_add_id3_image (flacparse->tags,
--        map.data + gst_byte_reader_get_pos (&reader), img_len, img_type);
-+    gst_tag_list_add_id3_image (flacparse->tags, img_data, img_len, img_type);
-   }
- 
-   gst_buffer_unmap (buffer, &map);
diff --git a/debian/patches/Skip-failing-tests.patch b/debian/patches/Skip-failing-tests.patch
deleted file mode 100644
index f21010de7fb2ca76546211e9a7581ee2549d7fd4..0000000000000000000000000000000000000000
--- a/debian/patches/Skip-failing-tests.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From: Jeremy Bicha <jeremy.bicha@canonical.com>
-Date: Mon, 16 May 2022 13:13:11 -0400
-Subject: Skip failing tests
-
-The rtpvp8 test is new for 1.20 but fails.
-The rtpstorage test isn't new but started failing with 1.20
-
-https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1220
-https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1221
----
- tests/check/meson.build | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/tests/check/meson.build b/tests/check/meson.build
-index b44f1a0..5776067 100644
---- a/tests/check/meson.build
-+++ b/tests/check/meson.build
-@@ -90,7 +90,7 @@ if not get_option('rtp').disabled() and not get_option('rtpmanager').disabled()
-     [ 'elements/rtph264' ],
-     [ 'elements/rtph265' ],
-     [ 'elements/rtpopus' ],
--    [ 'elements/rtpvp8' ],
-+    [ 'elements/rtpvp8', true, ],
-     [ 'elements/rtpvp9' ],
-     [ 'elements/rtpbin' ],
-     [ 'elements/rtpbin_buffer_list' ],
-@@ -107,7 +107,7 @@ if not get_option('rtp').disabled() and not get_option('rtpmanager').disabled()
-     [ 'elements/rtpptdemux' ],
-     [ 'elements/rtprtx' ],
-     [ 'elements/rtpsession' ],
--    [ 'elements/rtpstorage', false, [],  ['../../gst/rtp/gstrtpstorage.c',
-+    [ 'elements/rtpstorage', true, [],  ['../../gst/rtp/gstrtpstorage.c',
- 					'../../gst/rtp/gstrtpelement.c',
- 					'../../gst/rtp/gstrtputils.c',
- 					'../../gst/rtp/rtpstorage.c',
diff --git a/debian/patches/avisubtitle-Fix-size-checks-and-avoid-overflows-when.patch b/debian/patches/avisubtitle-Fix-size-checks-and-avoid-overflows-when.patch
deleted file mode 100644
index e3b07a39929ff3061b5353b7c2109bdb6a65e491..0000000000000000000000000000000000000000
--- a/debian/patches/avisubtitle-Fix-size-checks-and-avoid-overflows-when.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 14:04:03 +0300
-Subject: avisubtitle: Fix size checks and avoid overflows when checking sizes
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/98c2175d255bd2459d7645ac6aee50be5cb57fe3
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47774
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-262
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3890
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8055>
----
- subprojects/gst-plugins-good/gst/avi/gstavisubtitle.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
---- a/gst/avi/gstavisubtitle.c
-+++ b/gst/avi/gstavisubtitle.c
-@@ -196,7 +196,7 @@ gst_avi_subtitle_parse_gab2_chunk (GstAv
-   /* read 'name' of subtitle */
-   name_length = GST_READ_UINT32_LE (map.data + 5 + 2);
-   GST_LOG_OBJECT (sub, "length of name: %u", name_length);
--  if (map.size <= 17 + name_length)
-+  if (G_MAXUINT32 - 17 < name_length || map.size < 17 + name_length)
-     goto wrong_name_length;
- 
-   name_utf8 =
-@@ -216,7 +216,8 @@ gst_avi_subtitle_parse_gab2_chunk (GstAv
-   file_length = GST_READ_UINT32_LE (map.data + 13 + name_length);
-   GST_LOG_OBJECT (sub, "length srt/ssa file: %u", file_length);
- 
--  if (map.size < (17 + name_length + file_length))
-+  if (G_MAXUINT32 - 17 - name_length < file_length
-+      || map.size < 17 + name_length + file_length)
-     goto wrong_total_length;
- 
-   /* store this, so we can send it again after a seek; note that we shouldn't
diff --git a/debian/patches/gdkpixbufdec-Check-if-initializing-the-video-info-ac.patch b/debian/patches/gdkpixbufdec-Check-if-initializing-the-video-info-ac.patch
deleted file mode 100644
index 8abde57e473da44aac2d70c4513bfb7dc937e820..0000000000000000000000000000000000000000
--- a/debian/patches/gdkpixbufdec-Check-if-initializing-the-video-info-ac.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Wed, 2 Oct 2024 14:44:21 +0300
-Subject: gdkpixbufdec: Check if initializing the video info actually succeeded
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/5106dc94fb9b2d8bd0db547e2c325244b7c1f32c
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47613
-
-Otherwise a 0-byte buffer would be allocated, which gives NULL memory when
-mapped.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-118
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3876
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8053>
----
- .../gst-plugins-good/ext/gdk_pixbuf/gstgdkpixbufdec.c    | 9 ++++++++-
- 1 file changed, 8 insertions(+), 1 deletion(-)
-
---- a/ext/gdk_pixbuf/gstgdkpixbufdec.c
-+++ b/ext/gdk_pixbuf/gstgdkpixbufdec.c
-@@ -322,7 +322,8 @@ gst_gdk_pixbuf_dec_flush (GstGdkPixbufDe
- 
- 
-     gst_video_info_init (&info);
--    gst_video_info_set_format (&info, fmt, width, height);
-+    if (!gst_video_info_set_format (&info, fmt, width, height))
-+      goto format_not_supported;
-     info.fps_n = filter->in_fps_n;
-     info.fps_d = filter->in_fps_d;
-     caps = gst_video_info_to_caps (&info);
-@@ -384,6 +385,12 @@ channels_not_supported:
-         ("%d channels not supported", n_channels));
-     return GST_FLOW_ERROR;
-   }
-+format_not_supported:
-+  {
-+    GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
-+        ("%d channels with %dx%d not supported", n_channels, width, height));
-+    return GST_FLOW_ERROR;
-+  }
- no_buffer:
-   {
-     GST_DEBUG ("Failed to create outbuffer - %s", gst_flow_get_name (ret));
diff --git a/debian/patches/jpegdec-Directly-error-out-on-negotiation-failures.patch b/debian/patches/jpegdec-Directly-error-out-on-negotiation-failures.patch
deleted file mode 100644
index 3cefb86c48e8f17c81a32049e8c65bcf14647efd..0000000000000000000000000000000000000000
--- a/debian/patches/jpegdec-Directly-error-out-on-negotiation-failures.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 16:22:19 +0300
-Subject: jpegdec: Directly error out on negotiation failures
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/8b1c866e93749fd42d1908ec77a4f339343acbb2
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47599
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-247
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3862
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8052>
----
- .../gst-plugins-good/ext/jpeg/gstjpegdec.c    | 22 ++++++++++++++-----
- 1 file changed, 17 insertions(+), 5 deletions(-)
-
---- a/ext/jpeg/gstjpegdec.c
-+++ b/ext/jpeg/gstjpegdec.c
-@@ -1068,13 +1068,14 @@ gst_jpeg_turbo_parse_ext_fmt_convert (Gs
- }
- #endif
- 
--static void
-+static gboolean
- gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc,
-     gboolean interlaced)
- {
-   GstVideoCodecState *outstate;
-   GstVideoInfo *info;
-   GstVideoFormat format;
-+  gboolean res;
- 
- #ifdef JCS_EXTENSIONS
-   if (dec->format_convert) {
-@@ -1104,7 +1105,7 @@ gst_jpeg_dec_negotiate (GstJpegDec * dec
-         height == GST_VIDEO_INFO_HEIGHT (info) &&
-         format == GST_VIDEO_INFO_FORMAT (info)) {
-       gst_video_codec_state_unref (outstate);
--      return;
-+      return TRUE;
-     }
-     gst_video_codec_state_unref (outstate);
-   }
-@@ -1118,6 +1119,8 @@ gst_jpeg_dec_negotiate (GstJpegDec * dec
-   outstate =
-       gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec), format,
-       width, height, dec->input_state);
-+  if (!outstate)
-+    return FALSE;
- 
-   switch (clrspc) {
-     case JCS_RGB:
-@@ -1142,10 +1145,12 @@ gst_jpeg_dec_negotiate (GstJpegDec * dec
- 
-   gst_video_codec_state_unref (outstate);
- 
--  gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec));
-+  res = gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec));
- 
-   GST_DEBUG_OBJECT (dec, "max_v_samp_factor=%d", dec->cinfo.max_v_samp_factor);
-   GST_DEBUG_OBJECT (dec, "max_h_samp_factor=%d", dec->cinfo.max_h_samp_factor);
-+
-+  return res;
- }
- 
- static GstFlowReturn
-@@ -1424,8 +1429,9 @@ gst_jpeg_dec_handle_frame (GstVideoDecod
-     num_fields = 1;
-   }
- 
--  gst_jpeg_dec_negotiate (dec, width, output_height,
--      dec->cinfo.jpeg_color_space, num_fields == 2);
-+  if (!gst_jpeg_dec_negotiate (dec, width, output_height,
-+          dec->cinfo.jpeg_color_space, num_fields == 2))
-+    goto negotiation_failed;
- 
-   state = gst_video_decoder_get_output_state (bdec);
-   ret = gst_video_decoder_allocate_output_frame (bdec, frame);
-@@ -1557,6 +1563,12 @@ map_failed:
-     ret = GST_FLOW_ERROR;
-     goto exit;
-   }
-+negotiation_failed:
-+  {
-+    GST_ELEMENT_ERROR (dec, CORE, NEGOTIATION, (NULL), ("failed to negotiate"));
-+    ret = GST_FLOW_NOT_NEGOTIATED;
-+    goto exit;
-+  }
- decode_error:
-   {
-     gchar err_msg[JMSG_LENGTH_MAX];
diff --git a/debian/patches/matroskademux-Check-for-big-enough-WavPack-codec-pri.patch b/debian/patches/matroskademux-Check-for-big-enough-WavPack-codec-pri.patch
deleted file mode 100644
index f0ceb1fcf808f8053c41a5fee984abaf7d779941..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Check-for-big-enough-WavPack-codec-pri.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 18:25:53 +0300
-Subject: matroskademux: Check for big enough WavPack codec private data before
- accessing it
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/eec4043430d30956ad4aea02a7b67a5758d99f11
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47602
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-250
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3866
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- subprojects/gst-plugins-good/gst/matroska/matroska-demux.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/gst/matroska/matroska-demux.c
-+++ b/gst/matroska/matroska-demux.c
-@@ -3888,6 +3888,11 @@ gst_matroska_demux_add_wvpk_header (GstE
-   guint8 *buf_data, *data;
-   Wavpack4Header wvh;
- 
-+  if (!stream->codec_priv || stream->codec_priv_size < 2) {
-+    GST_ERROR_OBJECT (element, "No or too small wavpack codec private data");
-+    return GST_FLOW_ERROR;
-+  }
-+
-   wvh.ck_id[0] = 'w';
-   wvh.ck_id[1] = 'v';
-   wvh.ck_id[2] = 'p';
diff --git a/debian/patches/matroskademux-Don-t-take-data-out-of-an-empty-adapte.patch b/debian/patches/matroskademux-Don-t-take-data-out-of-an-empty-adapte.patch
deleted file mode 100644
index ed84d9c74e96ca7ecfba8aaef43dcc2d8f92f82f..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Don-t-take-data-out-of-an-empty-adapte.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 19:04:51 +0300
-Subject: matroskademux: Don't take data out of an empty adapter when
- processing WavPack frames
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/2dcb071d4995032ed9242bb863189939b211f5cc
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47601
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-249
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3865
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- .../gst-plugins-good/gst/matroska/matroska-demux.c    | 11 ++++++++---
- 1 file changed, 8 insertions(+), 3 deletions(-)
-
---- a/gst/matroska/matroska-demux.c
-+++ b/gst/matroska/matroska-demux.c
-@@ -4036,11 +4036,16 @@ gst_matroska_demux_add_wvpk_header (GstE
-     }
-     gst_buffer_unmap (*buf, &map);
- 
--    newbuf = gst_adapter_take_buffer (adapter, gst_adapter_available (adapter));
-+    size = gst_adapter_available (adapter);
-+    if (size > 0) {
-+      newbuf = gst_adapter_take_buffer (adapter, size);
-+      gst_buffer_copy_into (newbuf, *buf,
-+          GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS, 0, -1);
-+    } else {
-+      newbuf = NULL;
-+    }
-     g_object_unref (adapter);
- 
--    gst_buffer_copy_into (newbuf, *buf,
--        GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS, 0, -1);
-     gst_buffer_unref (*buf);
-     *buf = newbuf;
- 
diff --git a/debian/patches/matroskademux-Fix-off-by-one-when-parsing-multi-chan.patch b/debian/patches/matroskademux-Fix-off-by-one-when-parsing-multi-chan.patch
deleted file mode 100644
index d80641a8fa444603f653eebbd1a8fab39f27fe18..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Fix-off-by-one-when-parsing-multi-chan.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 16:33:39 +0300
-Subject: matroskademux: Fix off-by-one when parsing multi-channel WavPack
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/816a970a042c96669da25b7a046f0ab8311a78d9
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- subprojects/gst-plugins-good/gst/matroska/matroska-demux.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/gst/matroska/matroska-demux.c
-+++ b/gst/matroska/matroska-demux.c
-@@ -3970,7 +3970,7 @@ gst_matroska_demux_add_wvpk_header (GstE
-     data += 4;
-     size -= 4;
- 
--    while (size > 12) {
-+    while (size >= 12) {
-       flags = GST_READ_UINT32_LE (data);
-       data += 4;
-       size -= 4;
diff --git a/debian/patches/matroskademux-Only-unmap-GstMapInfo-in-WavPack-heade.patch b/debian/patches/matroskademux-Only-unmap-GstMapInfo-in-WavPack-heade.patch
deleted file mode 100644
index 4ff3f9c2b8bf863a65d222a177e7076b7b66b827..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Only-unmap-GstMapInfo-in-WavPack-heade.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 16:32:48 +0300
-Subject: matroskademux: Only unmap GstMapInfo in WavPack header extraction
- error paths if previously mapped
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/a16851ebf34a9f9be4285b2c0d75fe7844354efe
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47540
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-197
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3863
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- subprojects/gst-plugins-good/gst/matroska/matroska-demux.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/gst/matroska/matroska-demux.c
-+++ b/gst/matroska/matroska-demux.c
-@@ -3885,7 +3885,6 @@ gst_matroska_demux_add_wvpk_header (GstE
-   GstMatroskaTrackAudioContext *audiocontext =
-       (GstMatroskaTrackAudioContext *) stream;
-   GstBuffer *newbuf = NULL;
--  GstMapInfo map, outmap;
-   guint8 *buf_data, *data;
-   Wavpack4Header wvh;
- 
-@@ -3902,11 +3901,11 @@ gst_matroska_demux_add_wvpk_header (GstE
- 
-   if (audiocontext->channels <= 2) {
-     guint32 block_samples, tmp;
-+    GstMapInfo outmap;
-     gsize size = gst_buffer_get_size (*buf);
- 
-     if (size < 4) {
-       GST_ERROR_OBJECT (element, "Too small wavpack buffer");
--      gst_buffer_unmap (*buf, &map);
-       return GST_FLOW_ERROR;
-     }
- 
-@@ -3944,6 +3943,7 @@ gst_matroska_demux_add_wvpk_header (GstE
-     *buf = newbuf;
-     audiocontext->wvpk_block_index += block_samples;
-   } else {
-+    GstMapInfo map, outmap;
-     guint8 *outdata = NULL;
-     gsize buf_size, size;
-     guint32 block_samples, flags, crc;
diff --git a/debian/patches/matroskademux-Put-a-copy-of-the-codec-data-into-the-.patch b/debian/patches/matroskademux-Put-a-copy-of-the-codec-data-into-the-.patch
deleted file mode 100644
index 43ff1c71ffa6516fde277b1795130c2f31747472..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Put-a-copy-of-the-codec-data-into-the-.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Wed, 9 Oct 2024 11:52:52 -0400
-Subject: matroskademux: Put a copy of the codec data into the A_MS/ACM caps
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/2c9abe111bd9122967784ef2b55c9017dc2682b8
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47834
-
-The original codec data buffer is owned by matroskademux and does not
-necessarily live as long as the caps.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-280
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3894
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- subprojects/gst-plugins-good/gst/matroska/matroska-demux.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/gst/matroska/matroska-demux.c
-+++ b/gst/matroska/matroska-demux.c
-@@ -7151,8 +7151,7 @@ gst_matroska_demux_audio_caps (GstMatros
- 
-       /* 18 is the waveformatex size */
-       if (size > 18) {
--        codec_data = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
--            data + 18, size - 18, 0, size - 18, NULL, NULL);
-+        codec_data = gst_buffer_new_memdup (data + 18, size - 18);
-       }
- 
-       if (riff_audio_fmt)
diff --git a/debian/patches/matroskademux-Skip-over-laces-directly-when-postproc.patch b/debian/patches/matroskademux-Skip-over-laces-directly-when-postproc.patch
deleted file mode 100644
index ef0ac11de85837cab6b1e7ddba6727325699feeb..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Skip-over-laces-directly-when-postproc.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 19:06:03 +0300
-Subject: matroskademux: Skip over laces directly when postprocessing the frame
- fails
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/e5ffa9c9778454457665c1ee1c5bcc17ed3537ac
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47601
-
-Otherwise NULL buffers might be handled afterwards.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-249
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3865
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- .../gst-plugins-good/gst/matroska/matroska-demux.c   | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
---- a/gst/matroska/matroska-demux.c
-+++ b/gst/matroska/matroska-demux.c
-@@ -4982,6 +4982,18 @@ gst_matroska_demux_parse_blockgroup_or_s
-       if (stream->postprocess_frame) {
-         GST_LOG_OBJECT (demux, "running post process");
-         ret = stream->postprocess_frame (GST_ELEMENT (demux), stream, &sub);
-+        if (ret != GST_FLOW_OK) {
-+          gst_clear_buffer (&sub);
-+          goto next_lace;
-+        }
-+
-+        if (sub == NULL) {
-+          GST_WARNING_OBJECT (demux,
-+              "Postprocessing buffer with timestamp %" GST_TIME_FORMAT
-+              " for stream %d failed", GST_TIME_ARGS (buffer_timestamp),
-+              stream_num);
-+          goto next_lace;
-+        }
-       }
- 
-       /* At this point, we have a sub-buffer pointing at data within a larger
diff --git a/debian/patches/matroskademux-Skip-over-zero-sized-Xiph-stream-heade.patch b/debian/patches/matroskademux-Skip-over-zero-sized-Xiph-stream-heade.patch
deleted file mode 100644
index 21521dc059c81207261a653ca779c55a39059fe2..0000000000000000000000000000000000000000
--- a/debian/patches/matroskademux-Skip-over-zero-sized-Xiph-stream-heade.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Mon, 30 Sep 2024 19:19:42 +0300
-Subject: matroskademux: Skip over zero-sized Xiph stream headers
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/09803e225de515c8881fd13ed464c23771a4d1a6
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47603
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-251
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3867
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8058>
----
- subprojects/gst-plugins-good/gst/matroska/matroska-ids.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
---- a/gst/matroska/matroska-ids.c
-+++ b/gst/matroska/matroska-ids.c
-@@ -189,8 +189,10 @@ gst_matroska_parse_xiph_stream_headers (
-     if (offset + length[i] > codec_data_size)
-       goto error;
- 
--    hdr = gst_buffer_new_memdup (p + offset, length[i]);
--    gst_buffer_list_add (list, hdr);
-+    if (length[i] > 0) {
-+      hdr = gst_buffer_new_memdup (p + offset, length[i]);
-+      gst_buffer_list_add (list, hdr);
-+    }
- 
-     offset += length[i];
-   }
diff --git a/debian/patches/qtdemux-Actually-handle-errors-returns-from-various-.patch b/debian/patches/qtdemux-Actually-handle-errors-returns-from-various-.patch
deleted file mode 100644
index 5343e38df12512688e55e12a36ea94cfe610bb47..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Actually-handle-errors-returns-from-various-.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 27 Sep 2024 10:39:30 +0300
-Subject: qtdemux: Actually handle errors returns from various functions
- instead of ignoring them
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/83056792a8bd179d7e4ba4b3d234ab75205e47d2
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47597
-
-Ignoring them might cause the element to continue as if all is fine despite the
-internal state being inconsistent. This can lead to all kinds of follow-up
-issues, including memory safety issues.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-245
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3847
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- .../gst-plugins-good/gst/isomp4/qtdemux.c     | 29 +++++++++++++++----
- 1 file changed, 23 insertions(+), 6 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -4811,10 +4811,15 @@ gst_qtdemux_loop_state_header (GstQTDemu
- beach:
-   if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
-     /* digested all data, show what we have */
--    qtdemux_prepare_streams (qtdemux);
-+    ret = qtdemux_prepare_streams (qtdemux);
-+    if (ret != GST_FLOW_OK)
-+      return ret;
-+
-     QTDEMUX_EXPOSE_LOCK (qtdemux);
-     ret = qtdemux_expose_streams (qtdemux);
-     QTDEMUX_EXPOSE_UNLOCK (qtdemux);
-+    if (ret != GST_FLOW_OK)
-+      return ret;
- 
-     qtdemux->state = QTDEMUX_STATE_MOVIE;
-     GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
-@@ -7464,13 +7469,21 @@ gst_qtdemux_process_adapter (GstQTDemux
-             gst_qtdemux_stream_concat (demux,
-                 demux->old_streams, demux->active_streams);
- 
--            qtdemux_parse_moov (demux, data, demux->neededbytes);
-+            if (!qtdemux_parse_moov (demux, data, demux->neededbytes)) {
-+              ret = GST_FLOW_ERROR;
-+              break;
-+            }
-             qtdemux_node_dump (demux, demux->moov_node);
-             qtdemux_parse_tree (demux);
--            qtdemux_prepare_streams (demux);
-+            ret = qtdemux_prepare_streams (demux);
-+            if (ret != GST_FLOW_OK)
-+              break;
-+
-             QTDEMUX_EXPOSE_LOCK (demux);
--            qtdemux_expose_streams (demux);
-+            ret = qtdemux_expose_streams (demux);
-             QTDEMUX_EXPOSE_UNLOCK (demux);
-+            if (ret != GST_FLOW_OK)
-+              break;
- 
-             demux->got_moov = TRUE;
- 
-@@ -7561,8 +7574,10 @@ gst_qtdemux_process_adapter (GstQTDemux
-             /* in MSS we need to expose the pads after the first moof as we won't get a moov */
-             if (demux->mss_mode && !demux->exposed) {
-               QTDEMUX_EXPOSE_LOCK (demux);
--              qtdemux_expose_streams (demux);
-+              ret = qtdemux_expose_streams (demux);
-               QTDEMUX_EXPOSE_UNLOCK (demux);
-+              if (ret != GST_FLOW_OK)
-+                goto done;
-             }
- 
-             gst_qtdemux_check_send_pending_segment (demux);
-@@ -13589,8 +13604,10 @@ qtdemux_prepare_streams (GstQTDemux * qt
- 
-     /* parse the initial sample for use in setting the frame rate cap */
-     while (sample_num == 0 && sample_num < stream->n_samples) {
--      if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
-+      if (!qtdemux_parse_samples (qtdemux, stream, sample_num)) {
-+        ret = GST_FLOW_ERROR;
-         break;
-+      }
-       ++sample_num;
-     }
-   }
diff --git a/debian/patches/qtdemux-Add-size-check-for-parsing-SMI-SEQH-atom.patch b/debian/patches/qtdemux-Add-size-check-for-parsing-SMI-SEQH-atom.patch
deleted file mode 100644
index 555caa722a11b6e83040b0ffeb37591089c839ce..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Add-size-check-for-parsing-SMI-SEQH-atom.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 27 Sep 2024 00:31:36 +0300
-Subject: qtdemux: Add size check for parsing SMI / SEQH atom
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/8603e78a07a307139fd45ee11e7623de01494bf3
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47596
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-244
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3853
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -10545,8 +10545,9 @@ qtdemux_parse_svq3_stsd_data (GstQTDemux
-                 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
-                     " found, ignoring");
-               } else {
-+                /* Note: The size does *not* include the fourcc and the size field itself */
-                 seqh_size = QT_UINT32 (data + 4);
--                if (seqh_size > 0) {
-+                if (seqh_size > 0 && seqh_size <= size - 8) {
-                   _seqh = gst_buffer_new_and_alloc (seqh_size);
-                   gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
-                 }
diff --git a/debian/patches/qtdemux-Avoid-integer-overflow-when-parsing-Theora-e.patch b/debian/patches/qtdemux-Avoid-integer-overflow-when-parsing-Theora-e.patch
deleted file mode 100644
index dbf1c97c61673d07bba405c30f82b5ddcfe503c5..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Avoid-integer-overflow-when-parsing-Theora-e.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Thu, 26 Sep 2024 22:16:06 +0300
-Subject: qtdemux: Avoid integer overflow when parsing Theora extension
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/2d7a11f5e6be5c323b2fed8158bc9df37752e495
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47606
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-166
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3851
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8044>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -8172,7 +8172,7 @@ qtdemux_parse_theora_extension (GstQTDem
-   end -= 8;
- 
-   while (buf < end) {
--    gint size;
-+    guint32 size;
-     guint32 type;
- 
-     size = QT_UINT32 (buf);
-@@ -8180,7 +8180,7 @@ qtdemux_parse_theora_extension (GstQTDem
- 
-     GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
- 
--    if (buf + size > end || size <= 0)
-+    if (end - buf < size || size < 8)
-       break;
- 
-     buf += 8;
diff --git a/debian/patches/qtdemux-Check-for-invalid-atom-length-when-extractin.patch b/debian/patches/qtdemux-Check-for-invalid-atom-length-when-extractin.patch
deleted file mode 100644
index c699f274baa3360a1872bbcb4a1fe702d6372b55..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Check-for-invalid-atom-length-when-extractin.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Thu, 26 Sep 2024 19:16:19 +0300
-Subject: qtdemux: Check for invalid atom length when extracting Closed Caption
- data
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/f31dbbc1bcc00096ab863ee6aaecad493c71c333
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47546
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-243
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3849
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -5780,7 +5780,7 @@ extract_cc_from_data (QtDemuxStream * st
-     goto invalid_cdat;
-   atom_length = QT_UINT32 (data);
-   fourcc = QT_FOURCC (data + 4);
--  if (G_UNLIKELY (atom_length > size || atom_length == 8))
-+  if (G_UNLIKELY (atom_length > size || atom_length <= 8))
-     goto invalid_cdat;
- 
-   GST_DEBUG_OBJECT (stream->pad, "here");
diff --git a/debian/patches/qtdemux-Check-sizes-of-stsc-stco-stts-before-trying-.patch b/debian/patches/qtdemux-Check-sizes-of-stsc-stco-stts-before-trying-.patch
deleted file mode 100644
index 23a67d6fbad52a1ba94f46344e462f64f06c21a4..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Check-sizes-of-stsc-stco-stts-before-trying-.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 27 Sep 2024 15:50:54 +0300
-Subject: qtdemux: Check sizes of stsc/stco/stts before trying to merge entries
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/1def2965d8da8cc74ab0036d7f8d59e81e676cad
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47598
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-246
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3854
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- .../gst-plugins-good/gst/isomp4/qtdemux.c     | 22 +++++++++++++++++++
- 1 file changed, 22 insertions(+)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -9392,6 +9392,21 @@ qtdemux_merge_sample_table (GstQTDemux *
-     return;
-   }
- 
-+  if (gst_byte_reader_get_remaining (&stream->stts) < 8) {
-+    GST_DEBUG_OBJECT (qtdemux, "Too small stts");
-+    return;
-+  }
-+
-+  if (stream->stco.size < 8) {
-+    GST_DEBUG_OBJECT (qtdemux, "Too small stco");
-+    return;
-+  }
-+
-+  if (stream->n_samples_per_chunk == 0) {
-+    GST_DEBUG_OBJECT (qtdemux, "No samples per chunk");
-+    return;
-+  }
-+
-   /* Parse the stts to get the sample duration and number of samples */
-   gst_byte_reader_skip_unchecked (&stream->stts, 4);
-   stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
-@@ -9403,6 +9418,13 @@ qtdemux_merge_sample_table (GstQTDemux *
-   GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
-       num_chunks);
- 
-+  if (gst_byte_reader_get_remaining (&stream->stsc) <
-+      stream->n_samples_per_chunk * 3 * 4 +
-+      (stream->n_samples_per_chunk - 1) * 4) {
-+    GST_DEBUG_OBJECT (qtdemux, "Too small stsc");
-+    return;
-+  }
-+
-   /* Now parse stsc, convert chunks into single samples and generate a
-    * new stsc, stts and stsz from this information */
-   gst_byte_writer_init (&stsc);
diff --git a/debian/patches/qtdemux-Don-t-iterate-over-all-trun-entries-if-none-.patch b/debian/patches/qtdemux-Don-t-iterate-over-all-trun-entries-if-none-.patch
deleted file mode 100644
index 1ff18b08d0e8afedaa0e8b53e35b30f6233ab843..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Don-t-iterate-over-all-trun-entries-if-none-.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Thu, 26 Sep 2024 18:41:39 +0300
-Subject: qtdemux: Don't iterate over all trun entries if none of the flags are
- set
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/eb7f9331c2294bc28a549b79c9f931c3e6c6bc44
-
-Nothing would be printed anyway.
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux_dump.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/gst/isomp4/qtdemux_dump.c
-+++ b/gst/isomp4/qtdemux_dump.c
-@@ -836,6 +836,11 @@ qtdemux_dump_trun (GstQTDemux * qtdemux,
-     GST_LOG ("%*s    first-sample-flags: %u", depth, "", first_sample_flags);
-   }
- 
-+  /* Nothing to print below */
-+  if ((flags & (TR_SAMPLE_DURATION | TR_SAMPLE_SIZE | TR_SAMPLE_FLAGS |
-+              TR_COMPOSITION_TIME_OFFSETS)) == 0)
-+    return TRUE;
-+
-   for (i = 0; i < samples_count; i++) {
-     if (flags & TR_SAMPLE_DURATION) {
-       if (!gst_byte_reader_get_uint32_be (data, &sample_duration))
diff --git a/debian/patches/qtdemux-Fix-debug-output-during-trun-parsing.patch b/debian/patches/qtdemux-Fix-debug-output-during-trun-parsing.patch
deleted file mode 100644
index 7f6783e34c1628e1d6629d91aec9fc1add58cd97..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Fix-debug-output-during-trun-parsing.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Thu, 26 Sep 2024 18:40:56 +0300
-Subject: qtdemux: Fix debug output during trun parsing
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/812f175c580a2e702581859fd481c8f51d633508
-
-Various integers are unsigned so print them as such. Also print the actual
-allocation size if allocation fails, not only parts of it.
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- .../gst-plugins-good/gst/isomp4/qtdemux.c       | 17 +++++++++--------
- 1 file changed, 9 insertions(+), 8 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -3338,8 +3338,8 @@ qtdemux_parse_trun (GstQTDemux * qtdemux
-   gint64 initial_offset;
-   gint32 min_ct = 0;
- 
--  GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
--      "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
-+  GST_LOG_OBJECT (qtdemux, "parsing trun track-id %u; "
-+      "default dur %u, size %u, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
-       "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
-       d_sample_size, d_sample_flags, *base_offset, decode_ts);
- 
-@@ -3367,7 +3367,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux
-     /* note this is really signed */
-     if (!gst_byte_reader_get_int32_be (trun, &data_offset))
-       goto fail;
--    GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
-+    GST_LOG_OBJECT (qtdemux, "trun data offset %u", data_offset);
-     /* default base offset = first byte of moof */
-     if (*base_offset == -1) {
-       GST_LOG_OBJECT (qtdemux, "base_offset at moof");
-@@ -3389,7 +3389,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux
- 
-   GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
-       *running_offset);
--  GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
-+  GST_LOG_OBJECT (qtdemux, "trun offset %u, flags 0x%x, entries %u",
-       data_offset, flags, samples_count);
- 
-   if (flags & TR_FIRST_SAMPLE_FLAGS) {
-@@ -3598,14 +3598,15 @@ fail:
-   }
- out_of_memory:
-   {
--    GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
--        stream->n_samples);
-+    GST_WARNING_OBJECT (qtdemux, "failed to allocate %u + %u samples",
-+        stream->n_samples, samples_count);
-     return FALSE;
-   }
- index_too_big:
-   {
--    GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
--        "be larger than %uMB (broken file?)", stream->n_samples,
-+    GST_WARNING_OBJECT (qtdemux,
-+        "not allocating index of %u + %u samples, would "
-+        "be larger than %uMB (broken file?)", stream->n_samples, samples_count,
-         QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
-     return FALSE;
-   }
diff --git a/debian/patches/qtdemux-Fix-error-handling-when-parsing-cenc-sample-.patch b/debian/patches/qtdemux-Fix-error-handling-when-parsing-cenc-sample-.patch
deleted file mode 100644
index e32e87a84ad489f434c7bb748aa4d00d7e14f331..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Fix-error-handling-when-parsing-cenc-sample-.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 27 Sep 2024 09:47:50 +0300
-Subject: qtdemux: Fix error handling when parsing cenc sample groups fails
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/8e884e4e31649a9fc19095d6501a1143b074aba8
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47544
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-238, GHSL-2024-239, GHSL-2024-240
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3846
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- .../gst-plugins-good/gst/isomp4/qtdemux.c     | 25 ++++++++++++++-----
- 1 file changed, 19 insertions(+), 6 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -11316,12 +11316,15 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-       if (stream->subtype != FOURCC_soun) {
-         GST_ERROR_OBJECT (qtdemux,
-             "Unexpeced stsd type 'aavd' outside 'soun' track");
-+        goto corrupt_file;
-       } else {
-         /* encrypted audio with sound sample description v0 */
-         GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
-         stream->protected = TRUE;
--        if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
-+        if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc)) {
-           GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
-+          goto corrupt_file;
-+        }
-       }
-     }
- 
-@@ -11330,8 +11333,10 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-        * with the same type */
-       GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
-       stream->protected = TRUE;
--      if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
-+      if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc)) {
-         GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
-+        goto corrupt_file;
-+      }
-     }
- 
-     if (stream->subtype == FOURCC_vide) {
diff --git a/debian/patches/qtdemux-Fix-integer-overflow-when-allocating-the-sam.patch b/debian/patches/qtdemux-Fix-integer-overflow-when-allocating-the-sam.patch
deleted file mode 100644
index a69ce104d2c72ded44671d1b8678e8811a70dfb0..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Fix-integer-overflow-when-allocating-the-sam.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From: Antonio Morales <antonio-morales@github.com>
-Date: Thu, 26 Sep 2024 18:39:37 +0300
-Subject: qtdemux: Fix integer overflow when allocating the samples table for
- fragmented MP4
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/c3a2af94c652513ac1b1858295688ac88c5cc737
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47537
-
-This can lead to out of bounds writes and NULL pointer dereferences.
-
-Fixes GHSL-2024-094, GHSL-2024-237, GHSL-2024-241
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3839
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -3332,6 +3332,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux
-   gint i;
-   guint8 *data;
-   guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
-+  guint new_n_samples;
-   QtDemuxSample *sample;
-   gboolean ismv = FALSE;
-   gint64 initial_offset;
-@@ -3432,14 +3433,13 @@ qtdemux_parse_trun (GstQTDemux * qtdemux
-     goto fail;
-   data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
- 
--  if (stream->n_samples + samples_count >=
--      QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
-+  if (!g_uint_checked_add (&new_n_samples, stream->n_samples, samples_count) ||
-+      new_n_samples >= QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
-     goto index_too_big;
- 
-   GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
--      stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
--      (stream->n_samples + samples_count) *
--      sizeof (QtDemuxSample) / (1024.0 * 1024.0));
-+      new_n_samples, (guint) sizeof (QtDemuxSample),
-+      (new_n_samples) * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
- 
-   /* create a new array of samples if it's the first sample parsed */
-   if (stream->n_samples == 0) {
-@@ -3448,7 +3448,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux
-     /* or try to reallocate it with space enough to insert the new samples */
-   } else
-     stream->samples = g_try_renew (QtDemuxSample, stream->samples,
--        stream->n_samples + samples_count);
-+        new_n_samples);
-   if (stream->samples == NULL)
-     goto out_of_memory;
- 
diff --git a/debian/patches/qtdemux-Fix-length-checks-and-offsets-in-stsd-entry-.patch b/debian/patches/qtdemux-Fix-length-checks-and-offsets-in-stsd-entry-.patch
deleted file mode 100644
index 24ba91c99999760149c4f63090f4a55da13678b8..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Fix-length-checks-and-offsets-in-stsd-entry-.patch
+++ /dev/null
@@ -1,418 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 27 Sep 2024 00:12:57 +0300
-Subject: qtdemux: Fix length checks and offsets in stsd entry parsing
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/fe9d5d37234aca04fef7248184177168905a7a69
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47545
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-242
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3845
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- .../gst-plugins-good/gst/isomp4/qtdemux.c     | 218 +++++++-----------
- 1 file changed, 79 insertions(+), 139 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -11595,40 +11595,35 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-           case FOURCC_avc1:
-           case FOURCC_avc3:
-           {
--            guint len = QT_UINT32 (stsd_entry_data);
-+            guint32 len = QT_UINT32 (stsd_entry_data);
-             len = len <= 0x56 ? 0 : len - 0x56;
-             const guint8 *avc_data = stsd_entry_data + 0x56;
- 
-             /* find avcC */
--            while (len >= 0x8) {
--              guint size;
--
--              if (QT_UINT32 (avc_data) <= 0x8)
--                size = 0;
--              else if (QT_UINT32 (avc_data) <= len)
--                size = QT_UINT32 (avc_data) - 0x8;
--              else
--                size = len - 0x8;
-+            while (len >= 8) {
-+              guint32 size = QT_UINT32 (avc_data);
- 
--              if (size < 1)
--                /* No real data, so break out */
-+              if (size < 8 || size > len)
-                 break;
- 
--              switch (QT_FOURCC (avc_data + 0x4)) {
-+              switch (QT_FOURCC (avc_data + 4)) {
-                 case FOURCC_avcC:
-                 {
-                   /* parse, if found */
-                   GstBuffer *buf;
- 
-+                  if (size < 8 + 1)
-+                    break;
-+
-                   GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
- 
-                   /* First 4 bytes are the length of the atom, the next 4 bytes
-                    * are the fourcc, the next 1 byte is the version, and the
-                    * subsequent bytes are profile_tier_level structure like data. */
-                   gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
--                      avc_data + 8 + 1, size - 1);
--                  buf = gst_buffer_new_and_alloc (size);
--                  gst_buffer_fill (buf, 0, avc_data + 0x8, size);
-+                      avc_data + 8 + 1, size - 8 - 1);
-+                  buf = gst_buffer_new_and_alloc (size - 8);
-+                  gst_buffer_fill (buf, 0, avc_data + 8, size - 8);
-                   gst_caps_set_simple (entry->caps,
-                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                   gst_buffer_unref (buf);
-@@ -11639,6 +11634,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                 {
-                   GstBuffer *buf;
- 
-+                  if (size < 8 + 40 + 1)
-+                    break;
-+
-                   GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
- 
-                   /* First 4 bytes are the length of the atom, the next 4 bytes
-@@ -11646,17 +11644,14 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                    * next 1 byte is the version, and the
-                    * subsequent bytes are sequence parameter set like data. */
- 
--                  size -= 40;   /* we'll be skipping BITMAPINFOHEADER */
--                  if (size > 1) {
--                    gst_codec_utils_h264_caps_set_level_and_profile
--                        (entry->caps, avc_data + 8 + 40 + 1, size - 1);
-+                  gst_codec_utils_h264_caps_set_level_and_profile
-+                      (entry->caps, avc_data + 8 + 40 + 1, size - 8 - 40 - 1);
- 
--                    buf = gst_buffer_new_and_alloc (size);
--                    gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
--                    gst_caps_set_simple (entry->caps,
--                        "codec_data", GST_TYPE_BUFFER, buf, NULL);
--                    gst_buffer_unref (buf);
--                  }
-+                  buf = gst_buffer_new_and_alloc (size - 8 - 40);
-+                  gst_buffer_fill (buf, 0, avc_data + 8 + 40, size - 8 - 40);
-+                  gst_caps_set_simple (entry->caps,
-+                      "codec_data", GST_TYPE_BUFFER, buf, NULL);
-+                  gst_buffer_unref (buf);
-                   break;
-                 }
-                 case FOURCC_btrt:
-@@ -11664,11 +11659,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                   guint avg_bitrate, max_bitrate;
- 
-                   /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
--                  if (size < 12)
-+                  if (size < 8 + 12)
-                     break;
- 
--                  max_bitrate = QT_UINT32 (avc_data + 0xc);
--                  avg_bitrate = QT_UINT32 (avc_data + 0x10);
-+                  max_bitrate = QT_UINT32 (avc_data + 8 + 4);
-+                  avg_bitrate = QT_UINT32 (avc_data + 8 + 8);
- 
-                   if (!max_bitrate && !avg_bitrate)
-                     break;
-@@ -11700,8 +11695,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                   break;
-               }
- 
--              len -= size + 8;
--              avc_data += size + 8;
-+              len -= size;
-+              avc_data += size;
-             }
- 
-             break;
-@@ -11712,41 +11707,36 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-           case FOURCC_dvh1:
-           case FOURCC_dvhe:
-           {
--            guint len = QT_UINT32 (stsd_entry_data);
-+            guint32 len = QT_UINT32 (stsd_entry_data);
-             len = len <= 0x56 ? 0 : len - 0x56;
-             const guint8 *hevc_data = stsd_entry_data + 0x56;
- 
-             /* find hevc */
--            while (len >= 0x8) {
--              guint size;
--
--              if (QT_UINT32 (hevc_data) <= 0x8)
--                size = 0;
--              else if (QT_UINT32 (hevc_data) <= len)
--                size = QT_UINT32 (hevc_data) - 0x8;
--              else
--                size = len - 0x8;
-+            while (len >= 8) {
-+              guint32 size = QT_UINT32 (hevc_data);
- 
--              if (size < 1)
--                /* No real data, so break out */
-+              if (size < 8 || size > len)
-                 break;
- 
--              switch (QT_FOURCC (hevc_data + 0x4)) {
-+              switch (QT_FOURCC (hevc_data + 4)) {
-                 case FOURCC_hvcC:
-                 {
-                   /* parse, if found */
-                   GstBuffer *buf;
- 
-+                  if (size < 8 + 1)
-+                    break;
-+
-                   GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
- 
-                   /* First 4 bytes are the length of the atom, the next 4 bytes
-                    * are the fourcc, the next 1 byte is the version, and the
-                    * subsequent bytes are sequence parameter set like data. */
-                   gst_codec_utils_h265_caps_set_level_tier_and_profile
--                      (entry->caps, hevc_data + 8 + 1, size - 1);
-+                      (entry->caps, hevc_data + 8 + 1, size - 8 - 1);
- 
--                  buf = gst_buffer_new_and_alloc (size);
--                  gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
-+                  buf = gst_buffer_new_and_alloc (size - 8);
-+                  gst_buffer_fill (buf, 0, hevc_data + 8, size - 8);
-                   gst_caps_set_simple (entry->caps,
-                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                   gst_buffer_unref (buf);
-@@ -11755,8 +11745,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                 default:
-                   break;
-               }
--              len -= size + 8;
--              hevc_data += size + 8;
-+              len -= size;
-+              hevc_data += size;
-             }
-             break;
-           }
-@@ -12136,33 +12126,25 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-           }
-           case FOURCC_vc_1:
-           {
--            guint len = QT_UINT32 (stsd_entry_data);
-+            guint32 len = QT_UINT32 (stsd_entry_data);
-             len = len <= 0x56 ? 0 : len - 0x56;
-             const guint8 *vc1_data = stsd_entry_data + 0x56;
- 
-             /* find dvc1 */
-             while (len >= 8) {
--              guint size;
--
--              if (QT_UINT32 (vc1_data) <= 8)
--                size = 0;
--              else if (QT_UINT32 (vc1_data) <= len)
--                size = QT_UINT32 (vc1_data) - 8;
--              else
--                size = len - 8;
-+              guint32 size = QT_UINT32 (vc1_data);
- 
--              if (size < 1)
--                /* No real data, so break out */
-+              if (size < 8 || size > len)
-                 break;
- 
--              switch (QT_FOURCC (vc1_data + 0x4)) {
-+              switch (QT_FOURCC (vc1_data + 4)) {
-                 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
-                 {
-                   GstBuffer *buf;
- 
-                   GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
--                  buf = gst_buffer_new_and_alloc (size);
--                  gst_buffer_fill (buf, 0, vc1_data + 8, size);
-+                  buf = gst_buffer_new_and_alloc (size - 8);
-+                  gst_buffer_fill (buf, 0, vc1_data + 8, size - 8);
-                   gst_caps_set_simple (entry->caps,
-                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                   gst_buffer_unref (buf);
-@@ -12171,33 +12153,25 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                 default:
-                   break;
-               }
--              len -= size + 8;
--              vc1_data += size + 8;
-+              len -= size;
-+              vc1_data += size;
-             }
-             break;
-           }
-           case FOURCC_av01:
-           {
--            guint len = QT_UINT32 (stsd_entry_data);
-+            guint32 len = QT_UINT32 (stsd_entry_data);
-             len = len <= 0x56 ? 0 : len - 0x56;
-             const guint8 *av1_data = stsd_entry_data + 0x56;
- 
-             /* find av1C */
--            while (len >= 0x8) {
--              guint size;
--
--              if (QT_UINT32 (av1_data) <= 0x8)
--                size = 0;
--              else if (QT_UINT32 (av1_data) <= len)
--                size = QT_UINT32 (av1_data) - 0x8;
--              else
--                size = len - 0x8;
-+            while (len >= 8) {
-+              guint32 size = QT_UINT32 (av1_data);
- 
--              if (size < 1)
--                /* No real data, so break out */
-+              if (size < 8 || size > len)
-                 break;
- 
--              switch (QT_FOURCC (av1_data + 0x4)) {
-+              switch (QT_FOURCC (av1_data + 4)) {
-                 case FOURCC_av1C:
-                 {
-                   /* parse, if found */
-@@ -12208,7 +12182,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                       "found av1C codec_data in stsd of size %d", size);
- 
-                   /* not enough data, just ignore and hope for the best */
--                  if (size < 5)
-+                  if (size < 8 + 5)
-                     break;
- 
-                   /* Content is:
-@@ -12234,10 +12208,10 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                         "presentation-delay", G_TYPE_INT,
-                         (gint) (pres_delay_field & 0x0F) + 1, NULL);
-                   }
--                  if (size > 5) {
--                    buf = gst_buffer_new_and_alloc (size - 5);
-+                  if (size > 8 + 5) {
-+                    buf = gst_buffer_new_and_alloc (size - 8 - 5);
-                     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
--                    gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
-+                    gst_buffer_fill (buf, 0, av1_data + 13, size - 8 - 5);
-                     gst_caps_set_simple (entry->caps,
-                         "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                     gst_buffer_unref (buf);
-@@ -12248,8 +12222,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                   break;
-               }
- 
--              len -= size + 8;
--              av1_data += size + 8;
-+              len -= size;
-+              av1_data += size;
-             }
- 
-             break;
-@@ -12260,26 +12234,18 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-              * vp08, vp09, and vp10 fourcc. */
-           case FOURCC_vp09:
-           {
--            guint len = QT_UINT32 (stsd_entry_data);
-+            guint32 len = QT_UINT32 (stsd_entry_data);
-             len = len <= 0x56 ? 0 : len - 0x56;
-             const guint8 *vpcc_data = stsd_entry_data + 0x56;
- 
-             /* find vpcC */
--            while (len >= 0x8) {
--              guint size;
--
--              if (QT_UINT32 (vpcc_data) <= 0x8)
--                size = 0;
--              else if (QT_UINT32 (vpcc_data) <= len)
--                size = QT_UINT32 (vpcc_data) - 0x8;
--              else
--                size = len - 0x8;
-+            while (len >= 8) {
-+              guint32 size = QT_UINT32 (vpcc_data);
- 
--              if (size < 1)
--                /* No real data, so break out */
-+              if (size < 8 || size > len)
-                 break;
- 
--              switch (QT_FOURCC (vpcc_data + 0x4)) {
-+              switch (QT_FOURCC (vpcc_data + 4)) {
-                 case FOURCC_vpcC:
-                 {
-                   const gchar *profile_str = NULL;
-@@ -12295,7 +12261,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
- 
-                   /* the meaning of "size" is length of the atom body, excluding
-                    * atom length and fourcc fields */
--                  if (size < 12)
-+                  if (size < 8 + 12)
-                     break;
- 
-                   /* Content is:
-@@ -12401,8 +12367,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                   break;
-               }
- 
--              len -= size + 8;
--              vpcc_data += size + 8;
-+              len -= size;
-+              vpcc_data += size;
-             }
- 
-             break;
-@@ -12733,7 +12699,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-         }
-         case FOURCC_wma_:
-         {
--          guint len = QT_UINT32 (stsd_entry_data);
-+          guint32 len = QT_UINT32 (stsd_entry_data);
-           len = len <= offset ? 0 : len - offset;
-           const guint8 *wfex_data = stsd_entry_data + offset;
-           const gchar *codec_name = NULL;
-@@ -12758,17 +12724,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
- 
-           /* find wfex */
-           while (len >= 8) {
--            guint size;
--
--            if (QT_UINT32 (wfex_data) <= 0x8)
--              size = 0;
--            else if (QT_UINT32 (wfex_data) <= len)
--              size = QT_UINT32 (wfex_data) - 8;
--            else
--              size = len - 8;
-+            guint32 size = QT_UINT32 (wfex_data);
- 
--            if (size < 1)
--              /* No real data, so break out */
-+            if (size < 8 || size > len)
-               break;
- 
-             switch (QT_FOURCC (wfex_data + 4)) {
-@@ -12814,12 +12772,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-                     "width", G_TYPE_INT, wfex.wBitsPerSample,
-                     "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
- 
--                if (size > wfex.cbSize) {
-+                if (size > 8 + wfex.cbSize) {
-                   GstBuffer *buf;
- 
--                  buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
-+                  buf = gst_buffer_new_and_alloc (size - 8 - wfex.cbSize);
-                   gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
--                      size - wfex.cbSize);
-+                      size - 8 - wfex.cbSize);
-                   gst_caps_set_simple (entry->caps,
-                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                   gst_buffer_unref (buf);
-@@ -12836,8 +12794,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-               default:
-                 break;
-             }
--            len -= size + 8;
--            wfex_data += size + 8;
-+            len -= size;
-+            wfex_data += size;
-           }
-           break;
-         }
diff --git a/debian/patches/qtdemux-Make-sure-enough-data-is-available-before-re.patch b/debian/patches/qtdemux-Make-sure-enough-data-is-available-before-re.patch
deleted file mode 100644
index 1e7681cbc79cdb0f12c037c617790c33b2fa9d5f..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Make-sure-enough-data-is-available-before-re.patch
+++ /dev/null
@@ -1,111 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Thu, 26 Sep 2024 14:17:02 +0300
-Subject: qtdemux: Make sure enough data is available before reading wave
- header node
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/8ef08a7a41da987aa630082df355ea651aa09132
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47543
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-236
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3843
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- .../gst-plugins-good/gst/isomp4/qtdemux.c     | 84 ++++++++++---------
- 1 file changed, 45 insertions(+), 39 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -12935,47 +12935,53 @@ qtdemux_parse_trak (GstQTDemux * qtdemux
-         } else {
-           guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
-           const guint8 *data = stsd_entry_data + offset + 16;
--          GNode *wavenode;
--          GNode *waveheadernode;
- 
--          wavenode = g_node_new ((guint8 *) data);
--          if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
--            const guint8 *waveheader;
--            guint32 headerlen;
--
--            waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
--            if (waveheadernode) {
--              waveheader = (const guint8 *) waveheadernode->data;
--              headerlen = QT_UINT32 (waveheader);
--
--              if (headerlen > 8) {
--                gst_riff_strf_auds *header = NULL;
--                GstBuffer *headerbuf;
--                GstBuffer *extra;
--
--                waveheader += 8;
--                headerlen -= 8;
--
--                headerbuf = gst_buffer_new_and_alloc (headerlen);
--                gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
--
--                if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
--                        headerbuf, &header, &extra)) {
--                  gst_caps_unref (entry->caps);
--                  /* FIXME: Need to do something with the channel reorder map */
--                  entry->caps =
--                      gst_riff_create_audio_caps (header->format, NULL, header,
--                      extra, NULL, NULL, NULL);
--
--                  if (extra)
--                    gst_buffer_unref (extra);
--                  g_free (header);
-+          if (len < datalen || len - datalen < offset + 16) {
-+            GST_WARNING_OBJECT (qtdemux, "Not enough data for waveheadernode");
-+          } else {
-+            GNode *wavenode;
-+            GNode *waveheadernode;
-+
-+            wavenode = g_node_new ((guint8 *) data);
-+            if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
-+              const guint8 *waveheader;
-+              guint32 headerlen;
-+
-+              waveheadernode =
-+                  qtdemux_tree_get_child_by_type (wavenode, fourcc);
-+              if (waveheadernode) {
-+                waveheader = (const guint8 *) waveheadernode->data;
-+                headerlen = QT_UINT32 (waveheader);
-+
-+                if (headerlen > 8) {
-+                  gst_riff_strf_auds *header = NULL;
-+                  GstBuffer *headerbuf;
-+                  GstBuffer *extra;
-+
-+                  waveheader += 8;
-+                  headerlen -= 8;
-+
-+                  headerbuf = gst_buffer_new_and_alloc (headerlen);
-+                  gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
-+
-+                  if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
-+                          headerbuf, &header, &extra)) {
-+                    gst_caps_unref (entry->caps);
-+                    /* FIXME: Need to do something with the channel reorder map */
-+                    entry->caps =
-+                        gst_riff_create_audio_caps (header->format, NULL,
-+                        header, extra, NULL, NULL, NULL);
-+
-+                    if (extra)
-+                      gst_buffer_unref (extra);
-+                    g_free (header);
-+                  }
-                 }
--              }
--            } else
--              GST_DEBUG ("Didn't find waveheadernode for this codec");
-+              } else
-+                GST_DEBUG ("Didn't find waveheadernode for this codec");
-+            }
-+            g_node_destroy (wavenode);
-           }
--          g_node_destroy (wavenode);
-         }
-       } else if (esds) {
-         gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
diff --git a/debian/patches/qtdemux-Make-sure-only-an-even-number-of-bytes-is-pr.patch b/debian/patches/qtdemux-Make-sure-only-an-even-number-of-bytes-is-pr.patch
deleted file mode 100644
index f32b5c188658bbd5769cbc91b6d783f8376e22ad..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Make-sure-only-an-even-number-of-bytes-is-pr.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Thu, 26 Sep 2024 09:20:28 +0300
-Subject: qtdemux: Make sure only an even number of bytes is processed when
- handling CEA608 data
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/314945426c7105ad90f44a188037bc43bb3b0300
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47539
-
-An odd number of bytes would lead to out of bound reads and writes, and doesn't
-make any sense as CEA608 comes in byte pairs.
-
-Strip off any leftover bytes and assume everything before that is valid.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-195
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3841
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -5737,6 +5737,11 @@ convert_to_s334_1a (const guint8 * ccpai
-   guint8 *storage;
-   gsize i;
- 
-+  /* Strip off any leftover odd bytes and assume everything before is valid */
-+  if (ccpair_size % 2 != 0) {
-+    ccpair_size -= 1;
-+  }
-+
-   /* We are converting from pairs to triplets */
-   *res = ccpair_size / 2 * 3;
-   storage = g_malloc (*res);
diff --git a/debian/patches/qtdemux-Make-sure-there-are-enough-offsets-to-read-w.patch b/debian/patches/qtdemux-Make-sure-there-are-enough-offsets-to-read-w.patch
deleted file mode 100644
index e8d7aab7f9fcbd797a344c9b022db980a1576b42..0000000000000000000000000000000000000000
--- a/debian/patches/qtdemux-Make-sure-there-are-enough-offsets-to-read-w.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 27 Sep 2024 10:38:50 +0300
-Subject: qtdemux: Make sure there are enough offsets to read when parsing
- samples
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/7f8f280555201f51898727919831259e68271868
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47597
-
-While this specific case is also caught when initializing co_chunk, the error
-is ignored in various places and calling into the function would lead to out of
-bounds reads if the error message doesn't cause the pipeline to be shut down
-fast enough.
-
-To avoid this, no matter what, make sure enough offsets are available when
-parsing them. While this is potentially slower, the same is already done in the
-non-chunks_are_samples case.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-245
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3847
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8060>
----
- subprojects/gst-plugins-good/gst/isomp4/qtdemux.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/gst/isomp4/qtdemux.c
-+++ b/gst/isomp4/qtdemux.c
-@@ -9982,9 +9982,9 @@ qtdemux_parse_samples (GstQTDemux * qtde
-           goto done;
-         }
- 
--        cur->offset =
--            qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
--            stream->co_size);
-+        if (!qt_atom_parser_get_offset (&stream->co_chunk,
-+                stream->co_size, &cur->offset))
-+          goto corrupt_file;
- 
-         GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
-             "%" G_GUINT64_FORMAT, j, cur->offset);
diff --git a/debian/patches/series b/debian/patches/series
index 81eef24524c8d1d169f81992d3cc403fca4b74dc..f203df4b87bc5eef0da96452dbafc2747bcc7234 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,32 +1 @@
-Skip-failing-tests.patch
-GST-2023-0001.patch
-qtdemux-Avoid-integer-overflow-when-parsing-Theora-e.patch
-jpegdec-Directly-error-out-on-negotiation-failures.patch
-gdkpixbufdec-Check-if-initializing-the-video-info-ac.patch
-wavparse-Check-for-short-reads-when-parsing-headers-.patch
-wavparse-Make-sure-enough-data-for-the-tag-list-tag-.patch
-wavparse-Fix-parsing-of-acid-chunk.patch
-wavparse-Check-that-at-least-4-bytes-are-available-b.patch
-wavparse-Check-that-at-least-32-bytes-are-available-.patch
-wavparse-Fix-clipping-of-size-to-the-file-size.patch
-wavparse-Check-size-before-reading-ds64-chunk.patch
-avisubtitle-Fix-size-checks-and-avoid-overflows-when.patch
-matroskademux-Only-unmap-GstMapInfo-in-WavPack-heade.patch
-matroskademux-Fix-off-by-one-when-parsing-multi-chan.patch
-matroskademux-Check-for-big-enough-WavPack-codec-pri.patch
-matroskademux-Don-t-take-data-out-of-an-empty-adapte.patch
-matroskademux-Skip-over-laces-directly-when-postproc.patch
-matroskademux-Skip-over-zero-sized-Xiph-stream-heade.patch
-matroskademux-Put-a-copy-of-the-codec-data-into-the-.patch
-qtdemux-Fix-integer-overflow-when-allocating-the-sam.patch
-qtdemux-Fix-debug-output-during-trun-parsing.patch
-qtdemux-Don-t-iterate-over-all-trun-entries-if-none-.patch
-qtdemux-Check-sizes-of-stsc-stco-stts-before-trying-.patch
-qtdemux-Make-sure-only-an-even-number-of-bytes-is-pr.patch
-qtdemux-Make-sure-enough-data-is-available-before-re.patch
-qtdemux-Fix-length-checks-and-offsets-in-stsd-entry-.patch
-qtdemux-Fix-error-handling-when-parsing-cenc-sample-.patch
-qtdemux-Make-sure-there-are-enough-offsets-to-read-w.patch
-qtdemux-Actually-handle-errors-returns-from-various-.patch
-qtdemux-Check-for-invalid-atom-length-when-extractin.patch
-qtdemux-Add-size-check-for-parsing-SMI-SEQH-atom.patch
+0000_remove-flv-test.patch
diff --git a/debian/patches/wavparse-Check-for-short-reads-when-parsing-headers-.patch b/debian/patches/wavparse-Check-for-short-reads-when-parsing-headers-.patch
deleted file mode 100644
index 33f13b270b14966d75d24a67e8a1ceb0c207df75..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Check-for-short-reads-when-parsing-headers-.patch
+++ /dev/null
@@ -1,163 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:00:57 +0300
-Subject: wavparse: Check for short reads when parsing headers in pull mode
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/c627f3a28bc792580f9a9ebcbb309b2256e4a895
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47776
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47778
-
-And also return the actual flow return to the caller instead of always returning
-GST_FLOW_ERROR.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-258, GHSL-2024-260
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3886
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3888
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- .../gst/wavparse/gstwavparse.c                | 63 ++++++++++++++-----
- 1 file changed, 46 insertions(+), 17 deletions(-)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -1097,6 +1097,24 @@ parse_ds64 (GstWavParse * wav, GstBuffer
- }
- 
- static GstFlowReturn
-+gst_wavparse_pull_range_exact (GstWavParse * wav, guint64 offset, guint size,
-+    GstBuffer ** buffer)
-+{
-+  GstFlowReturn res;
-+
-+  res = gst_pad_pull_range (wav->sinkpad, offset, size, buffer);
-+  if (res != GST_FLOW_OK)
-+    return res;
-+
-+  if (gst_buffer_get_size (*buffer) < size) {
-+    gst_clear_buffer (buffer);
-+    return GST_FLOW_EOS;
-+  }
-+
-+  return res;
-+}
-+
-+static GstFlowReturn
- gst_wavparse_stream_headers (GstWavParse * wav)
- {
-   GstFlowReturn res = GST_FLOW_OK;
-@@ -1291,9 +1309,9 @@ gst_wavparse_stream_headers (GstWavParse
- 
-       buf = NULL;
-       if ((res =
--              gst_pad_pull_range (wav->sinkpad, wav->offset, 8,
-+              gst_wavparse_pull_range_exact (wav, wav->offset, 8,
-                   &buf)) != GST_FLOW_OK)
--        goto header_read_error;
-+        goto header_pull_error;
-       gst_buffer_map (buf, &map, GST_MAP_READ);
-       tag = GST_READ_UINT32_LE (map.data);
-       size = GST_READ_UINT32_LE (map.data + 4);
-@@ -1396,9 +1414,9 @@ gst_wavparse_stream_headers (GstWavParse
-             gst_buffer_unref (buf);
-             buf = NULL;
-             if ((res =
--                    gst_pad_pull_range (wav->sinkpad, wav->offset + 8,
-+                    gst_wavparse_pull_range_exact (wav, wav->offset + 8,
-                         data_size, &buf)) != GST_FLOW_OK)
--              goto header_read_error;
-+              goto header_pull_error;
-             gst_buffer_extract (buf, 0, &wav->fact, 4);
-             wav->fact = GUINT32_FROM_LE (wav->fact);
-             gst_buffer_unref (buf);
-@@ -1443,9 +1461,9 @@ gst_wavparse_stream_headers (GstWavParse
-           gst_buffer_unref (buf);
-           buf = NULL;
-           if ((res =
--                  gst_pad_pull_range (wav->sinkpad, wav->offset + 8,
--                      size, &buf)) != GST_FLOW_OK)
--            goto header_read_error;
-+                  gst_wavparse_pull_range_exact (wav, wav->offset + 8, size,
-+                      &buf)) != GST_FLOW_OK)
-+            goto header_pull_error;
-           gst_buffer_map (buf, &map, GST_MAP_READ);
-           acid = (const gst_riff_acid *) map.data;
-           tempo = acid->tempo;
-@@ -1483,9 +1501,9 @@ gst_wavparse_stream_headers (GstWavParse
-           gst_buffer_unref (buf);
-           buf = NULL;
-           if ((res =
--                  gst_pad_pull_range (wav->sinkpad, wav->offset, 12,
-+                  gst_wavparse_pull_range_exact (wav, wav->offset, 12,
-                       &buf)) != GST_FLOW_OK)
--            goto header_read_error;
-+            goto header_pull_error;
-           gst_buffer_extract (buf, 8, &ltag, 4);
-           ltag = GUINT32_FROM_LE (ltag);
-         }
-@@ -1512,9 +1530,9 @@ gst_wavparse_stream_headers (GstWavParse
-               buf = NULL;
-               if (data_size > 0) {
-                 if ((res =
--                        gst_pad_pull_range (wav->sinkpad, wav->offset,
-+                        gst_wavparse_pull_range_exact (wav, wav->offset,
-                             data_size, &buf)) != GST_FLOW_OK)
--                  goto header_read_error;
-+                  goto header_pull_error;
-               }
-             }
-             if (data_size > 0) {
-@@ -1552,9 +1570,9 @@ gst_wavparse_stream_headers (GstWavParse
-               buf = NULL;
-               wav->offset += 12;
-               if ((res =
--                      gst_pad_pull_range (wav->sinkpad, wav->offset,
-+                      gst_wavparse_pull_range_exact (wav, wav->offset,
-                           data_size, &buf)) != GST_FLOW_OK)
--                goto header_read_error;
-+                goto header_pull_error;
-               gst_buffer_map (buf, &map, GST_MAP_READ);
-               gst_wavparse_adtl_chunk (wav, (const guint8 *) map.data,
-                   data_size);
-@@ -1597,9 +1615,9 @@ gst_wavparse_stream_headers (GstWavParse
-           gst_buffer_unref (buf);
-           buf = NULL;
-           if ((res =
--                  gst_pad_pull_range (wav->sinkpad, wav->offset,
-+                  gst_wavparse_pull_range_exact (wav, wav->offset,
-                       data_size, &buf)) != GST_FLOW_OK)
--            goto header_read_error;
-+            goto header_pull_error;
-           gst_buffer_map (buf, &map, GST_MAP_READ);
-           if (!gst_wavparse_cue_chunk (wav, (const guint8 *) map.data,
-                   data_size)) {
-@@ -1641,9 +1659,9 @@ gst_wavparse_stream_headers (GstWavParse
-           gst_buffer_unref (buf);
-           buf = NULL;
-           if ((res =
--                  gst_pad_pull_range (wav->sinkpad, wav->offset,
-+                  gst_wavparse_pull_range_exact (wav, wav->offset,
-                       data_size, &buf)) != GST_FLOW_OK)
--            goto header_read_error;
-+            goto header_pull_error;
-           gst_buffer_map (buf, &map, GST_MAP_READ);
-           if (!gst_wavparse_smpl_chunk (wav, (const guint8 *) map.data,
-                   data_size)) {
-@@ -1795,6 +1813,17 @@ header_read_error:
-         ("Couldn't read in header %d (%s)", res, gst_flow_get_name (res)));
-     goto fail;
-   }
-+header_pull_error:
-+  {
-+    if (res == GST_FLOW_EOS) {
-+      GST_WARNING_OBJECT (wav, "Couldn't pull header %d (%s)", res,
-+          gst_flow_get_name (res));
-+    } else {
-+      GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
-+          ("Couldn't pull header %d (%s)", res, gst_flow_get_name (res)));
-+    }
-+    goto exit;
-+  }
- }
- 
- /*
diff --git a/debian/patches/wavparse-Check-size-before-reading-ds64-chunk.patch b/debian/patches/wavparse-Check-size-before-reading-ds64-chunk.patch
deleted file mode 100644
index 7a3f85de331a5b816f8d0d7149482fc5f2c0cedf..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Check-size-before-reading-ds64-chunk.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:51:00 +0300
-Subject: wavparse: Check size before reading ds64 chunk
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/ba8476d3448eeaf016345ae0697b8447c0f62636
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47775
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-261
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3889
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- subprojects/gst-plugins-good/gst/wavparse/gstwavparse.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -1087,6 +1087,11 @@ parse_ds64 (GstWavParse * wav, GstBuffer
-   guint32 sampleCountLow, sampleCountHigh;
- 
-   gst_buffer_map (buf, &map, GST_MAP_READ);
-+  if (map.size < 6 * 4) {
-+    GST_WARNING_OBJECT (wav, "Too small ds64 chunk (%" G_GSIZE_FORMAT ")",
-+        map.size);
-+    return FALSE;
-+  }
-   dataSizeLow = GST_READ_UINT32_LE (map.data + 2 * 4);
-   dataSizeHigh = GST_READ_UINT32_LE (map.data + 3 * 4);
-   sampleCountLow = GST_READ_UINT32_LE (map.data + 4 * 4);
diff --git a/debian/patches/wavparse-Check-that-at-least-32-bytes-are-available-.patch b/debian/patches/wavparse-Check-that-at-least-32-bytes-are-available-.patch
deleted file mode 100644
index a55cf59117e708e1e4c698e1ae0530a69424bd67..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Check-that-at-least-32-bytes-are-available-.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:22:02 +0300
-Subject: wavparse: Check that at least 32 bytes are available before parsing
- smpl chunks
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/3d2a5841d777dd95afdea30ad134f96c876f84ab
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47777
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-259
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3887
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- subprojects/gst-plugins-good/gst/wavparse/gstwavparse.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -893,6 +893,9 @@ gst_wavparse_smpl_chunk (GstWavParse * w
- {
-   guint32 note_number;
- 
-+  if (size < 32)
-+    return FALSE;
-+
-   /*
-      manufacturer_id = GST_READ_UINT32_LE (data);
-      product_id = GST_READ_UINT32_LE (data + 4);
diff --git a/debian/patches/wavparse-Check-that-at-least-4-bytes-are-available-b.patch b/debian/patches/wavparse-Check-that-at-least-4-bytes-are-available-b.patch
deleted file mode 100644
index e1218e79e71c0863049794580fbfa2bc19c0b082..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Check-that-at-least-4-bytes-are-available-b.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:21:44 +0300
-Subject: wavparse: Check that at least 4 bytes are available before parsing
- cue chunks
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/8f04506d7e68a653c8d7c5e2fb0a19ef93c6ea35
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- subprojects/gst-plugins-good/gst/wavparse/gstwavparse.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -789,6 +789,11 @@ gst_wavparse_cue_chunk (GstWavParse * wa
-     return TRUE;
-   }
- 
-+  if (size < 4) {
-+    GST_WARNING_OBJECT (wav, "broken file %d", size);
-+    return FALSE;
-+  }
-+
-   ncues = GST_READ_UINT32_LE (data);
- 
-   if (size < 4 + ncues * 24) {
diff --git a/debian/patches/wavparse-Fix-clipping-of-size-to-the-file-size.patch b/debian/patches/wavparse-Fix-clipping-of-size-to-the-file-size.patch
deleted file mode 100644
index 869585872b7086ba379bd4b01076391bef25af48..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Fix-clipping-of-size-to-the-file-size.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:27:27 +0300
-Subject: wavparse: Fix clipping of size to the file size
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/34cfd6b82c3ae6772b9b43b3f6243f85cea35c38
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47776
-
-The size does not include the 8 bytes tag and length, so an additional 8 bytes
-must be removed here. 8 bytes are always available at this point because
-otherwise the parsing of the tag and length right above would've failed.
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-260
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3888
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- subprojects/gst-plugins-good/gst/wavparse/gstwavparse.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -1337,10 +1337,11 @@ gst_wavparse_stream_headers (GstWavParse
-     }
- 
-     /* Clip to upstream size if known */
--    if (upstream_size > 0 && size + wav->offset > upstream_size) {
-+    if (upstream_size > 0 && size + 8 + wav->offset > upstream_size) {
-       GST_WARNING_OBJECT (wav, "Clipping chunk size to file size");
-       g_assert (upstream_size >= wav->offset);
--      size = upstream_size - wav->offset;
-+      g_assert (upstream_size - wav->offset >= 8);
-+      size = upstream_size - wav->offset - 8;
-     }
- 
-     /* wav is a st00pid format, we don't know for sure where data starts.
diff --git a/debian/patches/wavparse-Fix-parsing-of-acid-chunk.patch b/debian/patches/wavparse-Fix-parsing-of-acid-chunk.patch
deleted file mode 100644
index 9f8a5740c1f15ee47e055462ca86639ee313c9b2..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Fix-parsing-of-acid-chunk.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:15:27 +0300
-Subject: wavparse: Fix parsing of acid chunk
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/8911020ae3da65b224dd1c87de3437a532e9efa4
-
-Simply casting the bytes to a struct can lead to crashes because of unaligned
-reads, and is also missing the endianness swapping that is necessary on big
-endian architectures.
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- .../gst-plugins-good/gst/wavparse/gstwavparse.c      | 12 +++++-------
- 1 file changed, 5 insertions(+), 7 deletions(-)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -1433,8 +1433,7 @@ gst_wavparse_stream_headers (GstWavParse
-         break;
-       }
-       case GST_RIFF_TAG_acid:{
--        const gst_riff_acid *acid = NULL;
--        const guint data_size = sizeof (gst_riff_acid);
-+        const guint data_size = 24;
-         gfloat tempo;
- 
-         GST_INFO_OBJECT (wav, "Have acid chunk");
-@@ -1448,13 +1447,13 @@ gst_wavparse_stream_headers (GstWavParse
-           break;
-         }
-         if (wav->streaming) {
-+          const guint8 *data;
-           if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
-             goto exit;
-           }
-           gst_adapter_flush (wav->adapter, 8);
--          acid = (const gst_riff_acid *) gst_adapter_map (wav->adapter,
--              data_size);
--          tempo = acid->tempo;
-+          data = gst_adapter_map (wav->adapter, data_size);
-+          tempo = GST_READ_FLOAT_LE (data + 20);
-           gst_adapter_unmap (wav->adapter);
-         } else {
-           GstMapInfo map;
-@@ -1465,8 +1464,7 @@ gst_wavparse_stream_headers (GstWavParse
-                       &buf)) != GST_FLOW_OK)
-             goto header_pull_error;
-           gst_buffer_map (buf, &map, GST_MAP_READ);
--          acid = (const gst_riff_acid *) map.data;
--          tempo = acid->tempo;
-+          tempo = GST_READ_FLOAT_LE (map.data + 20);
-           gst_buffer_unmap (buf, &map);
-         }
-         /* send data as tags */
diff --git a/debian/patches/wavparse-Make-sure-enough-data-for-the-tag-list-tag-.patch b/debian/patches/wavparse-Make-sure-enough-data-for-the-tag-list-tag-.patch
deleted file mode 100644
index e596caef92ca141d64eff75439662add44a868a2..0000000000000000000000000000000000000000
--- a/debian/patches/wavparse-Make-sure-enough-data-for-the-tag-list-tag-.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
-Date: Fri, 4 Oct 2024 13:09:43 +0300
-Subject: wavparse: Make sure enough data for the tag list tag is available
- before parsing
-Origin: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/f5fa594695e5a9b347e88719b487d9779f80926a
-Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-47778
-
-Thanks to Antonio Morales for finding and reporting the issue.
-
-Fixes GHSL-2024-258
-Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3886
-
-Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8054>
----
- subprojects/gst-plugins-good/gst/wavparse/gstwavparse.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/gst/wavparse/gstwavparse.c
-+++ b/gst/wavparse/gstwavparse.c
-@@ -1488,6 +1488,10 @@ gst_wavparse_stream_headers (GstWavParse
-       case GST_RIFF_TAG_LIST:{
-         guint32 ltag;
- 
-+        /* Need at least the ltag */
-+        if (size < 4)
-+          goto exit;
-+
-         if (wav->streaming) {
-           const guint8 *data = NULL;
- 
diff --git a/debian/rules b/debian/rules
index 203fe077dba8ec8f51974ac8dbbc343244b1809d..d889aaeb718e91d46c110b498c57d8c0791240df 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,21 +1,18 @@
 #!/usr/bin/make -f
 
 include /usr/share/dpkg/default.mk
+include /usr/share/dpkg/pkg-info.mk
 
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+export DEB_LDFLAGS_MAINT_APPEND = -Wl,-O1 -Wl,-z,defs
 export HOME=$(CURDIR)/fake-home
 export QT_SELECT=5
 
-# debian package version
-version=$(shell dpkg-parsechangelog | grep ^Version: | cut -d ' ' -f 2)
 # upstream version
-gst_version=$(shell echo $(version) | cut -d '-' -f 1)
+gst_version=$(shell echo $(DEB_VERSION) | cut -d '-' -f 1)
 
 built_binaries := $(shell dh_listpackages)
 
-CFLAGS += -Wno-error
-CXXFLAGS += -Wno-error
-LDFLAGS += -Wl,-z,defs -Wl,-O1
-
 # Let's decide the package name and url depending on the distribution
 DISTRO = "$(shell dpkg-vendor --query vendor)"
 
@@ -50,6 +47,21 @@ ifeq ($(filter gstreamer1.0-qt6,$(built_binaries)),)
 conf_flags += -Dqt6=disabled
 endif
 
+# qtwayland-opensource-src isn't built on hurd or Ubuntu's i386
+ifneq (,$(filter $(DEB_HOST_ARCH), i386 hurd-amd64 hurd-i386))
+conf_flags += -Dqt-wayland=disabled
+endif
+
+# qt-egl is not available on non-Linux
+ifneq (,$(filter $(DEB_HOST_ARCH_OS), hurd))
+conf_flags += -Dqt-egl=disabled
+endif
+
+# soup is not available on non-Linux
+ifeq ($(DEB_HOST_ARCH_OS),hurd)
+conf_flags+= -Dadaptivedemux2=disabled -Dsoup=disabled
+endif
+
 # Apertis
 conf_flags += -Djack=disabled -Dlame=disabled
 conf_flags += -Dtwolame=disabled -Dmpg123=disabled
@@ -74,6 +86,8 @@ endif
 execute_after_dh_install:
 ifneq ($(DEB_HOST_ARCH_OS),hurd)
 	dh_install -pgstreamer1.0-plugins-good debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/gstreamer-1.0/libgstoss4.so
+	dh_install -pgstreamer1.0-plugins-good debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/gstreamer-1.0/libgstadaptivedemux2.so
+	dh_install -pgstreamer1.0-plugins-good debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/gstreamer-1.0/libgstsoup.so
 endif
 
 ifeq ($(DEB_HOST_ARCH_OS),linux)
diff --git a/debian/watch b/debian/watch
index 3425195a97f9d33d4f5ece9a6937838603386fc4..b7fff343884b40a84fc7daf0b16eb7150f8fcf98 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,3 +1,3 @@
 version=4
 opts=pgpmode=auto \
-https://gstreamer.freedesktop.org/src/gst-plugins-good/ gst-plugins-good-(1\.[\d]+\.[\d]+)@ARCHIVE_EXT@
+https://gstreamer.freedesktop.org/src/gst-plugins-good/ gst-plugins-good-(1\.24\.[\d]+)@ARCHIVE_EXT@
diff --git a/docs/gst_plugins_cache.json b/docs/gst_plugins_cache.json
index acc50147a03c5b4515aac953e4f406698d840086..8ead65387a26c2e0dc1fd27e34507b79305a9861 100644
--- a/docs/gst_plugins_cache.json
+++ b/docs/gst_plugins_cache.json
@@ -996,7 +996,7 @@
         "description": "Adaptive Streaming 2 plugin",
         "elements": {
             "dashdemux2": {
-                "author": "Edward Hervey <edward@centricular.com>\nJan Schmidt <jan@centricular.com>",
+                "author": "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>",
                 "description": "Dynamic Adaptive Streaming over HTTP demuxer",
                 "hierarchy": [
                     "GstDashDemux2",
@@ -1102,12 +1102,26 @@
                         "readable": true,
                         "type": "gchararray",
                         "writable": true
+                    },
+                    "start-bitrate": {
+                        "blurb": "Initial bitrate to use to choose first alternate (0 = automatic) (bits/s)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "-1",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": true
                     }
                 },
                 "rank": "primary + 1"
             },
             "hlsdemux2": {
-                "author": "Edward Hervey <edward@centricular.com>\nJan Schmidt <jan@centricular.com>",
+                "author": "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>",
                 "description": "HTTP Live Streaming demuxer",
                 "hierarchy": [
                     "GstHLSDemux2",
@@ -1487,7 +1501,7 @@
         "description": "adds an alpha channel to video - constant or via chroma-keying",
         "elements": {
             "alpha": {
-                "author": "Wim Taymans <wim.taymans@gmail.com>\nEdward Hervey <edward.hervey@collabora.co.uk>\nJan Schmidt <thaytan@noraisin.net>",
+                "author": "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <edward.hervey@collabora.co.uk>, Jan Schmidt <thaytan@noraisin.net>",
                 "description": "Adds an alpha channel to video - uniform or via chroma-keying",
                 "hierarchy": [
                     "GstAlpha",
@@ -1728,6 +1742,205 @@
         "tracers": {},
         "url": "Unknown package origin"
     },
+    "amrnb": {
+        "description": "Adaptive Multi-Rate Narrow-Band",
+        "elements": {
+            "amrnbdec": {
+                "author": "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>",
+                "description": "Adaptive Multi-Rate Narrow-Band audio decoder",
+                "hierarchy": [
+                    "GstAmrnbDec",
+                    "GstAudioDecoder",
+                    "GstElement",
+                    "GstObject",
+                    "GInitiallyUnowned",
+                    "GObject"
+                ],
+                "klass": "Codec/Decoder/Audio",
+                "pad-templates": {
+                    "sink": {
+                        "caps": "audio/AMR:\n           rate: 8000\n       channels: 1\n",
+                        "direction": "sink",
+                        "presence": "always"
+                    },
+                    "src": {
+                        "caps": "audio/x-raw:\n         format: S16LE\n         layout: interleaved\n           rate: 8000\n       channels: 1\n",
+                        "direction": "src",
+                        "presence": "always"
+                    }
+                },
+                "properties": {
+                    "variant": {
+                        "blurb": "The decoder variant",
+                        "conditionally-available": false,
+                        "construct": true,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "IF1 (0)",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstAmrnbVariant",
+                        "writable": true
+                    }
+                },
+                "rank": "primary"
+            },
+            "amrnbenc": {
+                "author": "Wim Taymans <wim.taymans@gmail.com>",
+                "description": "Adaptive Multi-Rate Narrow-Band audio encoder",
+                "hierarchy": [
+                    "GstAmrnbEnc",
+                    "GstAudioEncoder",
+                    "GstElement",
+                    "GstObject",
+                    "GInitiallyUnowned",
+                    "GObject"
+                ],
+                "interfaces": [
+                    "GstPreset"
+                ],
+                "klass": "Codec/Encoder/Audio",
+                "pad-templates": {
+                    "sink": {
+                        "caps": "audio/x-raw:\n         format: S16LE\n         layout: interleaved\n           rate: 8000\n       channels: 1\n",
+                        "direction": "sink",
+                        "presence": "always"
+                    },
+                    "src": {
+                        "caps": "audio/AMR:\n           rate: 8000\n       channels: 1\n",
+                        "direction": "src",
+                        "presence": "always"
+                    }
+                },
+                "properties": {
+                    "band-mode": {
+                        "blurb": "Encoding Band Mode (Kbps)",
+                        "conditionally-available": false,
+                        "construct": true,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "MR122 (7)",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstAmrnbEncBandMode",
+                        "writable": true
+                    }
+                },
+                "rank": "secondary"
+            }
+        },
+        "filename": "gstamrnb",
+        "license": "unknown",
+        "other-types": {
+            "GstAmrnbEncBandMode": {
+                "kind": "enum",
+                "values": [
+                    {
+                        "desc": "MR475",
+                        "name": "MR475",
+                        "value": "0"
+                    },
+                    {
+                        "desc": "MR515",
+                        "name": "MR515",
+                        "value": "1"
+                    },
+                    {
+                        "desc": "MR59",
+                        "name": "MR59",
+                        "value": "2"
+                    },
+                    {
+                        "desc": "MR67",
+                        "name": "MR67",
+                        "value": "3"
+                    },
+                    {
+                        "desc": "MR74",
+                        "name": "MR74",
+                        "value": "4"
+                    },
+                    {
+                        "desc": "MR795",
+                        "name": "MR795",
+                        "value": "5"
+                    },
+                    {
+                        "desc": "MR102",
+                        "name": "MR102",
+                        "value": "6"
+                    },
+                    {
+                        "desc": "MR122",
+                        "name": "MR122",
+                        "value": "7"
+                    },
+                    {
+                        "desc": "MRDTX",
+                        "name": "MRDTX",
+                        "value": "8"
+                    }
+                ]
+            },
+            "GstAmrnbVariant": {
+                "kind": "enum",
+                "values": [
+                    {
+                        "desc": "IF1",
+                        "name": "IF1",
+                        "value": "0"
+                    },
+                    {
+                        "desc": "IF2",
+                        "name": "IF2",
+                        "value": "1"
+                    }
+                ]
+            }
+        },
+        "package": "GStreamer Good Plug-ins",
+        "source": "gst-plugins-good",
+        "tracers": {},
+        "url": "Unknown package origin"
+    },
+    "amrwbdec": {
+        "description": "Adaptive Multi-Rate Wide-Band Decoder",
+        "elements": {
+            "amrwbdec": {
+                "author": "Renato Araujo <renato.filho@indt.org.br>",
+                "description": "Adaptive Multi-Rate Wideband audio decoder",
+                "hierarchy": [
+                    "GstAmrwbDec",
+                    "GstAudioDecoder",
+                    "GstElement",
+                    "GstObject",
+                    "GInitiallyUnowned",
+                    "GObject"
+                ],
+                "klass": "Codec/Decoder/Audio",
+                "pad-templates": {
+                    "sink": {
+                        "caps": "audio/AMR-WB:\n           rate: 16000\n       channels: 1\n",
+                        "direction": "sink",
+                        "presence": "always"
+                    },
+                    "src": {
+                        "caps": "audio/x-raw:\n         format: S16LE\n         layout: interleaved\n           rate: 16000\n       channels: 1\n",
+                        "direction": "src",
+                        "presence": "always"
+                    }
+                },
+                "rank": "primary"
+            }
+        },
+        "filename": "gstamrwbdec",
+        "license": "unknown",
+        "other-types": {},
+        "package": "GStreamer Good Plug-ins",
+        "source": "gst-plugins-good",
+        "tracers": {},
+        "url": "Unknown package origin"
+    },
     "apetag": {
         "description": "APEv1/2 tag reader",
         "elements": {
@@ -3774,6 +3987,18 @@
                         "type": "GstCACASinkDithering",
                         "writable": true
                     },
+                    "driver": {
+                        "blurb": "Output driver",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "x11 (0)",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstCACASinkDriver",
+                        "writable": true
+                    },
                     "screen-height": {
                         "blurb": "The height of the screen",
                         "conditionally-available": false,
@@ -3935,6 +4160,11 @@
                     }
                 ]
             },
+            "GstCACASinkDriver": {
+                "ignore-enum-members": true,
+                "kind": "enum",
+                "values": []
+            },
             "GstCACATvDithering": {
                 "kind": "enum",
                 "values": [
@@ -4080,6 +4310,18 @@
                     }
                 },
                 "properties": {
+                    "audio-level-meta": {
+                        "blurb": "Set GstAudioLevelMeta on buffers",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "leaky": {
                         "blurb": "do we leak buffers when below threshold ?",
                         "conditionally-available": false,
@@ -4693,6 +4935,18 @@
                     }
                 },
                 "properties": {
+                    "scope": {
+                        "blurb": "Scope of tags to inject (stream | global)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "stream (0)",
+                        "mutable": "null",
+                        "readable": false,
+                        "type": "GstTagScope",
+                        "writable": true
+                    },
                     "tags": {
                         "blurb": "List of tags to inject into the target file",
                         "conditionally-available": false,
@@ -4866,12 +5120,12 @@
                 "long-name": "Deinterlacer",
                 "pad-templates": {
                     "sink": {
-                        "caps": "video/x-raw:\n         format: { AYUV, ARGB, ABGR, RGBA, BGRA, Y444, xRGB, xBGR, RGBx, BGRx, RGB, BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(ANY):\n         format: { ABGR64_LE, BGRA64_LE, AYUV64, ARGB64_LE, ARGB64, RGBA64_LE, ABGR64_BE, BGRA64_BE, ARGB64_BE, RGBA64_BE, GBRA_12LE, GBRA_12BE, Y412_LE, Y412_BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, RGB10A2_LE, BGR10A2_LE, Y410, GBRA, ABGR, VUYA, BGRA, AYUV, ARGB, RGBA, A420, AV12, Y444_16LE, Y444_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, v210, UYVP, I420_10LE, I420_10BE, P010_10LE, NV12_10LE32, NV12_10LE40, P010_10BE, NV12_10BE_8L128, Y444, RGBP, GBR, BGRP, NV24, xBGR, BGRx, xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, YV12, NV21, NV12, NV12_8L128, NV12_64Z32, NV12_4L4, NV12_32L32, NV12_16L32S, Y41B, IYU1, YVU9, YUV9, RGB16, BGR16, RGB15, BGR15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
+                        "caps": "video/x-raw:\n         format: { AYUV, ARGB, ABGR, RGBA, BGRA, Y444, xRGB, xBGR, RGBx, BGRx, RGB, BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21, Y444_16LE, Y444_12LE, Y444_10LE, I422_12LE, I422_10LE, I420_12LE, I420_10LE }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(ANY):\n         format: { DMA_DRM, A444_16LE, A444_16BE, AYUV64, RGBA64_LE, ARGB64, ARGB64_LE, BGRA64_LE, ABGR64_LE, RGBA64_BE, ARGB64_BE, BGRA64_BE, ABGR64_BE, A422_16LE, A422_16BE, A420_16LE, A420_16BE, A444_12LE, GBRA_12LE, A444_12BE, GBRA_12BE, Y412_LE, Y412_BE, A422_12LE, A422_12BE, A420_12LE, A420_12BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, BGR10A2_LE, RGB10A2_LE, Y410, A444, GBRA, AYUV, VUYA, RGBA, RBGA, ARGB, BGRA, ABGR, A422, A420, AV12, Y444_16LE, GBR_16LE, Y444_16BE, GBR_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, UYVP, v210, I420_10LE, I420_10BE, P010_10LE, NV12_10LE40, NV12_10LE32, P010_10BE, MT2110R, MT2110T, NV12_10BE_8L128, NV12_10LE40_4L4, Y444, BGRP, GBR, RGBP, NV24, v308, IYU2, RGBx, xRGB, BGRx, xBGR, RGB, BGR, Y42B, NV16, NV61, YUY2, YVYU, UYVY, VYUY, I420, YV12, NV12, NV21, NV12_16L32S, NV12_32L32, NV12_4L4, NV12_64Z32, NV12_8L128, Y41B, IYU1, YUV9, YVU9, BGR16, RGB16, BGR15, RGB15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
                         "direction": "sink",
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "video/x-raw:\n         format: { AYUV, ARGB, ABGR, RGBA, BGRA, Y444, xRGB, xBGR, RGBx, BGRx, RGB, BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(ANY):\n         format: { ABGR64_LE, BGRA64_LE, AYUV64, ARGB64_LE, ARGB64, RGBA64_LE, ABGR64_BE, BGRA64_BE, ARGB64_BE, RGBA64_BE, GBRA_12LE, GBRA_12BE, Y412_LE, Y412_BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, RGB10A2_LE, BGR10A2_LE, Y410, GBRA, ABGR, VUYA, BGRA, AYUV, ARGB, RGBA, A420, AV12, Y444_16LE, Y444_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, v210, UYVP, I420_10LE, I420_10BE, P010_10LE, NV12_10LE32, NV12_10LE40, P010_10BE, NV12_10BE_8L128, Y444, RGBP, GBR, BGRP, NV24, xBGR, BGRx, xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, YV12, NV21, NV12, NV12_8L128, NV12_64Z32, NV12_4L4, NV12_32L32, NV12_16L32S, Y41B, IYU1, YVU9, YUV9, RGB16, BGR16, RGB15, BGR15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
+                        "caps": "video/x-raw:\n         format: { AYUV, ARGB, ABGR, RGBA, BGRA, Y444, xRGB, xBGR, RGBx, BGRx, RGB, BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21, Y444_16LE, Y444_12LE, Y444_10LE, I422_12LE, I422_10LE, I420_12LE, I420_10LE }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(ANY):\n         format: { DMA_DRM, A444_16LE, A444_16BE, AYUV64, RGBA64_LE, ARGB64, ARGB64_LE, BGRA64_LE, ABGR64_LE, RGBA64_BE, ARGB64_BE, BGRA64_BE, ABGR64_BE, A422_16LE, A422_16BE, A420_16LE, A420_16BE, A444_12LE, GBRA_12LE, A444_12BE, GBRA_12BE, Y412_LE, Y412_BE, A422_12LE, A422_12BE, A420_12LE, A420_12BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, BGR10A2_LE, RGB10A2_LE, Y410, A444, GBRA, AYUV, VUYA, RGBA, RBGA, ARGB, BGRA, ABGR, A422, A420, AV12, Y444_16LE, GBR_16LE, Y444_16BE, GBR_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, UYVP, v210, I420_10LE, I420_10BE, P010_10LE, NV12_10LE40, NV12_10LE32, P010_10BE, MT2110R, MT2110T, NV12_10BE_8L128, NV12_10LE40_4L4, Y444, BGRP, GBR, RGBP, NV24, v308, IYU2, RGBx, xRGB, BGRx, xBGR, RGB, BGR, Y42B, NV16, NV61, YUY2, YVYU, UYVY, VYUY, I420, YV12, NV12, NV21, NV12_16L32S, NV12_32L32, NV12_4L4, NV12_64Z32, NV12_8L128, Y41B, IYU1, YUV9, YVU9, BGR16, RGB16, BGR15, RGB15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -5181,7 +5435,7 @@
                     "GInitiallyUnowned",
                     "GObject"
                 ],
-                "klass": "Codec/Depayloader/Network",
+                "klass": "Codec/Depayloader/Network/RTP",
                 "long-name": "RTP DTMF packet depayloader",
                 "pad-templates": {
                     "sink": {
@@ -5190,7 +5444,7 @@
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "audio/x-raw:\n         format: S16LE\n           rate: [ 1, 2147483647 ]\n       channels: 1\n",
+                        "caps": "audio/x-raw:\n         format: S16LE\n           rate: [ 1, 2147483647 ]\n       channels: 1\n         layout: interleaved\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -5238,7 +5492,7 @@
                     "GInitiallyUnowned",
                     "GObject"
                 ],
-                "klass": "Source/Network",
+                "klass": "Source/Network/RTP",
                 "long-name": "RTP DTMF packet generator",
                 "pad-templates": {
                     "src": {
@@ -6650,7 +6904,7 @@
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "audio/x-flac:\n",
+                        "caps": "audio/x-flac:\n         framed: true\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -7027,19 +7281,31 @@
                         "construct": false,
                         "construct-only": false,
                         "controllable": false,
-                        "default": "GStreamer 1.22.0 FLV muxer",
+                        "default": "GStreamer {VERSION} FLV muxer",
                         "mutable": "null",
                         "readable": true,
                         "type": "gchararray",
                         "writable": true
                     },
+                    "enforce-increasing-timestamps": {
+                        "blurb": "If set to true, flvmux will modify buffers timestamps to ensure they are always strictly increasing, inside one stream and also between the audio and video streams",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "true",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "metadatacreator": {
                         "blurb": "The value of metadatacreator in the meta packet.",
                         "conditionally-available": false,
                         "construct": false,
                         "construct-only": false,
                         "controllable": false,
-                        "default": "GStreamer 1.22.0 FLV muxer",
+                        "default": "GStreamer {VERSION} FLV muxer",
                         "mutable": "null",
                         "readable": true,
                         "type": "gchararray",
@@ -7178,12 +7444,12 @@
                 "long-name": "GdkPixbuf Overlay",
                 "pad-templates": {
                     "sink": {
-                        "caps": "video/x-raw:\n         format: { ABGR64_LE, BGRA64_LE, AYUV64, ARGB64_LE, ARGB64, RGBA64_LE, ABGR64_BE, BGRA64_BE, ARGB64_BE, RGBA64_BE, GBRA_12LE, GBRA_12BE, Y412_LE, Y412_BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, RGB10A2_LE, BGR10A2_LE, Y410, GBRA, ABGR, VUYA, BGRA, AYUV, ARGB, RGBA, A420, AV12, Y444_16LE, Y444_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, v210, UYVP, I420_10LE, I420_10BE, P010_10LE, NV12_10LE32, NV12_10LE40, P010_10BE, NV12_10BE_8L128, Y444, RGBP, GBR, BGRP, NV24, xBGR, BGRx, xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, YV12, NV21, NV12, NV12_8L128, NV12_64Z32, NV12_4L4, NV12_32L32, NV12_16L32S, Y41B, IYU1, YVU9, YUV9, RGB16, BGR16, RGB15, BGR15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
+                        "caps": "video/x-raw:\n         format: { A444_16LE, A444_16BE, AYUV64, RGBA64_LE, ARGB64, ARGB64_LE, BGRA64_LE, ABGR64_LE, RGBA64_BE, ARGB64_BE, BGRA64_BE, ABGR64_BE, A422_16LE, A422_16BE, A420_16LE, A420_16BE, A444_12LE, GBRA_12LE, A444_12BE, GBRA_12BE, Y412_LE, Y412_BE, A422_12LE, A422_12BE, A420_12LE, A420_12BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, BGR10A2_LE, RGB10A2_LE, Y410, A444, GBRA, AYUV, VUYA, RGBA, RBGA, ARGB, BGRA, ABGR, A422, A420, AV12, Y444_16LE, GBR_16LE, Y444_16BE, GBR_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, UYVP, v210, I420_10LE, I420_10BE, P010_10LE, NV12_10LE40, NV12_10LE32, P010_10BE, MT2110R, MT2110T, NV12_10BE_8L128, NV12_10LE40_4L4, Y444, BGRP, GBR, RGBP, NV24, v308, IYU2, RGBx, xRGB, BGRx, xBGR, RGB, BGR, Y42B, NV16, NV61, YUY2, YVYU, UYVY, VYUY, I420, YV12, NV12, NV21, NV12_16L32S, NV12_32L32, NV12_4L4, NV12_64Z32, NV12_8L128, Y41B, IYU1, YUV9, YVU9, BGR16, RGB16, BGR15, RGB15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
                         "direction": "sink",
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "video/x-raw:\n         format: { ABGR64_LE, BGRA64_LE, AYUV64, ARGB64_LE, ARGB64, RGBA64_LE, ABGR64_BE, BGRA64_BE, ARGB64_BE, RGBA64_BE, GBRA_12LE, GBRA_12BE, Y412_LE, Y412_BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, RGB10A2_LE, BGR10A2_LE, Y410, GBRA, ABGR, VUYA, BGRA, AYUV, ARGB, RGBA, A420, AV12, Y444_16LE, Y444_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, v210, UYVP, I420_10LE, I420_10BE, P010_10LE, NV12_10LE32, NV12_10LE40, P010_10BE, NV12_10BE_8L128, Y444, RGBP, GBR, BGRP, NV24, xBGR, BGRx, xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, YV12, NV21, NV12, NV12_8L128, NV12_64Z32, NV12_4L4, NV12_32L32, NV12_16L32S, Y41B, IYU1, YVU9, YUV9, RGB16, BGR16, RGB15, BGR15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
+                        "caps": "video/x-raw:\n         format: { A444_16LE, A444_16BE, AYUV64, RGBA64_LE, ARGB64, ARGB64_LE, BGRA64_LE, ABGR64_LE, RGBA64_BE, ARGB64_BE, BGRA64_BE, ABGR64_BE, A422_16LE, A422_16BE, A420_16LE, A420_16BE, A444_12LE, GBRA_12LE, A444_12BE, GBRA_12BE, Y412_LE, Y412_BE, A422_12LE, A422_12BE, A420_12LE, A420_12BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, BGR10A2_LE, RGB10A2_LE, Y410, A444, GBRA, AYUV, VUYA, RGBA, RBGA, ARGB, BGRA, ABGR, A422, A420, AV12, Y444_16LE, GBR_16LE, Y444_16BE, GBR_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, UYVP, v210, I420_10LE, I420_10BE, P010_10LE, NV12_10LE40, NV12_10LE32, P010_10BE, MT2110R, MT2110T, NV12_10BE_8L128, NV12_10LE40_4L4, Y444, BGRP, GBR, RGBP, NV24, v308, IYU2, RGBx, xRGB, BGRx, xBGR, RGB, BGR, Y42B, NV16, NV61, YUY2, YVYU, UYVY, VYUY, I420, YV12, NV12, NV21, NV12_16L32S, NV12_32L32, NV12_4L4, NV12_64Z32, NV12_8L128, Y41B, IYU1, YUV9, YVU9, BGR16, RGB16, BGR15, RGB15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -7529,7 +7795,7 @@
                 "long-name": "Gtk GL Video Sink",
                 "pad-templates": {
                     "sink": {
-                        "caps": "video/x-raw(memory:GLMemory):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(memory:GLMemory, meta:GstVideoOverlayComposition):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n",
+                        "caps": "video/x-raw(memory:GLMemory):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: { (string)2D, (string)rectangle }\n\nvideo/x-raw(memory:GLMemory, meta:GstVideoOverlayComposition):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: { (string)2D, (string)rectangle }\n",
                         "direction": "sink",
                         "presence": "always"
                     }
@@ -7777,12 +8043,12 @@
                 "long-name": "Still frame stream generator",
                 "pad-templates": {
                     "sink": {
-                        "caps": "video/x-raw(ANY):\n",
+                        "caps": "video/x-raw(ANY):\n\nvideo/x-bayer(ANY):\n",
                         "direction": "sink",
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "video/x-raw(ANY):\n",
+                        "caps": "video/x-raw(ANY):\n\nvideo/x-bayer(ANY):\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -9546,7 +9812,7 @@
                 "long-name": "Matroska muxer",
                 "pad-templates": {
                     "audio_%%u": {
-                        "caps": "audio/mpeg:\n    mpegversion: 1\n          layer: [ 1, 3 ]\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/mpeg:\n    mpegversion: { (int)2, (int)4 }\n  stream-format: raw\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-ac3:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-eac3:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-dts:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-vorbis:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-flac:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-opus:\naudio/x-speex:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-raw:\n         format: { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }\n         layout: interleaved\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-tta:\n          width: { (int)8, (int)16, (int)24 }\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 96000 ]\naudio/x-pn-realaudio:\n      raversion: { (int)1, (int)2, (int)8 }\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-wma:\n     wmaversion: [ 1, 3 ]\n    block_align: [ 0, 65535 ]\n        bitrate: [ 0, 524288 ]\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-alaw:\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 192000 ]\naudio/x-mulaw:\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 192000 ]\naudio/x-adpcm:\n         layout: dvi\n    block_align: [ 64, 8192 ]\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 96000 ]\naudio/G722:\n       channels: 1\n           rate: 16000\naudio/x-adpcm:\n         layout: g726\n       channels: 1\n           rate: 8000\n",
+                        "caps": "audio/mpeg:\n    mpegversion: 1\n          layer: [ 1, 3 ]\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/mpeg:\n    mpegversion: { (int)2, (int)4 }\n  stream-format: raw\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-ac3:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-eac3:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-dts:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-vorbis:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-flac:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-opus:\n       channels: [ 1, 8 ]\n           rate: { (int)8000, (int)16000, (int)24000, (int)32000, (int)48000 }\naudio/x-speex:\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-raw:\n         format: { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }\n         layout: interleaved\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-tta:\n          width: { (int)8, (int)16, (int)24 }\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 96000 ]\naudio/x-pn-realaudio:\n      raversion: { (int)1, (int)2, (int)8 }\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-wma:\n     wmaversion: [ 1, 3 ]\n    block_align: [ 0, 65535 ]\n        bitrate: [ 0, 524288 ]\n       channels: [ 1, 2147483647 ]\n           rate: [ 1, 2147483647 ]\naudio/x-alaw:\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 192000 ]\naudio/x-mulaw:\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 192000 ]\naudio/x-adpcm:\n         layout: dvi\n    block_align: [ 64, 8192 ]\n       channels: { (int)1, (int)2 }\n           rate: [ 8000, 96000 ]\naudio/G722:\n       channels: 1\n           rate: 16000\naudio/x-adpcm:\n         layout: g726\n       channels: 1\n           rate: 8000\n",
                         "direction": "sink",
                         "presence": "request"
                     },
@@ -9561,7 +9827,7 @@
                         "presence": "request"
                     },
                     "video_%%u": {
-                        "caps": "video/mpeg:\n    mpegversion: { (int)1, (int)2, (int)4 }\n   systemstream: false\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-h264:\n  stream-format: { (string)avc, (string)avc3 }\n      alignment: au\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-h265:\n  stream-format: { (string)hvc1, (string)hev1 }\n      alignment: au\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-divx:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-huffyuv:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-dv:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-h263:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-msmpeg:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nimage/jpeg:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-theora:\nvideo/x-dirac:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-pn-realvideo:\n      rmversion: [ 1, 4 ]\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-vp8:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-vp9:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-raw:\n         format: { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-prores:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-wmv:\n     wmvversion: [ 1, 3 ]\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-av1:\n  stream-format: obu-stream\n      alignment: tu\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-ffv:\n      ffversion: 1\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n",
+                        "caps": "video/mpeg:\n    mpegversion: { (int)1, (int)2, (int)4 }\n   systemstream: false\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-h264:\n  stream-format: { (string)avc, (string)avc3 }\n      alignment: au\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-h265:\n  stream-format: { (string)hvc1, (string)hev1 }\n      alignment: au\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-divx:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-huffyuv:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-dv:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-h263:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-msmpeg:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nimage/jpeg:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-theora:\nvideo/x-dirac:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-pn-realvideo:\n      rmversion: [ 1, 4 ]\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-vp8:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-vp9:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-raw:\n         format: { YUY2, I420, YV12, UYVY, AYUV, GRAY8, GRAY10_LE32, GRAY16_LE, BGR, RGB, RGBA64_LE, BGRA64_LE }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-prores:\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-wmv:\n     wmvversion: [ 1, 3 ]\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-av1:\n  stream-format: obu-stream\n      alignment: tu\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\nvideo/x-ffv:\n      ffversion: 1\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n",
                         "direction": "sink",
                         "presence": "request"
                     }
@@ -9845,7 +10111,7 @@
                     }
                 },
                 "properties": {},
-                "rank": "marginal"
+                "rank": "primary"
             }
         },
         "filename": "gstmpg123",
@@ -9931,7 +10197,7 @@
         "description": "Reads/Writes buffers from/to sequentially named files",
         "elements": {
             "imagesequencesrc": {
-                "author": "Cesar Fabian Orccon Chipana <cfoch.fabian@gmail.com>\nThibault Saunier <tsaunier@igalia.com>",
+                "author": "Cesar Fabian Orccon Chipana <cfoch.fabian@gmail.com>, Thibault Saunier <tsaunier@igalia.com>",
                 "description": "Create a video stream from a sequence of image files",
                 "hierarchy": [
                     "GstImageSequenceSrc",
@@ -9975,7 +10241,7 @@
                         "construct": false,
                         "construct-only": false,
                         "controllable": false,
-                        "default": "NULL",
+                        "default": "%%05d",
                         "mutable": "null",
                         "readable": true,
                         "type": "gchararray",
@@ -11774,12 +12040,12 @@
                 "long-name": "Qt Video Overlay",
                 "pad-templates": {
                     "sink": {
-                        "caps": "video/x-raw(ANY):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n\nvideo/x-raw(memory:GLMemory):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n",
+                        "caps": "video/x-raw(ANY):\n         format: { RGBA, BGRA, YV12 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n",
                         "direction": "sink",
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "video/x-raw(memory:GLMemory):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n\nvideo/x-raw(ANY):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n",
+                        "caps": "video/x-raw(memory:GLMemory):\n         format: RGBA\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -11876,7 +12142,7 @@
                 "long-name": "Qt Video Sink",
                 "pad-templates": {
                     "sink": {
-                        "caps": "video/x-raw(memory:GLMemory):\n         format: { RGB, RGBA }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n",
+                        "caps": "video/x-raw(memory:GLMemory):\n         format: { RGB, RGBA, BGRA, YV12 }\n          width: [ 1, 2147483647 ]\n         height: [ 1, 2147483647 ]\n      framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n",
                         "direction": "sink",
                         "presence": "always"
                     }
@@ -13721,7 +13987,7 @@
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "audio/ac3:\n",
+                        "caps": "audio/x-ac3:\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -15626,70 +15892,193 @@
                 },
                 "rank": "primary"
             },
-            "rtppcmadepay": {
-                "author": "Edgard Lima <edgard.lima@gmail.com>, Zeeshan Ali <zeenix@gmail.com>",
-                "description": "Extracts PCMA audio from RTP packets",
-                "hierarchy": [
-                    "GstRtpPcmaDepay",
-                    "GstRTPBaseDepayload",
-                    "GstElement",
-                    "GstObject",
-                    "GInitiallyUnowned",
-                    "GObject"
-                ],
-                "klass": "Codec/Depayloader/Network/RTP",
-                "long-name": "RTP PCMA depayloader",
-                "pad-templates": {
-                    "sink": {
-                        "caps": "application/x-rtp:\n          media: audio\n        payload: 8\n     clock-rate: 8000\napplication/x-rtp:\n          media: audio\n     clock-rate: [ 1, 2147483647 ]\n  encoding-name: PCMA\n",
-                        "direction": "sink",
-                        "presence": "always"
-                    },
-                    "src": {
-                        "caps": "audio/x-alaw:\n       channels: 1\n           rate: [ 1, 2147483647 ]\n",
-                        "direction": "src",
-                        "presence": "always"
-                    }
-                },
-                "properties": {},
-                "rank": "secondary"
-            },
-            "rtppcmapay": {
-                "author": "Edgard Lima <edgard.lima@gmail.com>",
-                "description": "Payload-encodes PCMA audio into a RTP packet",
+            "rtppassthroughpay": {
+                "author": "Sebastian Dröge <sebastian@centricular.com>, Jonas Danielsson <jonas.danielsson@spiideo.com>",
+                "description": "Passes through RTP packets",
                 "hierarchy": [
-                    "GstRtpPcmaPay",
-                    "GstRTPBaseAudioPayload",
-                    "GstRTPBasePayload",
+                    "GstRtpPassthroughPay",
                     "GstElement",
                     "GstObject",
                     "GInitiallyUnowned",
                     "GObject"
                 ],
                 "klass": "Codec/Payloader/Network/RTP",
-                "long-name": "RTP PCMA payloader",
                 "pad-templates": {
                     "sink": {
-                        "caps": "audio/x-alaw:\n       channels: 1\n           rate: 8000\n",
+                        "caps": "application/x-rtp:\n        payload: [ 0, 127 ]\n     clock-rate: [ 1, 2147483647 ]\n",
                         "direction": "sink",
                         "presence": "always"
                     },
                     "src": {
-                        "caps": "application/x-rtp:\n          media: audio\n        payload: 8\n     clock-rate: 8000\n  encoding-name: PCMA\napplication/x-rtp:\n          media: audio\n        payload: [ 96, 127 ]\n     clock-rate: [ 1, 2147483647 ]\n  encoding-name: PCMA\n",
+                        "caps": "application/x-rtp:\n        payload: [ 0, 127 ]\n     clock-rate: [ 1, 2147483647 ]\n",
                         "direction": "src",
                         "presence": "always"
                     }
                 },
-                "properties": {},
-                "rank": "secondary"
-            },
-            "rtppcmudepay": {
-                "author": "Edgard Lima <edgard.lima@gmail.com>, Zeeshan Ali <zeenix@gmail.com>",
-                "description": "Extracts PCMU audio from RTP packets",
-                "hierarchy": [
-                    "GstRtpPcmuDepay",
-                    "GstRTPBaseDepayload",
-                    "GstElement",
+                "properties": {
+                    "mtu": {
+                        "blurb": "Maximum size of one packet",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "-1",
+                        "min": "28",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": true
+                    },
+                    "pt": {
+                        "blurb": "The payload type of the packets",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "128",
+                        "max": "128",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": true
+                    },
+                    "seqnum": {
+                        "blurb": "The RTP sequence number of the last processed packet",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "65535",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": false
+                    },
+                    "seqnum-offset": {
+                        "blurb": "Offset to add to all outgoing seqnum (-1 = random)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "65535",
+                        "min": "-1",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gint",
+                        "writable": true
+                    },
+                    "stats": {
+                        "blurb": "Various statistics",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "application/x-rtp-payload-stats, clock-rate=(uint)0, running-time=(guint64)0, seqnum=(uint)0, timestamp=(uint)0, ssrc=(uint)0, pt=(uint)128, seqnum-offset=(uint)0, timestamp-offset=(uint)0;",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstStructure",
+                        "writable": false
+                    },
+                    "timestamp": {
+                        "blurb": "The RTP timestamp of the last processed packet",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "-1",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": false
+                    },
+                    "timestamp-offset": {
+                        "blurb": "Offset to add to all outgoing timestamps (default = random)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "-1",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": true
+                    }
+                },
+                "rank": "none"
+            },
+            "rtppcmadepay": {
+                "author": "Edgard Lima <edgard.lima@gmail.com>, Zeeshan Ali <zeenix@gmail.com>",
+                "description": "Extracts PCMA audio from RTP packets",
+                "hierarchy": [
+                    "GstRtpPcmaDepay",
+                    "GstRTPBaseDepayload",
+                    "GstElement",
+                    "GstObject",
+                    "GInitiallyUnowned",
+                    "GObject"
+                ],
+                "klass": "Codec/Depayloader/Network/RTP",
+                "long-name": "RTP PCMA depayloader",
+                "pad-templates": {
+                    "sink": {
+                        "caps": "application/x-rtp:\n          media: audio\n        payload: 8\n     clock-rate: 8000\napplication/x-rtp:\n          media: audio\n     clock-rate: [ 1, 2147483647 ]\n  encoding-name: PCMA\n",
+                        "direction": "sink",
+                        "presence": "always"
+                    },
+                    "src": {
+                        "caps": "audio/x-alaw:\n       channels: 1\n           rate: [ 1, 2147483647 ]\n",
+                        "direction": "src",
+                        "presence": "always"
+                    }
+                },
+                "properties": {},
+                "rank": "secondary"
+            },
+            "rtppcmapay": {
+                "author": "Edgard Lima <edgard.lima@gmail.com>",
+                "description": "Payload-encodes PCMA audio into a RTP packet",
+                "hierarchy": [
+                    "GstRtpPcmaPay",
+                    "GstRTPBaseAudioPayload",
+                    "GstRTPBasePayload",
+                    "GstElement",
+                    "GstObject",
+                    "GInitiallyUnowned",
+                    "GObject"
+                ],
+                "klass": "Codec/Payloader/Network/RTP",
+                "long-name": "RTP PCMA payloader",
+                "pad-templates": {
+                    "sink": {
+                        "caps": "audio/x-alaw:\n       channels: 1\n           rate: 8000\n",
+                        "direction": "sink",
+                        "presence": "always"
+                    },
+                    "src": {
+                        "caps": "application/x-rtp:\n          media: audio\n        payload: 8\n     clock-rate: 8000\n  encoding-name: PCMA\napplication/x-rtp:\n          media: audio\n        payload: [ 96, 127 ]\n     clock-rate: [ 1, 2147483647 ]\n  encoding-name: PCMA\n",
+                        "direction": "src",
+                        "presence": "always"
+                    }
+                },
+                "properties": {},
+                "rank": "secondary"
+            },
+            "rtppcmudepay": {
+                "author": "Edgard Lima <edgard.lima@gmail.com>, Zeeshan Ali <zeenix@gmail.com>",
+                "description": "Extracts PCMU audio from RTP packets",
+                "hierarchy": [
+                    "GstRtpPcmuDepay",
+                    "GstRTPBaseDepayload",
+                    "GstElement",
                     "GstObject",
                     "GInitiallyUnowned",
                     "GObject"
@@ -16687,6 +17076,20 @@
                     }
                 },
                 "properties": {
+                    "picture-id": {
+                        "blurb": "Currently used picture-id for payloading",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "32767",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gint",
+                        "writable": false
+                    },
                     "picture-id-mode": {
                         "blurb": "The picture ID mode for payloading",
                         "conditionally-available": false,
@@ -16795,6 +17198,20 @@
                     }
                 },
                 "properties": {
+                    "picture-id": {
+                        "blurb": "Currently used picture-id for payloading",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "32767",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gint",
+                        "writable": false
+                    },
                     "picture-id-mode": {
                         "blurb": "The picture ID mode for payloading",
                         "conditionally-available": false,
@@ -16806,6 +17223,20 @@
                         "readable": true,
                         "type": "GstVP9RTPPayMode",
                         "writable": true
+                    },
+                    "picture-id-offset": {
+                        "blurb": "Offset to add to the initial picture-id (-1 = random)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "-1",
+                        "max": "32767",
+                        "min": "-1",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gint",
+                        "writable": true
                     }
                 },
                 "rank": "marginal"
@@ -16825,7 +17256,7 @@
                 "long-name": "RTP Raw Video depayloader",
                 "pad-templates": {
                     "sink": {
-                        "caps": "application/x-rtp:\n          media: video\n     clock-rate: 90000\n  encoding-name: RAW\n       sampling: { (string)RGB, (string)RGBA, (string)BGR, (string)BGRA, (string)YCbCr-4:4:4, (string)YCbCr-4:2:2, (string)YCbCr-4:2:0, (string)YCbCr-4:1:1 }\n          depth: { (string)8, (string)10, (string)12, (string)16 }\n",
+                        "caps": "application/x-rtp:\n          media: video\n     clock-rate: 90000\n  encoding-name: RAW\n       sampling: { (string)RGB, (string)RGBA, (string)BGR, (string)BGRA, (string)YCbCr-4:4:4, (string)YCbCr-4:2:2, (string)YCbCr-4:2:0, (string)YCbCr-4:1:1 }\n          depth: 8\napplication/x-rtp:\n          media: video\n     clock-rate: 90000\n  encoding-name: RAW\n       sampling: YCbCr-4:2:2\n          depth: 10\n",
                         "direction": "sink",
                         "presence": "always"
                     },
@@ -17412,6 +17843,18 @@
                         "type": "GstStructure",
                         "writable": true
                     },
+                    "timeout-inactive-sources": {
+                        "blurb": "Whether sources that don't receive RTP or RTCP packets for longer than 5x RTCP interval should be removed",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "true",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "ts-offset-smoothing-factor": {
                         "blurb": "Sets a smoothing factor for the timestamp offset in number of values for a calculated running moving average. (0 = no smoothing factor)",
                         "conditionally-available": false,
@@ -18000,7 +18443,7 @@
             "rtphdrextntp64": {
                 "RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:ntp-64",
                 "author": "Sebastian Dröge <sebastian@centricular.com>",
-                "description": "Extends RTP packets to add or retrieve a 64-bit NTP timestamp as specified in RFC6501",
+                "description": "Extends RTP packets to add or retrieve a 64-bit NTP timestamp as specified in RFC6051",
                 "hierarchy": [
                     "GstRTPHeaderExtensionNtp64",
                     "GstRTPHeaderExtension",
@@ -18347,6 +18790,18 @@
                         "type": "gboolean",
                         "writable": true
                     },
+                    "rfc7273-reference-timestamp-meta-only": {
+                        "blurb": "When enabled the PTS on the buffers are calculated normally and the RFC7273 clock is only used for the reference timestamp meta",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "rfc7273-sync": {
                         "blurb": "Synchronize received streams to the RFC7273 clock (requires clock and offset to be provided)",
                         "conditionally-available": false,
@@ -18359,6 +18814,18 @@
                         "type": "gboolean",
                         "writable": true
                     },
+                    "rfc7273-use-system-clock": {
+                        "blurb": "Use system clock as RFC7273 media clock (requires system clock to be synced externally)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "rtx-deadline": {
                         "blurb": "The deadline for a valid RTX request in milliseconds. (-1 automatic)",
                         "conditionally-available": false,
@@ -19344,6 +19811,18 @@
                         "type": "GstStructure",
                         "writable": false
                     },
+                    "timeout-inactive-sources": {
+                        "blurb": "Whether sources that don't receive RTP or RTCP packets for longer than 5x RTCP interval should be removed",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "true",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "twcc-stats": {
                         "blurb": "Various statistics from TWCC",
                         "conditionally-available": false,
@@ -19773,6 +20252,74 @@
                     }
                 ]
             },
+            "GstRTPMux": {
+                "hierarchy": [
+                    "GstRTPMux",
+                    "GstElement",
+                    "GstObject",
+                    "GInitiallyUnowned",
+                    "GObject"
+                ],
+                "kind": "object",
+                "properties": {
+                    "seqnum": {
+                        "blurb": "The RTP sequence number of the last processed packet",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "0",
+                        "max": "-1",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": false
+                    },
+                    "seqnum-offset": {
+                        "blurb": "Offset to add to all outgoing seqnum (-1 = random)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "-1",
+                        "max": "2147483647",
+                        "min": "-1",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gint",
+                        "writable": true
+                    },
+                    "ssrc": {
+                        "blurb": "The SSRC of the packets (default == random)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "-1",
+                        "max": "-1",
+                        "min": "0",
+                        "mutable": "playing",
+                        "readable": true,
+                        "type": "guint",
+                        "writable": true
+                    },
+                    "timestamp-offset": {
+                        "blurb": "Offset to add to all outgoing timestamps (-1 = random)",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "-1",
+                        "max": "2147483647",
+                        "min": "-1",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gint",
+                        "writable": true
+                    }
+                }
+            },
             "GstRtpNtpTimeSource": {
                 "kind": "enum",
                 "values": [
@@ -20119,6 +20666,18 @@
                         "type": "GstStructure",
                         "writable": false
                     },
+                    "timeout-inactive-sources": {
+                        "blurb": "Whether sources that don't receive RTP or RTCP packets for longer than 5x RTCP interval should be removed",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "true",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "twcc-feedback-interval": {
                         "blurb": "The interval to send TWCC reports on",
                         "conditionally-available": false,
@@ -20836,6 +21395,30 @@
                         "type": "gboolean",
                         "writable": true
                     },
+                    "extra-http-request-headers": {
+                        "blurb": "Extra headers to append to HTTP requests when in tunneled mode",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "extra-http-request-headers;",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "GstStructure",
+                        "writable": true
+                    },
+                    "force-non-compliant-url": {
+                        "blurb": "Revert to old non-compliant method of constructing URLs",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "ignore-x-server-reply": {
                         "blurb": "Whether to ignore the x-server-ip-address server header reply",
                         "conditionally-available": false,
@@ -21151,6 +21734,18 @@
                         "type": "guint64",
                         "writable": true
                     },
+                    "tcp-timestamp": {
+                        "blurb": "Timestamp RTP packets with receive times in TCP/HTTP mode",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "teardown-timeout": {
                         "blurb": "When transitioning PAUSED-READY, allow up to timeout (in nanoseconds) delay in order to send teardown (0 = disabled)",
                         "conditionally-available": false,
@@ -21257,7 +21852,7 @@
                         "construct": false,
                         "construct-only": false,
                         "controllable": false,
-                        "default": "GStreamer/1.22.0",
+                        "default": "GStreamer/{VERSION}",
                         "mutable": "null",
                         "readable": true,
                         "type": "gchararray",
@@ -21288,7 +21883,7 @@
                         "writable": true
                     }
                 },
-                "rank": "none",
+                "rank": "primary",
                 "signals": {
                     "accept-certificate": {
                         "args": [
@@ -21816,7 +22411,7 @@
                         "construct": false,
                         "construct-only": false,
                         "controllable": false,
-                        "default": "GStreamer 1.22.0",
+                        "default": "GStreamer {VERSION}",
                         "mutable": "null",
                         "readable": true,
                         "type": "gchararray",
@@ -23253,7 +23848,7 @@
                         "construct": false,
                         "construct-only": false,
                         "controllable": false,
-                        "default": "GStreamer souphttpsrc 1.22.0 ",
+                        "default": "GStreamer souphttpsrc {VERSION} ",
                         "mutable": "null",
                         "readable": true,
                         "type": "gchararray",
@@ -23284,7 +23879,23 @@
                         "writable": true
                     }
                 },
-                "rank": "primary"
+                "rank": "primary",
+                "signals": {
+                    "accept-certificate": {
+                        "args": [
+                            {
+                                "name": "arg0",
+                                "type": "GTlsCertificate"
+                            },
+                            {
+                                "name": "arg1",
+                                "type": "GTlsCertificateFlags"
+                            }
+                        ],
+                        "return-type": "gboolean",
+                        "when": "last"
+                    }
+                }
             }
         },
         "filename": "gstsoup",
@@ -24681,6 +25292,18 @@
                         "type": "gchararray",
                         "writable": true
                     },
+                    "multicast-source": {
+                        "blurb": "List of source to receive the stream with '+' (positive filter) or '-' (negative filter, ignored for now) prefix (e.g., \"+SOURCE0+SOURCE1+SOURCE2\"). Alternatively, user can use URI query with the key value \"multicast-source\"",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "NULL",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gchararray",
+                        "writable": true
+                    },
                     "port": {
                         "blurb": "The port to receive the packets from, 0=allocate",
                         "conditionally-available": false,
@@ -24891,7 +25514,7 @@
                 "long-name": "Video (video4linux2) Sink",
                 "pad-templates": {
                     "sink": {
-                        "caps": "image/jpeg:\nvideo/mpeg:\n    mpegversion: 4\n   systemstream: false\nvideo/mpeg:\n    mpegversion: { (int)1, (int)2 }\nvideo/mpegts:\n   systemstream: true\nvideo/x-bayer:\n         format: { bggr, gbrg, grbg, rggb }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-dv:\n   systemstream: true\nvideo/x-fwht:\nvideo/x-h263:\n        variant: itu\nvideo/x-h264:\n  stream-format: { (string)byte-stream, (string)avc }\n      alignment: au\nvideo/x-h265:\n  stream-format: byte-stream\n      alignment: au\nvideo/x-pwc1:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-pwc2:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-raw:\n         format: { RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YV12, YUY2, YVYU, UYVY, Y42B, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, NV61, NV16, NV21, NV12, I420, ARGB, xRGB, BGRA, BGRx, BGR15, RGB15 }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-sonix:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-vp8:\nvideo/x-vp9:\nvideo/x-wmv:\n     wmvversion: 3\n         format: WVC1\n\nvideo/x-raw(format:Interlaced):\n         format: { RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YV12, YUY2, YVYU, UYVY, Y42B, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, NV61, NV16, NV21, NV12, I420, ARGB, xRGB, BGRA, BGRx, BGR15, RGB15 }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\n interlace-mode: alternate\n",
+                        "caps": "image/jpeg:\n         parsed: true\nvideo/mpeg:\n    mpegversion: 4\n   systemstream: false\nvideo/mpeg:\n    mpegversion: { (int)1, (int)2 }\nvideo/mpegts:\n   systemstream: true\nvideo/x-bayer:\n         format: { bggr, gbrg, grbg, rggb, bggr10le, gbrg10le, grbg10le, rggb10le, bggr12le, gbrg12le, grbg12le, rggb12le, bggr14le, gbrg14le, grbg14le, rggb14le, bggr16le, gbrg16le, grbg16le, rggb16le }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-dv:\n   systemstream: true\nvideo/x-fwht:\nvideo/x-h263:\n        variant: itu\nvideo/x-h264:\n  stream-format: { (string)byte-stream, (string)avc }\n      alignment: au\nvideo/x-h265:\n  stream-format: byte-stream\n      alignment: au\nvideo/x-pwc1:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-pwc2:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-raw:\n         format: { RGB15, RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, BGR15, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YUY2, YVYU, UYVY, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, P010_10LE, NV61, NV16, NV21, NV12, I420, Y42B, YV12, ARGB, xRGB, BGRA, BGRx }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-sonix:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-vp8:\nvideo/x-vp9:\nvideo/x-wmv:\n     wmvversion: 3\n         format: WVC1\n\nvideo/x-raw(format:Interlaced):\n         format: { RGB15, RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, BGR15, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YUY2, YVYU, UYVY, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, P010_10LE, NV61, NV16, NV21, NV12, I420, Y42B, YV12, ARGB, xRGB, BGRA, BGRx }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\n interlace-mode: alternate\n",
                         "direction": "sink",
                         "presence": "always"
                     }
@@ -25199,7 +25822,7 @@
                 "long-name": "Video (video4linux2) Source",
                 "pad-templates": {
                     "src": {
-                        "caps": "image/jpeg:\nvideo/mpeg:\n    mpegversion: 4\n   systemstream: false\nvideo/mpeg:\n    mpegversion: { (int)1, (int)2 }\nvideo/mpegts:\n   systemstream: true\nvideo/x-bayer:\n         format: { bggr, gbrg, grbg, rggb }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-dv:\n   systemstream: true\nvideo/x-fwht:\nvideo/x-h263:\n        variant: itu\nvideo/x-h264:\n  stream-format: { (string)byte-stream, (string)avc }\n      alignment: au\nvideo/x-h265:\n  stream-format: byte-stream\n      alignment: au\nvideo/x-pwc1:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-pwc2:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-raw:\n         format: { RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YV12, YUY2, YVYU, UYVY, Y42B, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, NV61, NV16, NV21, NV12, I420, ARGB, xRGB, BGRA, BGRx, BGR15, RGB15 }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-sonix:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-vp8:\nvideo/x-vp9:\nvideo/x-wmv:\n     wmvversion: 3\n         format: WVC1\n\nvideo/x-raw(format:Interlaced):\n         format: { RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YV12, YUY2, YVYU, UYVY, Y42B, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, NV61, NV16, NV21, NV12, I420, ARGB, xRGB, BGRA, BGRx, BGR15, RGB15 }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\n interlace-mode: alternate\n",
+                        "caps": "image/jpeg:\n         parsed: true\nvideo/mpeg:\n    mpegversion: 4\n   systemstream: false\nvideo/mpeg:\n    mpegversion: { (int)1, (int)2 }\nvideo/mpegts:\n   systemstream: true\nvideo/x-bayer:\n         format: { bggr, gbrg, grbg, rggb, bggr10le, gbrg10le, grbg10le, rggb10le, bggr12le, gbrg12le, grbg12le, rggb12le, bggr14le, gbrg14le, grbg14le, rggb14le, bggr16le, gbrg16le, grbg16le, rggb16le }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-dv:\n   systemstream: true\nvideo/x-fwht:\nvideo/x-h263:\n        variant: itu\nvideo/x-h264:\n  stream-format: { (string)byte-stream, (string)avc }\n      alignment: au\nvideo/x-h265:\n  stream-format: byte-stream\n      alignment: au\nvideo/x-pwc1:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-pwc2:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-raw:\n         format: { RGB15, RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, BGR15, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YUY2, YVYU, UYVY, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, P010_10LE, NV61, NV16, NV21, NV12, I420, Y42B, YV12, ARGB, xRGB, BGRA, BGRx }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-sonix:\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\nvideo/x-vp8:\nvideo/x-vp9:\nvideo/x-wmv:\n     wmvversion: 3\n         format: WVC1\n\nvideo/x-raw(format:Interlaced):\n         format: { RGB15, RGB16, BGR, RGB, ABGR, xBGR, RGBA, RGBx, BGR15, GRAY8, GRAY16_LE, GRAY16_BE, YVU9, YUY2, YVYU, UYVY, Y41B, YUV9, NV12_64Z32, NV12_8L128, NV12_10BE_8L128, NV24, NV12_16L32S, P010_10LE, NV61, NV16, NV21, NV12, I420, Y42B, YV12, ARGB, xRGB, BGRA, BGRx }\n          width: [ 1, 32768 ]\n         height: [ 1, 32768 ]\n      framerate: [ 0/1, 2147483647/1 ]\n interlace-mode: alternate\n",
                         "direction": "src",
                         "presence": "always"
                     }
@@ -26293,7 +26916,7 @@
                     "method": {
                         "blurb": "method (deprecated, use video-direction instead)",
                         "conditionally-available": false,
-                        "construct": true,
+                        "construct": false,
                         "construct-only": false,
                         "controllable": true,
                         "default": "none (0)",
@@ -26516,7 +27139,7 @@
         "url": "Unknown package origin"
     },
     "vpx": {
-        "description": "VP8 plugin",
+        "description": "VP8/VP9 video encoding and decoding based on libvpx",
         "elements": {
             "vp8dec": {
                 "author": "David Schleef <ds@entropywave.com>, Sebastian Dröge <sebastian.droege@collabora.co.uk>",
@@ -28006,6 +28629,18 @@
                         "type": "gchararray",
                         "writable": true
                     },
+                    "enable-navigation-events": {
+                        "blurb": "When enabled, navigation events are handled",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "endx": {
                         "blurb": "X coordinate of bottom right corner of area to be recorded (0 for bottom right of screen)",
                         "conditionally-available": false,
diff --git a/docs/meson.build b/docs/meson.build
index 8aa95a20cc9dbbfab45e44552529b6070b3729e8..10bf4a138573ab64dadf578bc50a38189d6bf165 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -85,35 +85,27 @@ endforeach
 
 excludes += [join_paths(meson.current_source_dir(), '..', 'sys', 'rpicamsrc', 'Raspi*.[ch]')]
 
-list_plugin_res = run_command(python3, '-c',
-'''
-import sys
-import json
-
-with open("@0@") as f:
-    print(':'.join(json.load(f).keys()), end='')
-'''.format(plugins_cache),
-  check: true)
-foreach plugin_name: list_plugin_res.stdout().split(':')
-    plugins_doc += [hotdoc.generate_doc(plugin_name,
-    project_version: api_version,
-      sitemap: 'sitemap.txt',
-      index: 'index.md',
-      gst_index: 'index.md',
-      gst_smart_index: true,
-      gst_c_sources: [
-        '../sys/*/*.[cmh]',
-        '../ext/*/*.[ch]',
-        '../ext/*/*/*.[ch]',
-        '../gst/*/*.[ch]',
-      ],
-      gst_c_source_filters: excludes,
-      dependencies: [gst_dep],
-      gst_order_generated_subpages: true,
-      install: false,
-      disable_incremental_build: true,
-      gst_cache_file: plugins_cache,
-      gst_plugin_name: plugin_name,
-      include_paths: join_paths(meson.current_source_dir(), '..'),
-      )]
-endforeach
+cdir = meson.current_source_dir()
+if host_machine.system() == 'windows'
+  pathsep = ';'
+else
+  pathsep = ':'
+endif
+gst_plugins_doc = run_command(
+    plugins_cache_generator,
+    'hotdoc-config',
+    '--builddir', meson.current_build_dir(),
+    '--project_version', api_version,
+    '--sitemap', cdir / 'sitemap.txt',
+    '--index', cdir / 'index.md',
+    '--gst_index', cdir / 'index.md',
+    '--gst_c_sources',
+        cdir / '../sys/*/*.[cmh]',
+        cdir / '../ext/*/*.[ch]',
+        cdir / '../ext/*/*/*.[ch]',
+        cdir / '../gst/*/*.[ch]',
+    '--gst_c_source_filters', excludes,
+    '--gst_cache_file', plugins_cache,
+    '--include_paths', cdir / '..',
+    check: true,
+).stdout().split(pathsep)
diff --git a/ext/adaptivedemux2/dash/gstdashdemux.c b/ext/adaptivedemux2/dash/gstdashdemux.c
index ab7445f532edd39399980d86b5cb2ecb489e9c2e..063c5a27657ec3cab0ad7f50127f713057b4110e 100644
--- a/ext/adaptivedemux2/dash/gstdashdemux.c
+++ b/ext/adaptivedemux2/dash/gstdashdemux.c
@@ -310,6 +310,7 @@ enum
   PROP_MAX_VIDEO_HEIGHT,
   PROP_MAX_VIDEO_FRAMERATE,
   PROP_PRESENTATION_DELAY,
+  PROP_START_BITRATE,
   PROP_LAST
 };
 
@@ -319,6 +320,7 @@ enum
 #define DEFAULT_MAX_VIDEO_FRAMERATE_N     0
 #define DEFAULT_MAX_VIDEO_FRAMERATE_D     1
 #define DEFAULT_PRESENTATION_DELAY     "10s"    /* 10s */
+#define DEFAULT_START_BITRATE             0
 
 /* Clock drift compensation for live streams */
 #define SLOW_CLOCK_UPDATE_INTERVAL  (1000000 * 30 * 60) /* 30 minutes */
@@ -354,6 +356,8 @@ static void gst_dash_demux_dispose (GObject * obj);
 /* GstAdaptiveDemuxStream */
 static GstFlowReturn
 gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
+static void
+gst_dash_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream);
 static GstClockTime
 gst_dash_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream *
     stream);
@@ -495,6 +499,8 @@ gst_dash_demux_stream_class_init (GstDashDemux2StreamClass * klass)
       gst_dash_demux_stream_data_received;
   adaptivedemux2stream_class->need_another_chunk =
       gst_dash_demux_stream_need_another_chunk;
+  adaptivedemux2stream_class->create_tracks =
+      gst_dash_demux_stream_create_tracks;
 }
 
 
@@ -633,13 +639,24 @@ gst_dash_demux2_class_init (GstDashDemux2Class * klass)
           DEFAULT_PRESENTATION_DELAY,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * dashdemux2:start-bitrate:
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_START_BITRATE,
+      g_param_spec_uint ("start-bitrate", "Starting Bitrate",
+          "Initial bitrate to use to choose first alternate (0 = automatic) (bits/s)",
+          0, G_MAXUINT, DEFAULT_START_BITRATE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
 
   gst_element_class_set_static_metadata (gstelement_class,
       "DASH Demuxer",
       "Codec/Demuxer/Adaptive",
       "Dynamic Adaptive Streaming over HTTP demuxer",
-      "Edward Hervey <edward@centricular.com>\n"
+      "Edward Hervey <edward@centricular.com>, "
       "Jan Schmidt <jan@centricular.com>");
 
 
@@ -697,6 +714,9 @@ gst_dash_demux_set_property (GObject * object, guint prop_id,
       g_free (demux->default_presentation_delay);
       demux->default_presentation_delay = g_value_dup_string (value);
       break;
+    case PROP_START_BITRATE:
+      demux->start_bitrate = g_value_get_uint (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -726,6 +746,9 @@ gst_dash_demux_get_property (GObject * object, guint prop_id, GValue * value,
       else
         g_value_set_string (value, demux->default_presentation_delay);
       break;
+    case PROP_START_BITRATE:
+      g_value_set_uint (value, demux->start_bitrate);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -738,13 +761,20 @@ gst_dash_demux_setup_mpdparser_streams (GstDashDemux2 * demux,
 {
   gboolean has_streams = FALSE;
   GList *adapt_sets, *iter;
+  guint start_bitrate = demux->start_bitrate;
+
+  if (start_bitrate == 0) {
+    /* Using g_object_get so it goes through mutex locking in adaptivedemux2 */
+    g_object_get (demux, "connection-bitrate", &start_bitrate, NULL);
+  }
 
   adapt_sets = gst_mpd_client2_get_adaptation_sets (client);
   for (iter = adapt_sets; iter; iter = g_list_next (iter)) {
     GstMPDAdaptationSetNode *adapt_set_node = iter->data;
 
-    if (gst_mpd_client2_setup_streaming (client, adapt_set_node))
-      has_streams = TRUE;
+    has_streams |= gst_mpd_client2_setup_streaming (client, adapt_set_node,
+        start_bitrate, demux->max_video_width, demux->max_video_height,
+        demux->max_video_framerate_n, demux->max_video_framerate_d);
   }
 
   if (!has_streams) {
@@ -808,7 +838,7 @@ gst_dash_demux_setup_all_streams (GstDashDemux2 * demux)
   GST_DEBUG_OBJECT (demux, "Creating stream objects");
   for (i = 0; i < gst_mpd_client2_get_nb_active_stream (demux->client); i++) {
     GstDashDemux2Stream *stream;
-    GstAdaptiveDemuxTrack *track;
+    GstAdaptiveDemuxTrack *track = NULL;
     GstStreamType streamtype;
     GstActiveStream *active_stream;
     GstCaps *caps, *codec_caps;
@@ -816,6 +846,7 @@ gst_dash_demux_setup_all_streams (GstDashDemux2 * demux)
     GstStructure *s;
     gchar *lang = NULL;
     GstTagList *tags = NULL;
+    gchar *track_id = NULL;
 
     active_stream =
         gst_mpd_client2_get_active_stream_by_index (demux->client, i);
@@ -836,6 +867,46 @@ gst_dash_demux_setup_all_streams (GstDashDemux2 * demux)
     if (streamtype == GST_STREAM_TYPE_UNKNOWN)
       continue;
 
+    /* Fill container-specific track-id string */
+    if (active_stream->cur_adapt_set) {
+      /* FIXME: For CEA 608 and CEA 708 tracks we should check the Accessibility descriptor:
+       * https://dev.w3.org/html5/html-sourcing-inband-tracks/#mpegdash
+       * * An ISOBMFF CEA 608 caption service: the string "cc" concatenated with
+       * the value of the 'channel-number' field in the Accessibility descriptor
+       * in the ContentComponent or AdaptationSet.
+       * * An ISOBMFF CEA 708 caption service: the string "sn" concatenated with
+       * the value of the 'service-number' field in the Accessibility descriptor
+       * in the ContentComponent or AdaptationSet.
+       * * Otherwise:
+       */
+
+      /* Content of the id attribute in the ContentComponent or AdaptationSet
+       * element. */
+      if (active_stream->cur_adapt_set->id) {
+        track_id = g_strdup_printf ("%d", active_stream->cur_adapt_set->id);
+      } else {
+        GList *it;
+
+        for (it = active_stream->cur_adapt_set->ContentComponents; it;
+            it = it->next) {
+          GstMPDContentComponentNode *cc_node = it->data;
+          if (cc_node->id) {
+            track_id = g_strdup_printf ("%u", cc_node->id);
+            break;
+          }
+        }
+        /* Empty string if the id attribute is not present on either
+         * element. */
+        if (!track_id)
+          track_id = g_strdup ("");
+      }
+    }
+    if (track_id) {
+      tags = gst_tag_list_new (GST_TAG_CONTAINER_SPECIFIC_TRACK_ID, track_id,
+          NULL);
+      g_free (track_id);
+    }
+
     stream_id =
         g_strdup_printf ("%s-%d", gst_stream_type_get_name (streamtype), i);
 
@@ -864,26 +935,42 @@ gst_dash_demux_setup_all_streams (GstDashDemux2 * demux)
     }
 
     if (lang) {
+      if (!tags)
+        tags = gst_tag_list_new_empty ();
+
       if (gst_tag_check_language_code (lang))
-        tags = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, lang, NULL);
+        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_LANGUAGE_CODE,
+            lang, NULL);
       else
-        tags = gst_tag_list_new (GST_TAG_LANGUAGE_NAME, lang, NULL);
+        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_LANGUAGE_NAME,
+            lang, NULL);
     }
 
-    /* Create the track this stream provides */
-    track = gst_adaptive_demux_track_new (GST_ADAPTIVE_DEMUX_CAST (demux),
-        streamtype, GST_STREAM_FLAG_NONE, stream_id, codec_caps, tags);
-
     stream = gst_dash_demux_stream_new (demux->client->period_idx, stream_id);
     GST_ADAPTIVE_DEMUX2_STREAM_CAST (stream)->stream_type = streamtype;
 
+    /* Maybe there are multiple tracks in one stream such as some mpeg-ts
+     * streams, need create track by stream->stream_collection lately */
+    if (!codec_caps) {
+      GST_ADAPTIVE_DEMUX2_STREAM_CAST (stream)->pending_tracks = TRUE;
+    } else {
+      /* Create the track this stream provides */
+      track = gst_adaptive_demux_track_new (GST_ADAPTIVE_DEMUX_CAST (demux),
+          streamtype, GST_STREAM_FLAG_NONE, stream_id, codec_caps, tags);
+    }
+
     g_free (stream_id);
+    if (tags)
+      gst_adaptive_demux2_stream_set_tags (GST_ADAPTIVE_DEMUX2_STREAM_CAST
+          (stream), gst_tag_list_ref (tags));
 
     gst_adaptive_demux2_add_stream (GST_ADAPTIVE_DEMUX_CAST (demux),
         GST_ADAPTIVE_DEMUX2_STREAM_CAST (stream));
-    gst_adaptive_demux2_stream_add_track (GST_ADAPTIVE_DEMUX2_STREAM_CAST
-        (stream), track);
-    stream->track = track;
+    if (track) {
+      gst_adaptive_demux2_stream_add_track (GST_ADAPTIVE_DEMUX2_STREAM_CAST
+          (stream), track);
+      stream->track = track;
+    }
     stream->active_stream = active_stream;
 
     if (active_stream->cur_representation) {
@@ -900,9 +987,6 @@ gst_dash_demux_setup_all_streams (GstDashDemux2 * demux)
         || gst_structure_has_name (s, "audio/x-m4a");
     gst_adaptive_demux2_stream_set_caps (GST_ADAPTIVE_DEMUX2_STREAM_CAST
         (stream), caps);
-    if (tags)
-      gst_adaptive_demux2_stream_set_tags (GST_ADAPTIVE_DEMUX2_STREAM_CAST
-          (stream), tags);
     stream->index = i;
 
     if (active_stream->cur_adapt_set &&
@@ -918,6 +1002,46 @@ gst_dash_demux_setup_all_streams (GstDashDemux2 * demux)
   return TRUE;
 }
 
+static void
+gst_dash_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream)
+{
+  guint i;
+  gchar *stream_id;
+
+  /* Use the stream->stream_collection to check and
+   * create the track which has not yet been created */
+  for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
+      i++) {
+    GstStream *gst_stream =
+        gst_stream_collection_get_stream (stream->stream_collection, i);
+    GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
+    GstAdaptiveDemuxTrack *track;
+    GstTagList *tags;
+    GstCaps *caps;
+
+    if (stream_type == GST_STREAM_TYPE_UNKNOWN)
+      continue;
+
+    caps = gst_stream_get_caps (gst_stream);
+    tags = gst_stream_get_tags (gst_stream);
+
+    GST_DEBUG_OBJECT (stream, "create track type %d of the stream",
+        stream_type);
+    stream->stream_type |= stream_type;
+    stream_id =
+        g_strdup_printf ("%s-%d", gst_stream_type_get_name (stream_type), i);
+    /* Create the track this stream provides */
+    track = gst_adaptive_demux_track_new (stream->demux,
+        stream_type, GST_STREAM_FLAG_NONE, stream_id, caps, tags);
+    g_free (stream_id);
+
+    track->upstream_stream_id =
+        g_strdup (gst_stream_get_stream_id (gst_stream));
+    gst_adaptive_demux2_stream_add_track (stream, track);
+    gst_adaptive_demux_track_unref (track);
+  }
+}
+
 static void
 gst_dash_demux_send_content_protection_event (gpointer data, gpointer userdata)
 {
@@ -2283,6 +2407,10 @@ gst_dash_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
     goto end;
   }
 
+  /* If not calculated yet, continue using start bitrate */
+  if (bitrate == 0)
+    bitrate = demux->start_bitrate;
+
   GST_DEBUG_OBJECT (stream,
       "Trying to change to bitrate: %" G_GUINT64_FORMAT, bitrate);
 
@@ -3661,7 +3789,7 @@ gst_dash_demux_clock_drift_new (GstDashDemux2 * demux)
 {
   GstDashDemux2ClockDrift *clock_drift;
 
-  clock_drift = g_slice_new0 (GstDashDemux2ClockDrift);
+  clock_drift = g_new0 (GstDashDemux2ClockDrift, 1);
   g_mutex_init (&clock_drift->clock_lock);
   clock_drift->next_update =
       GST_TIME_AS_USECONDS (gst_adaptive_demux2_get_monotonic_time
@@ -3678,7 +3806,7 @@ gst_dash_demux_clock_drift_free (GstDashDemux2ClockDrift * clock_drift)
       g_object_unref (clock_drift->ntp_clock);
     g_mutex_unlock (&clock_drift->clock_lock);
     g_mutex_clear (&clock_drift->clock_lock);
-    g_slice_free (GstDashDemux2ClockDrift, clock_drift);
+    g_free (clock_drift);
   }
 }
 
diff --git a/ext/adaptivedemux2/dash/gstdashdemux.h b/ext/adaptivedemux2/dash/gstdashdemux.h
index 58fe181a2e71961eec3c6ffd507de16544c86f98..425967ef23829197ba25d022afc6ea704b31309c 100644
--- a/ext/adaptivedemux2/dash/gstdashdemux.h
+++ b/ext/adaptivedemux2/dash/gstdashdemux.h
@@ -155,6 +155,7 @@ struct _GstDashDemux2
   gint max_video_width, max_video_height;
   gint max_video_framerate_n, max_video_framerate_d;
   gchar* default_presentation_delay; /* presentation time delay if MPD@suggestedPresentationDelay is not present */
+  guint start_bitrate; /* Initial bitrate to use before any bandwidth measurement */
 
   gboolean allow_trickmode_key_units;
 };
diff --git a/ext/adaptivedemux2/dash/gstmpd-prelude.h b/ext/adaptivedemux2/dash/gstmpd-prelude.h
index a49a8f85dfb060422c6acef2bc04171179bc3081..3f42ec5ced24a653791071fa31690923ccb5cd20 100644
--- a/ext/adaptivedemux2/dash/gstmpd-prelude.h
+++ b/ext/adaptivedemux2/dash/gstmpd-prelude.h
@@ -33,10 +33,8 @@
 #define gst_mpd_descriptor_type_node_get_type gst_mpd_descriptor_type_node2_get_type
 #define gst_mpd_descriptor_type_node_new gst_mpd_descriptor_type_node2_new
 #define gst_mpd_helper_combine_urls gst_mpd_helper2_combine_urls
-#define gst_mpd_helper_get_audio_codec_from_mime gst_mpd_helper2_get_audio_codec_from_mime
 #define gst_mpd_helper_get_mpd_type gst_mpd_helper2_get_mpd_type
 #define gst_mpd_helper_get_SAP_type gst_mpd_helper2_get_SAP_type
-#define gst_mpd_helper_get_video_codec_from_mime gst_mpd_helper2_get_video_codec_from_mime
 #define gst_mpd_helper_mimetype_to_caps gst_mpd_helper2_mimetype_to_caps
 #define gst_mpd_helper_strncmp_ext gst_mpd_helper2_strncmp_ext
 #define gst_mpd_location_node_free gst_mpd_location_node2_free
diff --git a/ext/adaptivedemux2/dash/gstmpdadaptationsetnode.c b/ext/adaptivedemux2/dash/gstmpdadaptationsetnode.c
index c045ab7c3511c3e972958b12cc51725999ffd0bc..48aaf824f3671936a805b1d34c0579be6e070fb7 100644
--- a/ext/adaptivedemux2/dash/gstmpdadaptationsetnode.c
+++ b/ext/adaptivedemux2/dash/gstmpdadaptationsetnode.c
@@ -79,9 +79,9 @@ gst_mpd_adaptation_set_node_finalize (GObject * object)
     xmlFree (self->lang);
   if (self->contentType)
     xmlFree (self->contentType);
-  g_slice_free (GstXMLRatio, self->par);
-  g_slice_free (GstXMLConditionalUintType, self->segmentAlignment);
-  g_slice_free (GstXMLConditionalUintType, self->subsegmentAlignment);
+  g_free (self->par);
+  g_free (self->segmentAlignment);
+  g_free (self->subsegmentAlignment);
   g_list_free_full (self->Accessibility,
       (GDestroyNotify) gst_mpd_descriptor_type_node_free);
   g_list_free_full (self->Role,
diff --git a/ext/adaptivedemux2/dash/gstmpdclient.c b/ext/adaptivedemux2/dash/gstmpdclient.c
index c245012ed0f5692f0348b5ea654845c7375d5f99..470fb56d9e42247825b8d1f28a5a3bdb9363ce85 100644
--- a/ext/adaptivedemux2/dash/gstmpdclient.c
+++ b/ext/adaptivedemux2/dash/gstmpdclient.c
@@ -795,7 +795,7 @@ gst_mpd_client2_add_media_segment (GstActiveStream * stream,
 
   g_return_val_if_fail (stream->segments != NULL, FALSE);
 
-  media_segment = g_slice_new0 (GstMediaSegment);
+  media_segment = g_new0 (GstMediaSegment, 1);
 
   media_segment->SegmentURL = url_node;
   media_segment->number = number;
@@ -1420,7 +1420,7 @@ gst_mpd_client2_setup_media_presentation (GstMPDClient2 * client,
       goto syntax_error;
     }
 
-    stream_period = g_slice_new0 (GstStreamPeriod);
+    stream_period = g_new0 (GstStreamPeriod, 1);
     client->periods = g_list_append (client->periods, stream_period);
     stream_period->period = period_node;
     stream_period->number = idx++;
@@ -1612,11 +1612,14 @@ gst_mpd_client2_get_adaptation_sets (GstMPDClient2 * client)
 
 gboolean
 gst_mpd_client2_setup_streaming (GstMPDClient2 * client,
-    GstMPDAdaptationSetNode * adapt_set)
+    GstMPDAdaptationSetNode * adapt_set, gint64 max_bandwidth,
+    gint max_video_width, gint max_video_height,
+    gint max_video_framerate_n, gint max_video_framerate_d)
 {
-  GstMPDRepresentationNode *representation;
+  GstMPDRepresentationNode *representation = NULL;
   GList *rep_list = NULL;
   GstActiveStream *stream;
+  gint rep_id;
 
   rep_list = adapt_set->Representations;
   if (!rep_list) {
@@ -1624,7 +1627,7 @@ gst_mpd_client2_setup_streaming (GstMPDClient2 * client,
     return FALSE;
   }
 
-  stream = g_slice_new0 (GstActiveStream);
+  stream = g_new0 (GstActiveStream, 1);
   gst_mpdparser_init_active_stream_segments (stream);
 
   stream->baseURL_idx = 0;
@@ -1632,21 +1635,23 @@ gst_mpd_client2_setup_streaming (GstMPDClient2 * client,
 
   GST_DEBUG ("0. Current stream %p", stream);
 
-#if 0
-  /* fast start */
-  representation =
-      gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
-      stream->max_bandwidth);
+  rep_id = gst_mpd_client2_get_rep_idx_with_max_bandwidth (rep_list,
+      max_bandwidth, max_video_width, max_video_height,
+      max_video_framerate_n, max_video_framerate_d);
+
+  if (rep_id >= 0) {
+    GList *best_rep;
+
+    best_rep = g_list_nth (rep_list, rep_id);
+    if (best_rep)
+      representation = (GstMPDRepresentationNode *) best_rep->data;
+  }
 
   if (!representation) {
-    GST_WARNING
-        ("Can not retrieve a representation with the requested bandwidth");
+    GST_WARNING ("No representation with the requested bandwidth or video "
+        "resolution/framerate restriction");
     representation = gst_mpd_client2_get_lowest_representation (rep_list);
   }
-#else
-  /* slow start */
-  representation = gst_mpd_client2_get_lowest_representation (rep_list);
-#endif
 
   if (!representation) {
     GST_WARNING ("No valid representation in the MPD file, aborting...");
@@ -1770,7 +1775,7 @@ gst_mpd_client2_stream_seek (GstMPDClient2 * client, GstActiveStream * stream,
 
     g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
         (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
-    if (!GST_CLOCK_TIME_IS_VALID (duration)) {
+    if (!GST_CLOCK_TIME_IS_VALID (duration) || duration == 0) {
       return FALSE;
     }
 
@@ -2566,7 +2571,9 @@ gst_mpd_client2_get_rep_idx_with_max_bandwidth (GList * Representations,
   GstMPDRepresentationNode *representation;
   gint best_bandwidth = 0;
 
-  GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
+  GST_DEBUG ("Selecting rep with restrictions: bandwidth=%" G_GINT64_FORMAT ", "
+      "width=%i, height=%i, framerate=%i/%i", max_bandwidth, max_video_width,
+      max_video_height, max_video_framerate_n, max_video_framerate_d);
 
   if (Representations == NULL)
     return -1;
diff --git a/ext/adaptivedemux2/dash/gstmpdclient.h b/ext/adaptivedemux2/dash/gstmpdclient.h
index 72746e52cfe9cea5659a7cfdd5b2c9ceda138bca..8074ebea7b472e16bb295ef0350330efcc8bf0ab 100644
--- a/ext/adaptivedemux2/dash/gstmpdclient.h
+++ b/ext/adaptivedemux2/dash/gstmpdclient.h
@@ -70,7 +70,7 @@ void gst_mpd_client2_fetch_on_load_external_resources (GstMPDClient2 * client);
 
 /* Streaming management */
 gboolean gst_mpd_client2_setup_media_presentation (GstMPDClient2 *client, GstClockTime time, gint period_index, const gchar *period_id);
-gboolean gst_mpd_client2_setup_streaming (GstMPDClient2 * client, GstMPDAdaptationSetNode * adapt_set);
+gboolean gst_mpd_client2_setup_streaming (GstMPDClient2 * client, GstMPDAdaptationSetNode * adapt_set, gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint max_video_framerate_n, gint max_video_framerate_d);
 gboolean gst_mpd_client2_setup_representation (GstMPDClient2 *client, GstActiveStream *stream, GstMPDRepresentationNode *representation);
 
 GstClockTime gst_mpd_client2_get_next_fragment_duration (GstMPDClient2 * client, GstActiveStream * stream);
diff --git a/ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.c b/ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.c
index 779ba274c8c8fffc85143773ab578e41b59147e9..2a9df03c966c8e02cfbd51523b99698a584ac781 100644
--- a/ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.c
+++ b/ext/adaptivedemux2/dash/gstmpdcontentcomponentnode.c
@@ -35,7 +35,7 @@ gst_mpd_content_component_node_finalize (GObject * object)
     xmlFree (self->lang);
   if (self->contentType)
     xmlFree (self->contentType);
-  g_slice_free (GstXMLRatio, self->par);
+  g_free (self->par);
   g_list_free_full (self->Accessibility,
       (GDestroyNotify) gst_mpd_descriptor_type_node_free);
   g_list_free_full (self->Role,
diff --git a/ext/adaptivedemux2/dash/gstmpdhelper.c b/ext/adaptivedemux2/dash/gstmpdhelper.c
index 85a300216cd1b36585a10ed7d2b63591f951cc8d..4ccdd343f5b596b9f0bb178bc943d8e7bc08534d 100644
--- a/ext/adaptivedemux2/dash/gstmpdhelper.c
+++ b/ext/adaptivedemux2/dash/gstmpdhelper.c
@@ -82,60 +82,6 @@ gst_mpd_helper_get_SAP_type (xmlNode * a_node,
   return exists;
 }
 
-const gchar *
-gst_mpd_helper_get_audio_codec_from_mime (GstCaps * caps)
-{
-  GstStructure *s;
-  const gchar *name = "";
-  const gchar *codec_name = NULL;
-
-  if (!caps)
-    return NULL;
-  s = gst_caps_get_structure (caps, 0);
-  if (!s)
-    goto done;
-  name = gst_structure_get_name (s);
-  if (!g_strcmp0 (name, "audio/mpeg")) {
-    gint mpeg_version;
-    if (gst_structure_get_int (s, "mpegversion", &mpeg_version)) {
-      if (mpeg_version == 4)
-        return "mp4a";
-    }
-
-  } else {
-    GST_DEBUG ("No codecs for this caps name %s", name);
-  }
-
-done:
-  return codec_name;
-}
-
-const gchar *
-gst_mpd_helper_get_video_codec_from_mime (GstCaps * caps)
-{
-  GstStructure *s;
-  const gchar *name = "";
-  const gchar *codec_name = NULL;
-
-  if (!caps)
-    return NULL;
-
-  s = gst_caps_get_structure (caps, 0);
-  if (!s)
-    goto done;
-  name = gst_structure_get_name (s);
-  if (!g_strcmp0 (name, "video/x-h264")) {
-    return "avc1";
-  } else if (!g_strcmp0 (name, "video/x-h265")) {
-    return "hvc1";
-  } else {
-    GST_DEBUG ("No codecs for this caps name %s", name);
-  }
-
-done:
-  return codec_name;
-}
-
 const gchar *
 gst_mpd_helper_mimetype_to_caps (const gchar * mimeType)
 {
diff --git a/ext/adaptivedemux2/dash/gstmpdhelper.h b/ext/adaptivedemux2/dash/gstmpdhelper.h
index 65a7e45bf95ff5cd715c9414dd231f493e627204..14025c7e01843a11a86db6cb3a466197809dd15b 100644
--- a/ext/adaptivedemux2/dash/gstmpdhelper.h
+++ b/ext/adaptivedemux2/dash/gstmpdhelper.h
@@ -63,8 +63,7 @@ gboolean gst_mpd_helper_get_SAP_type (xmlNode * a_node, const gchar * property_n
 
 const gchar * gst_mpd_helper_mimetype_to_caps (const gchar * mimeType);
 GstCaps *gst_mpd_helper_mimetype_to_codec_caps (const gchar * mimeType);
-const gchar * gst_mpd_helper_get_video_codec_from_mime (GstCaps * caps);
-const gchar * gst_mpd_helper_get_audio_codec_from_mime (GstCaps * caps);
+
 GstUri *gst_mpd_helper_combine_urls (GstUri * base, GList * list, gchar ** query, guint idx);
 int gst_mpd_helper_strncmp_ext (const char *s1, const char *s2);
 
diff --git a/ext/adaptivedemux2/dash/gstmpdparser.c b/ext/adaptivedemux2/dash/gstmpdparser.c
index 431b374d57f44b4b4630c2ef5b525a57ffd55e19..ce983e1c86142d90683b80faa9590f52c6d57122 100644
--- a/ext/adaptivedemux2/dash/gstmpdparser.c
+++ b/ext/adaptivedemux2/dash/gstmpdparser.c
@@ -289,7 +289,7 @@ gst_mpdparser_parse_seg_base_type_ext (GstMPDSegmentBaseNode ** pointer,
   }
   if (gst_xml_helper_get_prop_range (a_node, "indexRange", &rangeval)) {
     if (seg_base_type->indexRange) {
-      g_slice_free (GstXMLRange, seg_base_type->indexRange);
+      g_free (seg_base_type->indexRange);
     }
     seg_base_type->indexRange = rangeval;
   }
@@ -1362,7 +1362,7 @@ void
 gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
 {
   if (stream_period) {
-    g_slice_free (GstStreamPeriod, stream_period);
+    g_free (stream_period);
   }
 }
 
@@ -1370,7 +1370,7 @@ void
 gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
 {
   if (media_segment) {
-    g_slice_free (GstMediaSegment, media_segment);
+    g_free (media_segment);
   }
 }
 
@@ -1393,7 +1393,7 @@ gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
     active_stream->queryURL = NULL;
     if (active_stream->segments)
       g_ptr_array_unref (active_stream->segments);
-    g_slice_free (GstActiveStream, active_stream);
+    g_free (active_stream);
   }
 }
 
diff --git a/ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.c b/ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.c
index 1f9cb5e378ed18d97cf86ffa3f90993a7b8bba73..bde13bd0cbf9fe50bbc01babf0d21b4a17439e9a 100644
--- a/ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.c
+++ b/ext/adaptivedemux2/dash/gstmpdrepresentationbasenode.c
@@ -65,21 +65,21 @@ gst_mpd_representation_base_node_set_property (GObject * object, guint prop_id,
       self->height = g_value_get_uint (value);
       break;
     case PROP_MPD_REPRESENTATION_BASE_SAR:
-      g_slice_free (GstXMLRatio, self->sar);
+      g_free (self->sar);
       self->sar = gst_xml_helper_clone_ratio (g_value_get_pointer (value));
       break;
     case PROP_MPD_REPRESENTATION_BASE_MIN_FRAME_RATE:
-      g_slice_free (GstXMLFrameRate, self->minFrameRate);
+      g_free (self->minFrameRate);
       self->minFrameRate =
           gst_xml_helper_clone_frame_rate (g_value_get_pointer (value));
       break;
     case PROP_MPD_REPRESENTATION_BASE_MAX_FRAME_RATE:
-      g_slice_free (GstXMLFrameRate, self->maxFrameRate);
+      g_free (self->maxFrameRate);
       self->maxFrameRate =
           gst_xml_helper_clone_frame_rate (g_value_get_pointer (value));
       break;
     case PROP_MPD_REPRESENTATION_BASE_FRAME_RATE:
-      g_slice_free (GstXMLFrameRate, self->frameRate);
+      g_free (self->frameRate);
       self->frameRate =
           gst_xml_helper_clone_frame_rate (g_value_get_pointer (value));
       break;
@@ -192,10 +192,10 @@ gst_mpd_representation_base_node_finalize (GObject * object)
 
   if (self->profiles)
     xmlFree (self->profiles);
-  g_slice_free (GstXMLRatio, self->sar);
-  g_slice_free (GstXMLFrameRate, self->frameRate);
-  g_slice_free (GstXMLFrameRate, self->minFrameRate);
-  g_slice_free (GstXMLFrameRate, self->maxFrameRate);
+  g_free (self->sar);
+  g_free (self->frameRate);
+  g_free (self->minFrameRate);
+  g_free (self->maxFrameRate);
   if (self->audioSamplingRate)
     xmlFree (self->audioSamplingRate);
   if (self->mimeType)
diff --git a/ext/adaptivedemux2/dash/gstmpdsegmentbasenode.c b/ext/adaptivedemux2/dash/gstmpdsegmentbasenode.c
index ad2097290b745782c9fcf1a167768b13265d5564..4045cd2e9c6e1ca373afa4b197ec5078375d8aec 100644
--- a/ext/adaptivedemux2/dash/gstmpdsegmentbasenode.c
+++ b/ext/adaptivedemux2/dash/gstmpdsegmentbasenode.c
@@ -32,7 +32,7 @@ gst_mpd_segment_base_node_finalize (GObject * object)
   GstMPDSegmentBaseNode *self = GST_MPD_SEGMENT_BASE_NODE (object);
 
   if (self->indexRange)
-    g_slice_free (GstXMLRange, self->indexRange);
+    g_free (self->indexRange);
   gst_mpd_url_type_node_free (self->Initialization);
   gst_mpd_url_type_node_free (self->RepresentationIndex);
 
diff --git a/ext/adaptivedemux2/dash/gstmpdsegmenturlnode.c b/ext/adaptivedemux2/dash/gstmpdsegmenturlnode.c
index fec60329d706e8f564da2a30918a93c2b7a81cee..48940e22ec3d53a60a32f6a00f9ca367e1095a12 100644
--- a/ext/adaptivedemux2/dash/gstmpdsegmenturlnode.c
+++ b/ext/adaptivedemux2/dash/gstmpdsegmenturlnode.c
@@ -71,10 +71,10 @@ gst_mpd_segment_url_node_finalize (GObject * object)
 
   if (self->media)
     xmlFree (self->media);
-  g_slice_free (GstXMLRange, self->mediaRange);
+  g_free (self->mediaRange);
   if (self->index)
     xmlFree (self->index);
-  g_slice_free (GstXMLRange, self->indexRange);
+  g_free (self->indexRange);
 
   G_OBJECT_CLASS (gst_mpd_segment_url_node_parent_class)->finalize (object);
 }
diff --git a/ext/adaptivedemux2/dash/gstmpdurltypenode.c b/ext/adaptivedemux2/dash/gstmpdurltypenode.c
index fc85d489373d121ef55618a05ebf0fd7164ac7d6..70fdb9dfbf3c09518f29b63ba236f55aab34c1ac 100644
--- a/ext/adaptivedemux2/dash/gstmpdurltypenode.c
+++ b/ext/adaptivedemux2/dash/gstmpdurltypenode.c
@@ -32,7 +32,7 @@ gst_mpd_url_type_node_finalize (GObject * object)
 
   if (self->sourceURL)
     xmlFree (self->sourceURL);
-  g_slice_free (GstXMLRange, self->range);
+  g_free (self->range);
   g_free (self->node_name);
 
   G_OBJECT_CLASS (gst_mpd_url_type_node_parent_class)->finalize (object);
diff --git a/ext/adaptivedemux2/dash/gstxmlhelper.c b/ext/adaptivedemux2/dash/gstxmlhelper.c
index 084be27ba7d086eccd5dbebb0f97d0313a5ac3bd..16abcc8a27a0ff62e5e9a2078d6e2ea73b42929b 100644
--- a/ext/adaptivedemux2/dash/gstxmlhelper.c
+++ b/ext/adaptivedemux2/dash/gstxmlhelper.c
@@ -287,7 +287,7 @@ gst_xml_helper_clone_range (GstXMLRange * range)
   GstXMLRange *clone = NULL;
 
   if (range) {
-    clone = g_slice_new0 (GstXMLRange);
+    clone = g_new0 (GstXMLRange, 1);
     clone->first_byte_pos = range->first_byte_pos;
     clone->last_byte_pos = range->last_byte_pos;
   }
@@ -301,7 +301,7 @@ gst_xml_helper_clone_ratio (GstXMLRatio * ratio)
   GstXMLRatio *clone = NULL;
 
   if (ratio) {
-    clone = g_slice_new0 (GstXMLRatio);
+    clone = g_new0 (GstXMLRatio, 1);
     clone->num = ratio->num;
     clone->den = ratio->den;
   }
@@ -315,7 +315,7 @@ gst_xml_helper_clone_frame_rate (GstXMLFrameRate * frameRate)
   GstXMLFrameRate *clone = NULL;
 
   if (frameRate) {
-    clone = g_slice_new0 (GstXMLFrameRate);
+    clone = g_new0 (GstXMLFrameRate, 1);
     clone->num = frameRate->num;
     clone->den = frameRate->den;
   }
@@ -628,7 +628,7 @@ gst_xml_helper_get_prop_range (xmlNode * a_node,
       goto error;
     }
     /* malloc return data structure */
-    *property_value = g_slice_new0 (GstXMLRange);
+    *property_value = g_new0 (GstXMLRange, 1);
     exists = TRUE;
     (*property_value)->first_byte_pos = first_byte_pos;
     (*property_value)->last_byte_pos = last_byte_pos;
@@ -685,7 +685,7 @@ gst_xml_helper_get_prop_ratio (xmlNode * a_node,
       }
     }
     /* malloc return data structure */
-    *property_value = g_slice_new0 (GstXMLRatio);
+    *property_value = g_new0 (GstXMLRatio, 1);
     exists = TRUE;
     (*property_value)->num = num;
     (*property_value)->den = den;
@@ -738,7 +738,7 @@ gst_xml_helper_get_prop_framerate (xmlNode * a_node,
       }
     }
     /* alloc return data structure */
-    *property_value = g_slice_new0 (GstXMLFrameRate);
+    *property_value = g_new0 (GstXMLFrameRate, 1);
     exists = TRUE;
     (*property_value)->num = num;
     (*property_value)->den = den;
@@ -786,7 +786,7 @@ gst_xml_helper_get_prop_cond_uint (xmlNode * a_node,
     }
 
     /* alloc return data structure */
-    *property_value = g_slice_new0 (GstXMLConditionalUintType);
+    *property_value = g_new0 (GstXMLConditionalUintType, 1);
     exists = TRUE;
     (*property_value)->flag = flag;
     (*property_value)->value = val;
diff --git a/ext/adaptivedemux2/downloadhelper.c b/ext/adaptivedemux2/downloadhelper.c
index bd6aa15885f408569c963ccc7caedfc491bf54bd..ebf9dc0783c7897b28195d9a70af1214ab17ee9b 100644
--- a/ext/adaptivedemux2/downloadhelper.c
+++ b/ext/adaptivedemux2/downloadhelper.c
@@ -49,7 +49,7 @@ struct DownloadHelper
 
   gchar *referer;
   gchar *user_agent;
-  gchar **cookies;
+  GSList *cookies;
 };
 
 struct DownloadHelperTransfer
@@ -99,7 +99,7 @@ transfer_completion_cb (gpointer src_object, GAsyncResult * res,
   DownloadRequest *request = transfer->request;
 
   if (transfer->blocking)
-    return;                     /* Somehow a completion got signalled for a blocking request */
+    return;
 
   download_request_lock (request);
   request->in_use = FALSE;
@@ -201,7 +201,8 @@ finish_transfer_task (DownloadHelper * dh, GTask * transfer_task,
 
       if (transfer->blocking)
         g_cond_broadcast (&transfer->cond);
-      else if (error != NULL)
+
+      if (error != NULL)
         g_task_return_error (transfer_task, error);
       else
         g_task_return_boolean (transfer_task, TRUE);
@@ -267,7 +268,7 @@ on_read_ready (GObject * source, GAsyncResult * result, gpointer user_data)
 
     if (!g_cancellable_is_cancelled (transfer->cancellable)) {
       GST_ERROR ("Failed to read stream: %s", error->message);
-      if (request->state != DOWNLOAD_REQUEST_STATE_UNSENT)
+      if (request->state != DOWNLOAD_REQUEST_STATE_CANCELLED)
         request->state = DOWNLOAD_REQUEST_STATE_ERROR;
       finish_transfer_task (dh, transfer_task, error);
     } else {
@@ -309,19 +310,19 @@ on_read_ready (GObject * source, GAsyncResult * result, gpointer user_data)
       }
     }
 
-    if (request->download_start_time == GST_CLOCK_TIME_NONE) {
-      GST_LOG ("Got first data for URI %s", request->uri);
-      request->download_start_time = now;
-    }
-
     if (gst_buffer != NULL) {
-      /* Unsent means cancellation is in progress, so don't override
-       * the state. Otherwise make sure it is LOADING */
-      if (request->state != DOWNLOAD_REQUEST_STATE_UNSENT)
+      /* Don't override CANCELLED state. Otherwise make sure it is LOADING */
+      if (request->state != DOWNLOAD_REQUEST_STATE_CANCELLED)
         request->state = DOWNLOAD_REQUEST_STATE_LOADING;
 
-      GST_LOG ("Adding %u bytes to buffer",
-          (guint) (gst_buffer_get_size (gst_buffer)));
+      if (request->download_start_time == GST_CLOCK_TIME_NONE) {
+        GST_LOG ("Got first data for URI %s", request->uri);
+        request->download_start_time = now;
+      }
+      request->download_newest_data_time = now;
+
+      GST_LOG ("Adding %u bytes to buffer (request URI %s)",
+          (guint) (gst_buffer_get_size (gst_buffer)), request->uri);
 
       download_request_add_buffer (request, gst_buffer);
 
@@ -348,17 +349,20 @@ on_read_ready (GObject * source, GAsyncResult * result, gpointer user_data)
 finish_transfer:
   if (request->in_use && !g_cancellable_is_cancelled (transfer->cancellable)) {
     SoupStatus status_code = _soup_message_get_status (transfer->msg);
+#ifndef GST_DISABLE_GST_DEBUG
+    guint download_ms = (now - request->download_request_time) / GST_MSECOND;
+    GST_LOG ("request complete in %u ms. Code %d URI %s range %" G_GINT64_FORMAT
+        " %" G_GINT64_FORMAT, download_ms, status_code,
+        request->uri, request->range_start, request->range_end);
+#endif
 
-    GST_LOG ("request complete. Code %d URI %s range %" G_GINT64_FORMAT " %"
-        G_GINT64_FORMAT, status_code, request->uri,
-        request->range_start, request->range_end);
-
-    if (request->state != DOWNLOAD_REQUEST_STATE_UNSENT) {
+    if (request->state != DOWNLOAD_REQUEST_STATE_CANCELLED) {
       if (SOUP_STATUS_IS_SUCCESSFUL (status_code)
-          || SOUP_STATUS_IS_REDIRECTION (status_code))
+          || SOUP_STATUS_IS_REDIRECTION (status_code)) {
         request->state = DOWNLOAD_REQUEST_STATE_COMPLETE;
-      else
+      } else {
         request->state = DOWNLOAD_REQUEST_STATE_ERROR;
+      }
     }
   }
   request->download_end_time = now;
@@ -528,7 +532,7 @@ on_request_sent (GObject * source, GAsyncResult * result, gpointer user_data)
           G_GINT64_FORMAT, request->status_code, request->uri,
           request->range_start, request->range_end);
 
-      if (request->state != DOWNLOAD_REQUEST_STATE_UNSENT)
+      if (request->state != DOWNLOAD_REQUEST_STATE_CANCELLED)
         request->state = DOWNLOAD_REQUEST_STATE_ERROR;
       finish_transfer_task (dh, transfer_task, error);
     } else {
@@ -543,8 +547,8 @@ on_request_sent (GObject * source, GAsyncResult * result, gpointer user_data)
     return;
   }
 
-  /* If the state went back to UNSENT, we were cancelled so don't override it */
-  if (request->state != DOWNLOAD_REQUEST_STATE_UNSENT &&
+  /* If the state is cancelled don't override it */
+  if (request->state != DOWNLOAD_REQUEST_STATE_CANCELLED &&
       request->state != DOWNLOAD_REQUEST_STATE_HEADERS_RECEIVED) {
 
     request->state = DOWNLOAD_REQUEST_STATE_HEADERS_RECEIVED;
@@ -585,7 +589,9 @@ finish_transfer_error:
     GST_LOG ("request complete. Code %d URI %s range %" G_GINT64_FORMAT " %"
         G_GINT64_FORMAT, _soup_message_get_status (msg), request->uri,
         request->range_start, request->range_end);
-    if (request->state != DOWNLOAD_REQUEST_STATE_UNSENT)
+
+    /* If the state is cancelled don't override it */
+    if (request->state != DOWNLOAD_REQUEST_STATE_CANCELLED)
       request->state = DOWNLOAD_REQUEST_STATE_ERROR;
   }
 
@@ -648,7 +654,7 @@ downloadhelper_free (DownloadHelper * dh)
 
   g_free (dh->referer);
   g_free (dh->user_agent);
-  g_strfreev (dh->cookies);
+  _soup_cookies_free (dh->cookies);
 
   g_free (dh);
 }
@@ -675,10 +681,25 @@ downloadhelper_set_user_agent (DownloadHelper * dh, const gchar * user_agent)
 void
 downloadhelper_set_cookies (DownloadHelper * dh, gchar ** cookies)
 {
+  guint i;
   g_mutex_lock (&dh->transfer_lock);
-  g_strfreev (dh->cookies);
-  dh->cookies = cookies;
+  _soup_cookies_free (dh->cookies);
+  dh->cookies = NULL;
+
+  for (i = 0; cookies[i]; i++) {
+    SoupCookie *cookie = _soup_cookie_parse (cookies[i]);
+
+    if (cookie == NULL) {
+      GST_WARNING ("Couldn't parse cookie, ignoring: %s", cookies[i]);
+      continue;
+    }
+
+    dh->cookies = g_slist_append (dh->cookies, cookie);
+  }
+
   g_mutex_unlock (&dh->transfer_lock);
+
+  g_strfreev (cookies);
 }
 
 /* Called with the transfer lock held */
@@ -689,6 +710,13 @@ submit_transfer (DownloadHelper * dh, GTask * transfer_task)
   DownloadRequest *request = transfer->request;
 
   download_request_lock (request);
+  if (request->state == DOWNLOAD_REQUEST_STATE_CANCELLED) {
+    download_request_unlock (request);
+
+    GST_DEBUG ("Don't submit already cancelled transfer");
+    return;
+  }
+
   request->state = DOWNLOAD_REQUEST_STATE_OPEN;
   request->download_request_time =
       gst_adaptive_demux_clock_get_time (dh->clock);
@@ -799,15 +827,14 @@ downloadhelper_stop (DownloadHelper * dh)
     DownloadRequest *request = transfer->request;
 
     download_request_lock (request);
-    /* Reset the state to UNSENT, to indicate cancellation, like an XMLHttpRequest does */
-    request->state = DOWNLOAD_REQUEST_STATE_UNSENT;
+    request->state = DOWNLOAD_REQUEST_STATE_CANCELLED;
     download_request_unlock (request);
 
     transfer->complete = TRUE;
     if (transfer->blocking)
       g_cond_broadcast (&transfer->cond);
-    else
-      g_task_return_boolean (transfer_task, TRUE);
+
+    g_task_return_boolean (transfer_task, TRUE);
   }
 
   g_array_set_size (dh->active_transfers, 0);
@@ -890,11 +917,7 @@ downloadhelper_submit_request (DownloadHelper * dh,
   }
 
   if (dh->cookies != NULL) {
-    gchar **cookie;
-
-    for (cookie = dh->cookies; *cookie != NULL; cookie++) {
-      _soup_message_headers_append (msg_headers, "Cookie", *cookie);
-    }
+    _soup_cookies_to_request (dh->cookies, msg);
   }
 
   transfer_task = transfer_task_new (dh, request, msg, blocking);
@@ -966,8 +989,7 @@ downloadhelper_cancel_request (DownloadHelper * dh, DownloadRequest * request)
   GST_DEBUG ("Cancelling request for URI %s range %" G_GINT64_FORMAT " %"
       G_GINT64_FORMAT, request->uri, request->range_start, request->range_end);
 
-  request->state = DOWNLOAD_REQUEST_STATE_UNSENT;
-
+  request->state = DOWNLOAD_REQUEST_STATE_CANCELLED;
   for (i = dh->active_transfers->len - 1; i >= 0; i--) {
     GTask *transfer_task = g_array_index (dh->active_transfers, GTask *, i);
     DownloadHelperTransfer *transfer = g_task_get_task_data (transfer_task);
diff --git a/ext/adaptivedemux2/downloadhelper.h b/ext/adaptivedemux2/downloadhelper.h
index a5abdeba10856553503e864a5a754cda35f8e8b2..563e4ff9e48df0a9e10c20682fe902d4415ecff1 100644
--- a/ext/adaptivedemux2/downloadhelper.h
+++ b/ext/adaptivedemux2/downloadhelper.h
@@ -32,6 +32,9 @@ typedef enum DownloadFlags DownloadFlags;
 
 #define HTTP_STATUS_IS_SUCCESSFUL(s) ((s) >= 200 && (s) < 300)
 
+/* RFC8673 recommended last-byte-pos value of 2^^53-1 */
+#define RFC8673_LAST_BYTE_POS (9007199254740991)
+
 enum DownloadFlags
 {
   DOWNLOAD_FLAG_NONE = 0,
diff --git a/ext/adaptivedemux2/downloadrequest.c b/ext/adaptivedemux2/downloadrequest.c
index a40878c2ec727ef1f0bb32719925d17d9b2622df..37c5acd9c8e08a4931a09ec099e2c964017c50bc 100644
--- a/ext/adaptivedemux2/downloadrequest.c
+++ b/ext/adaptivedemux2/downloadrequest.c
@@ -27,6 +27,7 @@
 #include <gst/base/gsttypefindhelper.h>
 #include <gst/base/gstadapter.h>
 #include "downloadrequest.h"
+#include <stdlib.h>
 
 typedef struct _DownloadRequestPrivate DownloadRequestPrivate;
 
@@ -53,7 +54,7 @@ DownloadRequest *
 download_request_new (void)
 {
   DownloadRequest *request =
-      (DownloadRequest *) g_slice_new0 (DownloadRequestPrivate);
+      (DownloadRequest *) g_new0 (DownloadRequestPrivate, 1);
   DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
 
   g_atomic_int_set (&request->ref_count, 1);
@@ -122,7 +123,7 @@ download_request_free (DownloadRequest * request)
 
   g_rec_mutex_clear (&priv->lock);
 
-  g_slice_free1 (sizeof (DownloadRequestPrivate), priv);
+  g_free (priv);
 }
 
 void
@@ -161,6 +162,7 @@ download_request_despatch_completion (DownloadRequest * request)
   DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
   switch (request->state) {
     case DOWNLOAD_REQUEST_STATE_UNSENT:
+    case DOWNLOAD_REQUEST_STATE_CANCELLED:
       if (priv->cancellation_cb)
         priv->cancellation_cb (request, request->state, priv->cb_data);
       break;
@@ -235,6 +237,115 @@ download_request_take_buffer (DownloadRequest * request)
   return buffer;
 }
 
+/* Extract the byte range of the download, matching the
+ * requested range against the GST_BUFFER_OFFSET() values of the
+ * data buffer, which tracks the byte position in the
+ * original resource */
+GstBuffer *
+download_request_take_buffer_range (DownloadRequest * request,
+    gint64 target_range_start, gint64 target_range_end)
+{
+  DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
+  GstBuffer *buffer = NULL;
+  GstBuffer *input_buffer = NULL;
+
+  g_return_val_if_fail (request != NULL, NULL);
+
+  g_rec_mutex_lock (&priv->lock);
+
+  if (request->state != DOWNLOAD_REQUEST_STATE_LOADING
+      && request->state != DOWNLOAD_REQUEST_STATE_COMPLETE) {
+    g_rec_mutex_unlock (&priv->lock);
+    return NULL;
+  }
+
+  input_buffer = priv->buffer;
+  priv->buffer = NULL;
+  if (input_buffer == NULL)
+    goto out;
+
+  /* Figure out how much of the available data (if any) belongs to
+   * the target range */
+  gint64 avail_start = GST_BUFFER_OFFSET (input_buffer);
+  gint64 avail_end = avail_start + gst_buffer_get_size (input_buffer) - 1;
+
+  target_range_start = MAX (avail_start, target_range_start);
+
+  if (target_range_start <= avail_end) {
+    /* There's at least 1 byte available that belongs to this target request, but
+     * does this buffer need splitting in two? */
+    if (target_range_end != -1 && target_range_end < avail_end) {
+      /* Yes, it does. Drop the front of the buffer if needed and take the piece we want */
+      guint64 start_offset = target_range_start - avail_start;
+
+      buffer =
+          gst_buffer_copy_region (input_buffer, GST_BUFFER_COPY_MEMORY,
+          start_offset, target_range_end - avail_start);
+      GST_BUFFER_OFFSET (buffer) =
+          GST_BUFFER_OFFSET (input_buffer) + start_offset;
+
+      /* Put the rest of the buffer back */
+      priv->buffer =
+          gst_buffer_copy_region (input_buffer, GST_BUFFER_COPY_MEMORY,
+          target_range_end - avail_start, -1);
+
+      /* Release the original buffer. The sub-buffers are holding their own refs as needed */
+      gst_buffer_unref (input_buffer);
+    } else if (target_range_start != avail_start) {
+      /* We want to the end of the buffer, but need to drop a piece at the front */
+      guint64 start_offset = target_range_start - avail_start;
+
+      buffer =
+          gst_buffer_copy_region (input_buffer, GST_BUFFER_COPY_MEMORY,
+          start_offset, -1);
+      GST_BUFFER_OFFSET (buffer) =
+          GST_BUFFER_OFFSET (input_buffer) + start_offset;
+
+      /* Release the original buffer. The sub-buffer is holding its own ref as needed */
+      gst_buffer_unref (input_buffer);
+    } else {
+      /* No, return the entire buffer as-is */
+      buffer = input_buffer;
+    }
+  }
+
+out:
+  g_rec_mutex_unlock (&priv->lock);
+  return buffer;
+}
+
+guint64
+download_request_get_bytes_available (DownloadRequest * request)
+{
+  DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
+  guint64 ret = 0;
+
+  g_rec_mutex_lock (&priv->lock);
+
+  if (priv->buffer != NULL)
+    ret = gst_buffer_get_size (priv->buffer);
+
+  g_rec_mutex_unlock (&priv->lock);
+
+  return ret;
+}
+
+guint64
+download_request_get_cur_offset (DownloadRequest * request)
+{
+  DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
+  guint64 ret = GST_BUFFER_OFFSET_NONE;
+
+  g_rec_mutex_lock (&priv->lock);
+
+  if (priv->buffer != NULL)
+    ret = GST_BUFFER_OFFSET (priv->buffer);
+
+  g_rec_mutex_unlock (&priv->lock);
+
+  return ret;
+}
+
 void
 download_request_set_uri (DownloadRequest * request, const gchar * uri,
     gint64 range_start, gint64 range_end)
@@ -367,6 +478,54 @@ download_request_get_caps (DownloadRequest * request)
   return caps;
 }
 
+static GstClockTime
+_get_age_header (GstStructure * headers)
+{
+  const GstStructure *response_headers;
+  const gchar *http_age;
+  const GValue *val;
+
+  val = gst_structure_get_value (headers, "response-headers");
+  if (!val) {
+    return 0;
+  }
+
+  response_headers = gst_value_get_structure (val);
+  http_age = gst_structure_get_string (response_headers, "Date");
+  if (!http_age) {
+    return 0;
+  }
+
+  return atoi (http_age) * GST_SECOND;
+}
+
+/* Return the age of the download from the Age header,
+ * or 0 if there was none */
+GstClockTime
+download_request_get_age (DownloadRequest * request)
+{
+  DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
+  GstClockTime age = 0;
+
+  g_return_val_if_fail (request != NULL, age);
+
+  if (request->state != DOWNLOAD_REQUEST_STATE_LOADING
+      && request->state != DOWNLOAD_REQUEST_STATE_COMPLETE)
+    return age;
+
+  g_rec_mutex_lock (&priv->lock);
+
+  if (request->headers != NULL) {
+    /* We have headers for the download, see if there was an Age
+     * header in the response */
+    GstClockTime age = _get_age_header (request->headers);
+    GST_LOG ("Got cached data with age %" GST_TIMEP_FORMAT, &age);
+  }
+  g_rec_mutex_unlock (&priv->lock);
+
+  return age;
+}
+
 void
 download_request_add_buffer (DownloadRequest * request, GstBuffer * buffer)
 {
diff --git a/ext/adaptivedemux2/downloadrequest.h b/ext/adaptivedemux2/downloadrequest.h
index b814d921b32bb9dfc55510afec5be5f83d993821..b8dea7466bbf3241d181bf8e0d3b7846ea4ad81a 100644
--- a/ext/adaptivedemux2/downloadrequest.h
+++ b/ext/adaptivedemux2/downloadrequest.h
@@ -41,6 +41,7 @@ enum _DownloadRequestState {
   DOWNLOAD_REQUEST_STATE_LOADING,  /* Content loading in progress */
   DOWNLOAD_REQUEST_STATE_COMPLETE, /* Request processing finished successfully - check status_code for completion 200-399 codes */
   DOWNLOAD_REQUEST_STATE_ERROR,    /* Request generated an http error - check status_code */
+  DOWNLOAD_REQUEST_STATE_CANCELLED, /* Request has been cancelled by the user */
 };
 
 struct _DownloadRequest
@@ -68,6 +69,7 @@ struct _DownloadRequest
 
   guint64 download_request_time;  /* Epoch time when the download started */
   guint64 download_start_time;    /* Epoch time when the first data for the download arrived */
+  guint64 download_newest_data_time; /* Epoch time when the most recent data for the download arrived */
   guint64 download_end_time;      /* Epoch time when the download finished */
 };
 
@@ -83,8 +85,13 @@ void download_request_set_caps (DownloadRequest * request, GstCaps * caps);
 
 GstCaps * download_request_get_caps (DownloadRequest * request);
 
+GstClockTime download_request_get_age (DownloadRequest *request);
+
 void download_request_add_buffer (DownloadRequest *request, GstBuffer *buffer);
 GstBuffer * download_request_take_buffer (DownloadRequest *request);
+GstBuffer * download_request_take_buffer_range (DownloadRequest *request, gint64 range_start, gint64 range_end);
+guint64 download_request_get_bytes_available (DownloadRequest *request);
+guint64 download_request_get_cur_offset (DownloadRequest *request);
 
 DownloadRequest * download_request_new (void);
 DownloadRequest * download_request_new_uri (const gchar * uri);
diff --git a/ext/adaptivedemux2/gstadaptivedemux-period.c b/ext/adaptivedemux2/gstadaptivedemux-period.c
index 35925cfccc7022c84aba775552d948e90e6e34d1..429b9ece83d8ad6feafe0f4e23f7685e3cd68079 100644
--- a/ext/adaptivedemux2/gstadaptivedemux-period.c
+++ b/ext/adaptivedemux2/gstadaptivedemux-period.c
@@ -212,6 +212,33 @@ gst_adaptive_demux_period_transfer_selection (GstAdaptiveDemux * demux,
   }
 }
 
+/* called with TRACKS_LOCK taken. Takes ownership of the stream */
+gboolean
+gst_adaptive_demux_period_add_stream (GstAdaptiveDemuxPeriod * period,
+    GstAdaptiveDemux2Stream * stream)
+{
+  GST_LOG ("period %d stream: %" GST_PTR_FORMAT, period->period_num, stream);
+
+  /* Set the stream's period */
+  stream->period = period;
+  period->streams = g_list_append (period->streams, stream);
+
+  /* Add any pre-existing stream tracks to our set */
+  if (stream->tracks) {
+    GList *iter;
+    for (iter = stream->tracks; iter; iter = iter->next) {
+      GstAdaptiveDemuxTrack *track = (GstAdaptiveDemuxTrack *) iter->data;
+      if (!gst_adaptive_demux_period_add_track (period, track)) {
+        GST_ERROR_OBJECT (period->demux, "period %d failed to add track %p",
+            period->period_num, track);
+        return FALSE;
+      }
+    }
+  }
+
+  return TRUE;
+}
+
 /* called with TRACKS_LOCK taken */
 gboolean
 gst_adaptive_demux_period_add_track (GstAdaptiveDemuxPeriod * period,
diff --git a/ext/adaptivedemux2/gstadaptivedemux-private.h b/ext/adaptivedemux2/gstadaptivedemux-private.h
index a72cefe3af2e3c70eb1b846c81bdc3f7c136b67f..dd433a9775551787bf9c4e658bce54c5a5dfdbaa 100644
--- a/ext/adaptivedemux2/gstadaptivedemux-private.h
+++ b/ext/adaptivedemux2/gstadaptivedemux-private.h
@@ -83,6 +83,8 @@ struct _GstAdaptiveDemuxPrivate
 
   /* Callback / timer id for the next manifest update */
   guint manifest_updates_cb;
+  gboolean manifest_updates_enabled;
+  gboolean need_manual_manifest_update;
 
   /* Count of failed manifest updates */
   gint update_failed_count;
@@ -100,7 +102,10 @@ struct _GstAdaptiveDemuxPrivate
   /* Set to TRUE if any stream is waiting on the manifest update */
   gboolean stream_waiting_for_manifest;
 
-  GMutex api_lock;
+  /* Set to TRUE if streams can download fragment data. If FALSE,
+   * they can load playlists / prepare for updata_fragment_info()
+   */
+  gboolean streams_can_download_fragments;
 
   /* Protects demux and stream segment information
    * Needed because seeks can update segment information
@@ -140,7 +145,7 @@ struct _GstAdaptiveDemuxPrivate
   /* Current output selection seqnum */
   guint32 current_selection_seqnum;
   /* Current output position (in running time) */
-  GstClockTimeDiff global_output_position;
+  GstClockTime global_output_position;
   /* End of fields protected by output_lock */
 
   gint n_audio_streams, n_video_streams, n_subtitle_streams;
@@ -181,6 +186,7 @@ gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux);
 
 void gst_adaptive_demux2_stream_on_manifest_update (GstAdaptiveDemux2Stream * stream);
 void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream);
+void gst_adaptive_demux2_stream_on_can_download_fragments(GstAdaptiveDemux2Stream *stream);
 
 gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream);
 GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux2Stream * stream,
@@ -191,6 +197,7 @@ gboolean gst_adaptive_demux_get_live_seek_range (GstAdaptiveDemux * demux,
 gboolean gst_adaptive_demux2_stream_in_live_seek_range (GstAdaptiveDemux * demux,
     GstAdaptiveDemux2Stream * stream);
 gboolean gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream *stream);
+gboolean gst_adaptive_demux2_stream_is_default_locked (GstAdaptiveDemux2Stream *stream);
 
 gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux);
 void gst_adaptive_demux_advance_period (GstAdaptiveDemux * demux);
@@ -212,10 +219,7 @@ typedef struct
   GstClockTimeDiff runningtime_buffering;
 } TrackQueueItem;
 
-GstAdaptiveDemux2Stream *find_stream_for_track_locked (GstAdaptiveDemux *
-    demux, GstAdaptiveDemuxTrack * track);
-
-GstMiniObject * track_dequeue_data_locked (GstAdaptiveDemux * demux, GstAdaptiveDemuxTrack * track, gboolean check_sticky_events);
+GstMiniObject * gst_adaptive_demux_track_dequeue_data_locked (GstAdaptiveDemux * demux, GstAdaptiveDemuxTrack * track, gboolean check_sticky_events);
 void gst_adaptive_demux_track_flush (GstAdaptiveDemuxTrack * track);
 void gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track, GstClockTime drain_running_time);
 void gst_adaptive_demux_track_update_next_position (GstAdaptiveDemuxTrack * track);
@@ -227,6 +231,8 @@ GstAdaptiveDemuxPeriod * gst_adaptive_demux_period_new (GstAdaptiveDemux * demux
 GstAdaptiveDemuxPeriod * gst_adaptive_demux_period_ref (GstAdaptiveDemuxPeriod * period);
 void                     gst_adaptive_demux_period_unref (GstAdaptiveDemuxPeriod * period);
 
+gboolean                 gst_adaptive_demux_period_add_stream (GstAdaptiveDemuxPeriod * period,
+							      GstAdaptiveDemux2Stream * stream);
 gboolean                 gst_adaptive_demux_period_add_track (GstAdaptiveDemuxPeriod * period,
 							      GstAdaptiveDemuxTrack * track);
 gboolean                 gst_adaptive_demux_track_add_elements (GstAdaptiveDemuxTrack * track,
diff --git a/ext/adaptivedemux2/gstadaptivedemux-stream.c b/ext/adaptivedemux2/gstadaptivedemux-stream.c
index a7949bef3a972a48041346268b070ecf44503538..95453e815179939e7633921f4d18bc50b3d29254 100644
--- a/ext/adaptivedemux2/gstadaptivedemux-stream.c
+++ b/ext/adaptivedemux2/gstadaptivedemux-stream.c
@@ -50,6 +50,13 @@ gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream *
     stream);
 static void gst_adaptive_demux2_stream_update_track_ids (GstAdaptiveDemux2Stream
     * stream);
+static GstFlowReturn
+gst_adaptive_demux2_stream_submit_request_default (GstAdaptiveDemux2Stream *
+    stream, DownloadRequest * download_req);
+static void
+gst_adaptive_demux2_stream_start_default (GstAdaptiveDemux2Stream * stream);
+static void
+gst_adaptive_demux2_stream_stop_default (GstAdaptiveDemux2Stream * stream);
 
 #define gst_adaptive_demux2_stream_parent_class parent_class
 G_DEFINE_ABSTRACT_TYPE (GstAdaptiveDemux2Stream, gst_adaptive_demux2_stream,
@@ -62,8 +69,11 @@ gst_adaptive_demux2_stream_class_init (GstAdaptiveDemux2StreamClass * klass)
 
   gobject_class->finalize = gst_adaptive_demux2_stream_finalize;
 
+  klass->start = gst_adaptive_demux2_stream_start_default;
+  klass->stop = gst_adaptive_demux2_stream_stop_default;
   klass->data_received = gst_adaptive_demux2_stream_data_received_default;
   klass->finish_fragment = gst_adaptive_demux2_stream_finish_fragment_default;
+  klass->submit_request = gst_adaptive_demux2_stream_submit_request_default;
 }
 
 static GType tsdemux_type = 0;
@@ -76,11 +86,16 @@ gst_adaptive_demux2_stream_init (GstAdaptiveDemux2Stream * stream)
   stream->last_ret = GST_FLOW_OK;
   stream->next_input_wakeup_time = GST_CLOCK_STIME_NONE;
 
+  stream->recommended_buffering_threshold = GST_CLOCK_TIME_NONE;
+
   stream->fragment_bitrates =
       g_malloc0 (sizeof (guint64) * NUM_LOOKBACK_FRAGMENTS);
 
   stream->start_position = stream->current_position = GST_CLOCK_TIME_NONE;
 
+  g_mutex_init (&stream->prepare_lock);
+  g_cond_init (&stream->prepare_cond);
+
   gst_segment_init (&stream->parse_segment, GST_FORMAT_TIME);
 }
 
@@ -136,7 +151,10 @@ gst_adaptive_demux2_stream_finalize (GObject * object)
     gst_caps_unref (stream->pending_caps);
 
   gst_clear_tag_list (&stream->pending_tags);
-  g_clear_pointer (&stream->stream_collection, gst_object_unref);
+  gst_clear_object (&stream->stream_collection);
+
+  g_mutex_clear (&stream->prepare_lock);
+  g_cond_clear (&stream->prepare_cond);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -155,10 +173,9 @@ gst_adaptive_demux2_stream_add_track (GstAdaptiveDemux2Stream * stream,
 {
   g_return_val_if_fail (track != NULL, FALSE);
 
-  GST_DEBUG_OBJECT (stream->demux, "stream:%p track:%s", stream,
-      track->stream_id);
+  GST_DEBUG_OBJECT (stream, "track:%s", track->stream_id);
   if (g_list_find (stream->tracks, track)) {
-    GST_DEBUG_OBJECT (stream->demux,
+    GST_DEBUG_OBJECT (stream,
         "track '%s' already handled by this stream", track->stream_id);
     return FALSE;
   }
@@ -193,9 +210,8 @@ static void
 gst_adaptive_demux2_stream_handle_playlist_eos (GstAdaptiveDemux2Stream *
     stream);
 static GstFlowReturn
-gst_adaptive_demux2_stream_begin_download_uri (GstAdaptiveDemux * demux,
-    GstAdaptiveDemux2Stream * stream, const gchar * uri, gint64 start,
-    gint64 end);
+gst_adaptive_demux2_stream_begin_download_uri (GstAdaptiveDemux2Stream * stream,
+    const gchar * uri, gint64 start, gint64 end);
 
 #ifndef GST_DISABLE_GST_DEBUG
 static const char *
@@ -213,7 +229,6 @@ uritype (GstAdaptiveDemux2Stream * s)
 static gboolean
 schedule_another_chunk (GstAdaptiveDemux2Stream * stream)
 {
-  GstAdaptiveDemux *demux = stream->demux;
   DownloadRequest *request = stream->download_request;
   GstFlowReturn ret;
 
@@ -256,7 +271,7 @@ schedule_another_chunk (GstAdaptiveDemux2Stream * stream)
       " chunk_size %" G_GINT64_FORMAT, uri, range_start, chunk_end, chunk_size);
 
   ret =
-      gst_adaptive_demux2_stream_begin_download_uri (demux, stream, uri,
+      gst_adaptive_demux2_stream_begin_download_uri (stream, uri,
       range_start, chunk_end);
   if (ret != GST_FLOW_OK) {
     GST_DEBUG_OBJECT (stream,
@@ -633,15 +648,15 @@ gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
       if (demux->have_group_id)
         gst_event_set_group_id (stream_start, demux->group_id);
     }
+    stream->first_fragment_buffer = FALSE;
+
+    if (stream->discont) {
+      discont = TRUE;
+      stream->discont = FALSE;
+    }
   } else {
     GST_BUFFER_PTS (buffer) = GST_CLOCK_TIME_NONE;
   }
-  stream->first_fragment_buffer = FALSE;
-
-  if (stream->discont) {
-    discont = TRUE;
-    stream->discont = FALSE;
-  }
 
   if (discont) {
     GST_DEBUG_OBJECT (stream, "Marking fragment as discontinuous");
@@ -1186,60 +1201,57 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
   stream->download_active = FALSE;
   stream->last_status_code = last_status_code;
 
+  live = gst_adaptive_demux_is_live (demux);
+
   GST_DEBUG_OBJECT (stream,
-      "Download finished with error, request state %d http status %u, dc %d",
-      request->state, last_status_code, stream->download_error_count);
+      "Download finished with error, request state %d http status %u, dc %d "
+      "live %d retried %d",
+      request->state, last_status_code,
+      stream->download_error_count, live, stream->download_error_retry);
 
-  live = gst_adaptive_demux_is_live (demux);
-  if (((last_status_code / 100 == 4 && live)
+  if (!stream->download_error_retry && ((last_status_code / 100 == 4 && live)
           || last_status_code / 100 == 5)) {
     /* 4xx/5xx */
     /* if current position is before available start, switch to next */
-    if (!gst_adaptive_demux2_stream_has_next_fragment (stream))
-      goto flushing;
-
     if (live) {
       gint64 range_start, range_stop;
 
-      if (!gst_adaptive_demux_get_live_seek_range (demux, &range_start,
-              &range_stop))
-        goto flushing;
-
-      if (demux->segment.position < range_start) {
-        GstFlowReturn ret;
-
-        GST_DEBUG_OBJECT (stream, "Retrying once with next segment");
-        gst_adaptive_demux2_stream_finish_download (stream, GST_FLOW_EOS, NULL);
-
-        GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
-
-        ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
-        GST_DEBUG_OBJECT (stream, "update_fragment_info ret: %s",
-            gst_flow_get_name (ret));
-
-        if (ret == GST_FLOW_OK)
-          goto again;
-
-      } else if (demux->segment.position > range_stop) {
-        /* wait a bit to be in range, we don't have any locks at that point */
-        GstClockTime wait_time =
-            gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
-        if (wait_time > 0) {
-          GST_DEBUG_OBJECT (stream,
-              "Download waiting for %" GST_TIME_FORMAT,
-              GST_TIME_ARGS (wait_time));
-          g_assert (stream->pending_cb_id == 0);
-          GST_LOG_OBJECT (stream, "Scheduling delayed load_a_fragment() call");
-          stream->pending_cb_id =
-              gst_adaptive_demux_loop_call_delayed (demux->priv->scheduler_task,
-              wait_time,
-              (GSourceFunc) gst_adaptive_demux2_stream_load_a_fragment,
-              gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
+      if (gst_adaptive_demux_get_live_seek_range (demux, &range_start,
+              &range_stop)) {
+        if (demux->segment.position < range_start) {
+          /* This should advance into the valid playlist range */
+          GST_DEBUG_OBJECT (stream, "Retrying once with next segment");
+          stream->download_error_retry = TRUE;
+          gst_adaptive_demux2_stream_finish_download (stream, GST_FLOW_OK,
+              NULL);
+          return;
+        } else if (demux->segment.position > range_stop) {
+          /* wait a bit to be in range */
+          GstClockTime wait_time =
+              gst_adaptive_demux2_stream_get_fragment_waiting_time (stream);
+          if (wait_time > 0) {
+            GST_DEBUG_OBJECT (stream,
+                "Download waiting for %" GST_TIME_FORMAT,
+                GST_TIME_ARGS (wait_time));
+            g_assert (stream->pending_cb_id == 0);
+            GST_LOG_OBJECT (stream,
+                "Scheduling delayed load_a_fragment() call");
+            stream->pending_cb_id =
+                gst_adaptive_demux_loop_call_delayed (demux->
+                priv->scheduler_task, wait_time,
+                (GSourceFunc) gst_adaptive_demux2_stream_load_a_fragment,
+                gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
+            return;
+          }
+        } else {
+          GST_LOG_OBJECT (stream,
+              "Failed segment is inside the live range, retrying");
         }
+      } else {
+        GST_LOG_OBJECT (stream, "Could not get live seek range after error");
       }
     }
 
-  flushing:
     if (stream->download_error_count >= MAX_DOWNLOAD_ERROR_COUNT) {
       /* looks like there is no way of knowing when a live stream has ended
        * Have to assume we are falling behind and cause a manifest reload */
@@ -1324,7 +1336,8 @@ on_download_progress (DownloadRequest * request, DownloadRequestState state,
         G_GUINT64_FORMAT " bytes", gst_buffer_get_size (buffer),
         request->content_received, request->content_length);
 
-    /* Drop the request lock when parsing data. FIXME: Check and comment why this is needed */
+    /* Drop the request lock when parsing data. That allows the DownloadHelper to
+     * add more data while we're parsing (if more arrives) */
     download_request_unlock (request);
     ret = gst_adaptive_demux2_stream_parse_buffer (stream, buffer);
     download_request_lock (request);
@@ -1359,6 +1372,7 @@ on_download_complete (DownloadRequest * request, DownloadRequestState state,
   GstBuffer *buffer;
 
   stream->download_active = FALSE;
+  stream->download_error_retry = FALSE;
 
   if (stream->state != GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING) {
     GST_DEBUG_OBJECT (stream, "Stream state changed to %d. Aborting",
@@ -1389,18 +1403,40 @@ on_download_complete (DownloadRequest * request, DownloadRequestState state,
   gst_adaptive_demux2_stream_finish_download (stream, ret, NULL);
 }
 
+static GstFlowReturn
+gst_adaptive_demux2_stream_submit_request_default (GstAdaptiveDemux2Stream *
+    stream, DownloadRequest * download_req)
+{
+  GstAdaptiveDemux *demux = stream->demux;
+
+  if (!downloadhelper_submit_request (demux->download_helper,
+          NULL, DOWNLOAD_FLAG_NONE, download_req, NULL))
+    return GST_FLOW_ERROR;
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_adaptive_demux2_stream_submit_request (GstAdaptiveDemux2Stream * stream,
+    DownloadRequest * download_req)
+{
+  GstAdaptiveDemux2StreamClass *klass =
+      GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+  g_assert (klass->submit_request != NULL);
+  return klass->submit_request (stream, download_req);
+}
+
 /* must be called from the scheduler context
  *
  * Will submit the request only, which will complete asynchronously
  */
 static GstFlowReturn
-gst_adaptive_demux2_stream_begin_download_uri (GstAdaptiveDemux * demux,
-    GstAdaptiveDemux2Stream * stream, const gchar * uri, gint64 start,
-    gint64 end)
+gst_adaptive_demux2_stream_begin_download_uri (GstAdaptiveDemux2Stream * stream,
+    const gchar * uri, gint64 start, gint64 end)
 {
   DownloadRequest *request = stream->download_request;
 
-  GST_DEBUG_OBJECT (demux,
+  GST_DEBUG_OBJECT (stream,
       "Downloading %s uri: %s, range:%" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
       uritype (stream), uri, start, end);
 
@@ -1424,13 +1460,15 @@ gst_adaptive_demux2_stream_begin_download_uri (GstAdaptiveDemux * demux,
         (DownloadRequestEventCallback) on_download_progress, stream);
   }
 
-  if (!downloadhelper_submit_request (demux->download_helper,
-          demux->manifest_uri, DOWNLOAD_FLAG_NONE, request, NULL))
-    return GST_FLOW_ERROR;
 
   stream->download_active = TRUE;
+  GstFlowReturn ret =
+      gst_adaptive_demux2_stream_submit_request (stream, request);
+  if (ret != GST_FLOW_OK) {
+    stream->download_active = FALSE;
+  }
 
-  return GST_FLOW_OK;
+  return ret;
 }
 
 /* must be called from the scheduler context */
@@ -1469,7 +1507,7 @@ gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
 
     stream->downloading_header = TRUE;
 
-    return gst_adaptive_demux2_stream_begin_download_uri (demux, stream,
+    return gst_adaptive_demux2_stream_begin_download_uri (stream,
         stream->fragment.header_uri, stream->fragment.header_range_start,
         stream->fragment.header_range_end);
   }
@@ -1483,7 +1521,7 @@ gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
 
     stream->downloading_index = TRUE;
 
-    return gst_adaptive_demux2_stream_begin_download_uri (demux, stream,
+    return gst_adaptive_demux2_stream_begin_download_uri (stream,
         stream->fragment.index_uri, stream->fragment.index_range_start,
         stream->fragment.index_range_end);
   }
@@ -1516,14 +1554,14 @@ gst_adaptive_demux2_stream_download_fragment (GstAdaptiveDemux2Stream * stream)
     GST_DEBUG_OBJECT (stream,
         "Starting chunked download %s %" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
         url, range_start, chunk_end);
-    return gst_adaptive_demux2_stream_begin_download_uri (demux, stream, url,
+    return gst_adaptive_demux2_stream_begin_download_uri (stream, url,
         range_start, chunk_end);
   }
 
   /* regular single chunk download */
   stream->fragment.chunk_size = 0;
 
-  return gst_adaptive_demux2_stream_begin_download_uri (demux, stream, url,
+  return gst_adaptive_demux2_stream_begin_download_uri (stream, url,
       stream->fragment.range_start, stream->fragment.range_end);
 
 no_url_error:
@@ -1732,6 +1770,71 @@ gst_adaptive_demux2_stream_on_manifest_update (GstAdaptiveDemux2Stream * stream)
       gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
 }
 
+void
+gst_adaptive_demux2_stream_on_can_download_fragments (GstAdaptiveDemux2Stream *
+    stream)
+{
+  GstAdaptiveDemux *demux = stream->demux;
+
+  if (stream->state != GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_BEFORE_DOWNLOAD)
+    return;
+
+  g_assert (stream->pending_cb_id == 0);
+
+  GST_LOG_OBJECT (stream, "Scheduling load_a_fragment() call");
+  stream->pending_cb_id =
+      gst_adaptive_demux_loop_call (demux->priv->scheduler_task,
+      (GSourceFunc) gst_adaptive_demux2_stream_load_a_fragment,
+      gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
+}
+
+/*
+ * Called by a subclass that has returned GST_ADAPTIVE_DEMUX_FLOW_BUSY
+ * from update_fragment_info() to indicate that it is ready to continue
+ * downloading now.
+ *
+ * Called from the scheduler task
+ */
+void
+gst_adaptive_demux2_stream_mark_prepared (GstAdaptiveDemux2Stream * stream)
+{
+  GstAdaptiveDemux *demux = stream->demux;
+
+  /* hlsdemux calls this method whenever a playlist is updated, so also
+   * use it to wake up a stream that's waiting at the live edge */
+  if (stream->state == GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE) {
+    gst_adaptive_demux2_stream_on_manifest_update (stream);
+  }
+
+  g_cond_broadcast (&stream->prepare_cond);
+  if (stream->state != GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_PREPARE)
+    return;
+
+  g_assert (stream->pending_cb_id == 0);
+
+  GST_LOG_OBJECT (stream, "Scheduling load_a_fragment() call");
+  stream->pending_cb_id =
+      gst_adaptive_demux_loop_call (demux->priv->scheduler_task,
+      (GSourceFunc) gst_adaptive_demux2_stream_load_a_fragment,
+      gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
+}
+
+/* Called by external threads (manifest input on sinkpad, and seek handling)
+ * when it requires the stream to be prepared before they can continue
+ * Must be held with the SCHEDULER lock held */
+gboolean
+gst_adaptive_demux2_stream_wait_prepared (GstAdaptiveDemux2Stream * stream)
+{
+  GstAdaptiveDemux *demux = stream->demux;
+
+  g_mutex_lock (&stream->prepare_lock);
+  GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
+  g_cond_wait (&stream->prepare_cond, &stream->prepare_lock);
+  g_mutex_unlock (&stream->prepare_lock);
+
+  return GST_ADAPTIVE_SCHEDULER_LOCK (demux);
+}
+
 static void
 gst_adaptive_demux2_stream_handle_playlist_eos (GstAdaptiveDemux2Stream *
     stream)
@@ -1773,17 +1876,23 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
   switch (stream->state) {
     case GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART:
     case GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT:
+    case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_PREPARE:
     case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE:
     case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE:
     case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE:
+    case GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_BEFORE_DOWNLOAD:
       /* Get information about the fragment to download */
       GST_DEBUG_OBJECT (demux, "Calling update_fragment_info");
       ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
       GST_DEBUG_OBJECT (stream,
           "Fragment info update result: %d %s", ret, gst_flow_get_name (ret));
 
-      if (ret == GST_FLOW_OK)
+      if (ret == GST_FLOW_OK) {
+        /* Wake anyone that's waiting for this stream to get prepared */
+        if (stream->state == GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_PREPARE)
+          g_cond_broadcast (&stream->prepare_cond);
         stream->starting_fragment = TRUE;
+      }
       break;
     case GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING:
       break;
@@ -1800,6 +1909,18 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
       break;
   }
 
+  if (ret == GST_ADAPTIVE_DEMUX_FLOW_BUSY) {
+    GST_LOG_OBJECT (stream,
+        "Sub-class returned BUSY flow return. Waiting in PREPARE state");
+    /* Need to take the prepare lock specifically when switching
+     * to WAITING_PREPARE state, to avoid a race in _wait_prepared();
+     */
+    g_mutex_lock (&stream->prepare_lock);
+    stream->state = GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_PREPARE;
+    g_mutex_unlock (&stream->prepare_lock);
+    return FALSE;
+  }
+
   if (ret == GST_FLOW_OK) {
     /* Wait for room in the output tracks */
     if (gst_adaptive_demux2_stream_wait_for_output_space (demux, stream,
@@ -1830,10 +1951,12 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
         return FALSE;
       }
     }
+  }
 
-    if (gst_adaptive_demux2_stream_download_fragment (stream) != GST_FLOW_OK) {
-      GST_ERROR_OBJECT (demux,
-          "Failed to begin fragment download for stream %p", stream);
+  if (ret == GST_FLOW_OK) {
+    if (!demux->priv->streams_can_download_fragments) {
+      GST_LOG_OBJECT (stream, "Waiting for fragment downloads to be unblocked");
+      stream->state = GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_BEFORE_DOWNLOAD;
       return FALSE;
     }
   }
@@ -1842,7 +1965,13 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
    * GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC is not in the GstFlowReturn enum */
   switch ((int) ret) {
     case GST_FLOW_OK:
-      break;                    /* all is good, let's go */
+      /* all is good, let's go */
+      if (gst_adaptive_demux2_stream_download_fragment (stream) != GST_FLOW_OK) {
+        GST_ERROR_OBJECT (demux,
+            "Failed to begin fragment download for stream %p", stream);
+        return FALSE;
+      }
+      break;
     case GST_FLOW_EOS:
       GST_DEBUG_OBJECT (stream, "EOS, checking to stop download loop");
       stream->last_ret = ret;
@@ -1851,6 +1980,7 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
     case GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC:
       GST_DEBUG_OBJECT (stream, "Lost sync, asking reset to current position");
       stream->state = GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED;
+      g_cond_broadcast (&stream->prepare_cond);
       gst_adaptive_demux_handle_lost_sync (demux);
       return FALSE;
     case GST_FLOW_NOT_LINKED:
@@ -1946,8 +2076,8 @@ gst_adaptive_demux2_stream_next_download (GstAdaptiveDemux2Stream * stream)
 
       GST_DEBUG_OBJECT (stream,
           "stream_time after restart seek: %" GST_STIME_FORMAT
-          " position %" GST_STIME_FORMAT, GST_STIME_ARGS (stream_time),
-          GST_STIME_ARGS (stream->current_position));
+          " position %" GST_TIME_FORMAT, GST_STIME_ARGS (stream_time),
+          GST_TIME_ARGS (stream->current_position));
     }
 
     /* Trigger (re)computation of the parsebin input segment */
@@ -1982,45 +2112,35 @@ gst_adaptive_demux2_stream_next_download (GstAdaptiveDemux2Stream * stream)
   return gst_adaptive_demux2_stream_load_a_fragment (stream);
 }
 
-static gboolean
-gst_adaptive_demux2_stream_can_start (GstAdaptiveDemux2Stream * stream)
-{
-  GstAdaptiveDemux2StreamClass *klass =
-      GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
-
-  if (!klass->can_start)
-    return TRUE;
-  return klass->can_start (stream);
-}
-
 /**
  * gst_adaptive_demux2_stream_start:
  * @stream: a #GstAdaptiveDemux2Stream
  *
- * Start the given @stream. Should be called by subclasses that previously
- * returned %FALSE in `GstAdaptiveDemux::stream_can_start()`
+ * Start the given @stream. Can be called by subclasses that previously
+ * returned %FALSE in `GstAdaptiveDemux2Stream::start()`, or from
+ * the demuxer when a stream should start downloading.
  */
 void
 gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream)
 {
-  GstAdaptiveDemux *demux;
-
   g_return_if_fail (stream && stream->demux);
 
-  demux = stream->demux;
-
   if (stream->pending_cb_id != 0 || stream->download_active) {
     /* There is already something active / pending on this stream */
     GST_LOG_OBJECT (stream, "Stream already running");
     return;
   }
 
-  /* Some streams require a delayed start, i.e. they need more information
-   * before they can actually be started */
-  if (!gst_adaptive_demux2_stream_can_start (stream)) {
-    GST_LOG_OBJECT (stream, "Stream will be started asynchronously");
-    return;
-  }
+  GstAdaptiveDemux2StreamClass *klass =
+      GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+
+  klass->start (stream);
+}
+
+static void
+gst_adaptive_demux2_stream_start_default (GstAdaptiveDemux2Stream * stream)
+{
+  GstAdaptiveDemux *demux = stream->demux;
 
   if (stream->state == GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS) {
     GST_LOG_OBJECT (stream, "Stream is EOS already");
@@ -2046,11 +2166,21 @@ gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream)
 
 void
 gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream)
+{
+  GstAdaptiveDemux2StreamClass *klass =
+      GST_ADAPTIVE_DEMUX2_STREAM_GET_CLASS (stream);
+
+  klass->stop (stream);
+}
+
+static void
+gst_adaptive_demux2_stream_stop_default (GstAdaptiveDemux2Stream * stream)
 {
   GstAdaptiveDemux *demux = stream->demux;
 
   GST_DEBUG_OBJECT (stream, "Stopping stream (from state %d)", stream->state);
   stream->state = GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED;
+  g_cond_broadcast (&stream->prepare_cond);
 
   if (stream->pending_cb_id != 0) {
     gst_adaptive_demux_loop_cancel_call (demux->priv->scheduler_task,
@@ -2065,6 +2195,8 @@ gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream)
   stream->downloading_header = stream->downloading_index = FALSE;
   stream->download_request = download_request_new ();
   stream->download_active = FALSE;
+  stream->download_error_retry = FALSE;
+  stream->download_error_count = 0;
 
   stream->next_input_wakeup_time = GST_CLOCK_STIME_NONE;
 }
@@ -2081,6 +2213,8 @@ gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream)
   return TRUE;
 }
 
+/* Returns TRUE if the stream has at least one selected track.
+ * Must be called with the TRACKS_LOCK held */
 gboolean
 gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream * stream)
 {
@@ -2095,6 +2229,20 @@ gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream * stream)
   return FALSE;
 }
 
+gboolean
+gst_adaptive_demux2_stream_is_default_locked (GstAdaptiveDemux2Stream * stream)
+{
+  GList *tmp;
+
+  for (tmp = stream->tracks; tmp; tmp = tmp->next) {
+    GstAdaptiveDemuxTrack *track = tmp->data;
+    if (track->flags & GST_STREAM_FLAG_SELECT)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
 /**
  * gst_adaptive_demux2_stream_is_selected:
  * @stream: A #GstAdaptiveDemux2Stream
@@ -2562,7 +2710,7 @@ gst_adaptive_demux2_stream_update_current_bitrate (GstAdaptiveDemux2Stream *
    * fraction of the measured download rate */
   target_download_rate =
       CLAMP (stream->current_download_rate, 0,
-      G_MAXUINT) * demux->bandwidth_target_ratio;
+      G_MAXUINT) * (gdouble) demux->bandwidth_target_ratio;
 
   GST_DEBUG_OBJECT (stream, "Bitrate after target ratio limit (%0.2f): %u",
       demux->bandwidth_target_ratio, target_download_rate);
diff --git a/ext/adaptivedemux2/gstadaptivedemux-stream.h b/ext/adaptivedemux2/gstadaptivedemux-stream.h
index 6cbde86fdd20c53ac6e7cb8d5a28213523d5489c..2b99b04c5afb6de4ab0eff79899bc3e0bef38960 100644
--- a/ext/adaptivedemux2/gstadaptivedemux-stream.h
+++ b/ext/adaptivedemux2/gstadaptivedemux-stream.h
@@ -82,9 +82,11 @@ enum _GstAdaptiveDemux2StreamState {
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT,
+  GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_PREPARE, /* Sub-class is busy and can't update_fragment_info() yet */
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE,
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE,
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE,
+  GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_BEFORE_DOWNLOAD, /* Ready, but not allowed to download yet */
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING,
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS,
   GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED
@@ -101,11 +103,25 @@ struct _GstAdaptiveDemux2StreamClass
    * Requests the stream to set the information about the current fragment to its
    * current fragment struct
    *
-   * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS
-   *          if there is no fragment.
+   * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error, #GST_FLOW_EOS
+   *          if there is no fragment, or the custom GST_ADAPTIVE_DEMUX_FLOW_BUSY
+   *          if the sub-class is still preparing.
    */
   GstFlowReturn (*update_fragment_info) (GstAdaptiveDemux2Stream * stream);
 
+  /**
+   * submit_request:
+   * @stream: #GstAdaptiveDemux2Stream
+   * @download_req: #DownloadRequest
+   *
+   * Requests the stream submit the provided download request for processing,
+   * either through the DownloadHelper (default), or through some sub-class
+   * mechanism
+   *
+   * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error
+   */
+  GstFlowReturn (*submit_request) (GstAdaptiveDemux2Stream * stream, DownloadRequest * download_req);
+
   /**
    * finish_fragment:
    * @stream: #GstAdaptiveDemux2Stream
@@ -138,14 +154,26 @@ struct _GstAdaptiveDemux2StreamClass
 				    GstClockTimeDiff       * final_ts);
 
   /**
-   * can_start:
+   * start:
    * @stream: a #GstAdaptiveDemux2Stream
    *
-   * Called before starting a @stream. sub-classes can return %FALSE if more
-   * information is required before it can be started. Sub-classes will have to
-   * call gst_adaptive_demux2_stream_start() when the stream should be started.
+   * Called to start downloading a @stream, sub-classes should chain up to the default
+   * implementation. Sub-classes can return %FALSE if more
+   * information is required before the stream can be started. In that case, sub-classes
+   * will have to call gst_adaptive_demux2_stream_start() again when the stream should
+   * be started.
    */
-  gboolean      (*can_start) (GstAdaptiveDemux2Stream *stream);
+  void       (*start) (GstAdaptiveDemux2Stream *stream);
+
+  /**
+   * stop:
+   * @stream: a #GstAdaptiveDemux2Stream
+   *
+   * Called to stop downloading a @stream, sub-classes should chain up to the default
+   * implementation.
+   */
+  void       (*stop) (GstAdaptiveDemux2Stream *stream);
+
 
   /**
    * create_tracks:
@@ -281,6 +309,10 @@ struct _GstAdaptiveDemux2Stream
   GstAdaptiveDemux2StreamState state;
   guint pending_cb_id;
   gboolean download_active;
+
+  GMutex prepare_lock;
+  GCond prepare_cond;
+
   /* The (global output) time at which this stream should be woken
    * to download more input */
   GstClockTimeDiff next_input_wakeup_time;
@@ -310,6 +342,7 @@ struct _GstAdaptiveDemux2Stream
 
   GstAdaptiveDemux2StreamFragment fragment;
 
+  gboolean download_error_retry;
   guint download_error_count;
 
   /* Last collection provided by parsebin */
@@ -347,6 +380,9 @@ GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux2Stre
 gboolean gst_adaptive_demux2_stream_handle_collection (GstAdaptiveDemux2Stream *stream,
     GstStreamCollection *collection, gboolean *had_pending_tracks);
 
+void gst_adaptive_demux2_stream_mark_prepared(GstAdaptiveDemux2Stream *stream);
+gboolean gst_adaptive_demux2_stream_wait_prepared(GstAdaptiveDemux2Stream *stream);
+
 void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f);
 
 G_END_DECLS
diff --git a/ext/adaptivedemux2/gstadaptivedemux-track.c b/ext/adaptivedemux2/gstadaptivedemux-track.c
index b620772313ca661d74b8b472577d89d5c56214a3..3fdb1df53583d5519204740a6ed4367fc46f5409 100644
--- a/ext/adaptivedemux2/gstadaptivedemux-track.c
+++ b/ext/adaptivedemux2/gstadaptivedemux-track.c
@@ -38,8 +38,8 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
 void
 gst_adaptive_demux_track_flush (GstAdaptiveDemuxTrack * track)
 {
-  GST_DEBUG_OBJECT (track->demux, "Flushing track '%s' with %u queued items",
-      track->stream_id, gst_queue_array_get_length (track->queue));
+  GST_DEBUG_ID (track->id, "Flushing track with %u queued items",
+      gst_queue_array_get_length (track->queue));
   gst_queue_array_clear (track->queue);
 
   gst_event_store_flush (&track->sticky_events);
@@ -69,15 +69,14 @@ static gboolean
 _track_sink_query_function (GstPad * pad, GstObject * parent, GstQuery * query)
 {
   GstAdaptiveDemuxTrack *track = gst_pad_get_element_private (pad);
-  GstAdaptiveDemux *demux = track->demux;
   gboolean ret = FALSE;
 
-  GST_DEBUG_OBJECT (pad, "query %" GST_PTR_FORMAT, query);
+  GST_DEBUG_ID (track->id, "query %" GST_PTR_FORMAT, query);
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_ACCEPT_CAPS:
       /* Should we intersect by track caps as a safety check ? */
-      GST_DEBUG_OBJECT (demux, "We accept any caps on %s:%s",
+      GST_DEBUG_ID (track->id, "We accept any caps on %s:%s",
           GST_DEBUG_PAD_NAME (pad));
       gst_query_set_accept_caps_result (query, TRUE);
       ret = TRUE;
@@ -103,9 +102,9 @@ track_dequeue_item_locked (GstAdaptiveDemux * demux,
   *out_item = *item;
   gst_queue_array_pop_head (track->queue);
 
-  GST_LOG_OBJECT (demux,
-      "track %s (period %u) item running_time %" GST_STIME_FORMAT " end %"
-      GST_STIME_FORMAT, track->stream_id, track->period_num,
+  GST_LOG_ID (track->id,
+      "item running_time %" GST_STIME_FORMAT " end %"
+      GST_STIME_FORMAT,
       GST_STIME_ARGS (out_item->runningtime),
       GST_STIME_ARGS (out_item->runningtime_end));
 
@@ -118,7 +117,7 @@ static inline GstClockTimeDiff my_segment_to_running_time (GstSegment * segment,
 /* Dequeue or generate a buffer/event from the track queue and update the buffering levels
  * TRACKS_LOCK hold */
 GstMiniObject *
-track_dequeue_data_locked (GstAdaptiveDemux * demux,
+gst_adaptive_demux_track_dequeue_data_locked (GstAdaptiveDemux * demux,
     GstAdaptiveDemuxTrack * track, gboolean check_sticky_events)
 {
   GstMiniObject *res = NULL;
@@ -136,9 +135,8 @@ track_dequeue_data_locked (GstAdaptiveDemux * demux,
       res = (GstMiniObject *) event;
       running_time_buffering = running_time = running_time_end =
           GST_CLOCK_STIME_NONE;
-      GST_DEBUG_OBJECT (demux,
-          "track %s (period %u) dequeued pending sticky event %" GST_PTR_FORMAT,
-          track->stream_id, track->period_num, event);
+      GST_DEBUG_ID (track->id,
+          "dequeued pending sticky event %" GST_PTR_FORMAT, event);
       is_pending_sticky = TRUE;
       goto handle_event;
     }
@@ -213,10 +211,10 @@ track_dequeue_data_locked (GstAdaptiveDemux * demux,
       pos = cstart;
       duration = cstop - cstart;
 
-      GST_DEBUG_OBJECT (demux,
-          "track %s (period %u) Starting gap for runningtime %" GST_STIME_FORMAT
+      GST_DEBUG_ID (track->id,
+          "Starting gap for runningtime %" GST_STIME_FORMAT
           " - clipped position %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT,
-          track->stream_id, track->period_num, GST_STIME_ARGS (running_time),
+          GST_STIME_ARGS (running_time),
           GST_TIME_ARGS (pos), GST_TIME_ARGS (duration));
 
       track->gap_position = pos;
@@ -252,8 +250,8 @@ handle_event:
           GstClockTimeDiff global_output_position =
               demux->priv->global_output_position;
 
-          GST_DEBUG ("track %s: Override segment for running time %"
-              GST_STIME_FORMAT " : %" GST_PTR_FORMAT, track->stream_id,
+          GST_DEBUG_ID (track->id, "Override segment for running time %"
+              GST_STIME_FORMAT " : %" GST_PTR_FORMAT,
               GST_STIME_ARGS (global_output_position), event);
           gst_event_unref (event);
           gst_segment_set_running_time (&track->output_segment, GST_FORMAT_TIME,
@@ -278,9 +276,7 @@ handle_event:
     /* Store any sticky event in the cache, unless this is already an event
      * from the pending sticky_events store */
     if (!is_pending_sticky && GST_EVENT_IS_STICKY (event)) {
-      GST_DEBUG_OBJECT (demux,
-          "track %s Storing sticky event %" GST_PTR_FORMAT,
-          track->stream_id, event);
+      GST_DEBUG_ID (track->id, "Storing sticky event %" GST_PTR_FORMAT, event);
       gst_event_store_insert_event (&track->sticky_events, event, FALSE);
     }
   }
@@ -289,14 +285,13 @@ handle_event:
   if (GST_CLOCK_STIME_IS_VALID (running_time_buffering)) {
     track->output_time = running_time_buffering;
 
-    GST_LOG_OBJECT (demux,
-        "track %s buffering time:%" GST_STIME_FORMAT,
-        track->stream_id, GST_STIME_ARGS (running_time_buffering));
+    GST_LOG_ID (track->id,
+        "buffering time:%" GST_STIME_FORMAT,
+        GST_STIME_ARGS (running_time_buffering));
 
     gst_adaptive_demux_track_update_level_locked (track);
   } else {
-    GST_LOG_OBJECT (demux, "track %s popping untimed item %" GST_PTR_FORMAT,
-        track->stream_id, res);
+    GST_LOG_ID (track->id, "popping untimed item %" GST_PTR_FORMAT, res);
   }
 
   track->level_bytes -= item_size;
@@ -310,9 +305,9 @@ gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track,
 {
   GstAdaptiveDemux *demux = track->demux;
 
-  GST_DEBUG_OBJECT (demux,
-      "Track '%s' draining to running time %" GST_STIME_FORMAT,
-      track->stream_id, GST_STIME_ARGS (drain_running_time));
+  GST_DEBUG_ID (track->id,
+      "draining to running time %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (drain_running_time));
 
   while (track->next_position == GST_CLOCK_STIME_NONE ||
       track->next_position < drain_running_time) {
@@ -334,9 +329,9 @@ gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track,
           my_segment_to_running_time (&track->output_segment, gap_end);
 
       if (running_time_end >= drain_running_time) {
-        GST_DEBUG_OBJECT (demux,
-            "Track '%s' drained to GAP with running time %" GST_STIME_FORMAT,
-            track->stream_id, GST_STIME_ARGS (running_time_end));
+        GST_DEBUG_ID (track->id,
+            "drained to GAP with running time %" GST_STIME_FORMAT,
+            GST_STIME_ARGS (running_time_end));
         return;
       }
 
@@ -348,8 +343,7 @@ gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track,
     item = gst_queue_array_peek_head_struct (track->queue);
     /* track is empty, we're done */
     if (item == NULL) {
-      GST_DEBUG_OBJECT (demux, "Track '%s' completely drained",
-          track->stream_id);
+      GST_DEBUG_ID (track->id, "Track completely drained");
       return;
     }
 
@@ -357,21 +351,22 @@ gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track,
      * we're done. */
     if (item->runningtime != GST_CLOCK_STIME_NONE
         && item->runningtime >= drain_running_time) {
-      GST_DEBUG_OBJECT (demux, "Track '%s' drained to item %" GST_PTR_FORMAT
+      GST_DEBUG_ID (track->id, "Track drained to item %" GST_PTR_FORMAT
           " with running time %" GST_STIME_FORMAT,
-          track->stream_id, item->item, GST_STIME_ARGS (item->runningtime));
+          item->item, GST_STIME_ARGS (item->runningtime));
       return;
     }
 
-    GST_DEBUG_OBJECT (demux, "Track '%s' discarding %" GST_PTR_FORMAT
+    GST_DEBUG_ID (track->id, "discarding %" GST_PTR_FORMAT
         " with running time %" GST_STIME_FORMAT,
-        track->stream_id, item->item, GST_STIME_ARGS (item->runningtime));
+        item->item, GST_STIME_ARGS (item->runningtime));
 
     /* Dequeue the item and discard. Sticky events
      * will be collected by the dequeue function, gaps will be started.
      * If it's a buffer, mark the track as discont to get the flag set
      * on the next output buffer */
-    next_mo = track_dequeue_data_locked (demux, track, FALSE);
+    next_mo =
+        gst_adaptive_demux_track_dequeue_data_locked (demux, track, FALSE);
     if (GST_IS_BUFFER (next_mo)) {
       track->output_discont = TRUE;
     }
@@ -379,8 +374,8 @@ gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track,
     gst_adaptive_demux_track_update_next_position (track);
   }
 
-  GST_DEBUG_OBJECT (demux,
-      "Track '%s' drained to running time %" GST_STIME_FORMAT, track->stream_id,
+  GST_DEBUG_ID (track->id,
+      "drained to running time %" GST_STIME_FORMAT,
       GST_STIME_ARGS (track->next_position));
 }
 
@@ -474,18 +469,17 @@ track_queue_data_locked (GstAdaptiveDemux * demux,
      * so buffering level is updated correctly */
     if (!GST_CLOCK_STIME_IS_VALID (track->output_time)) {
       track->output_time = track->lowest_input_time;
-      GST_LOG_OBJECT (track->sinkpad,
-          "track %s (period %u) set output_time = lowest input_time = %"
-          GST_STIME_FORMAT, track->stream_id, track->period_num,
-          GST_STIME_ARGS (track->output_time));
+      GST_LOG_ID (track->id,
+          "setting output_time = lowest input_time = %"
+          GST_STIME_FORMAT, GST_STIME_ARGS (track->output_time));
     }
 
     gst_adaptive_demux_track_update_level_locked (track);
   }
 
-  GST_LOG_OBJECT (track->sinkpad,
-      "track %s item running_time :%" GST_STIME_FORMAT " end :%"
-      GST_STIME_FORMAT, track->stream_id, GST_STIME_ARGS (item.runningtime),
+  GST_LOG_ID (track->id,
+      "item running_time :%" GST_STIME_FORMAT " end :%"
+      GST_STIME_FORMAT, GST_STIME_ARGS (item.runningtime),
       GST_STIME_ARGS (item.runningtime_end));
 
   track->level_bytes += size;
@@ -506,10 +500,22 @@ _track_sink_chain_function (GstPad * pad, GstObject * parent,
   GstAdaptiveDemux *demux = track->demux;
   GstClockTime ts;
 
-  GST_DEBUG_OBJECT (pad, "buffer %" GST_PTR_FORMAT, buffer);
+  GST_DEBUG_ID (track->id, "buffer %" GST_PTR_FORMAT, buffer);
 
   TRACKS_LOCK (demux);
 
+  /* Discard buffers that are received outside of a valid segment. This can
+   * happen if a flushing seek (which resets the track segment seqnums) was
+   * received but the stream is still providing buffers before returning.
+   */
+  if (track->input_segment_seqnum == GST_SEQNUM_INVALID) {
+    GST_DEBUG_OBJECT (pad,
+        "Dropping buffer because we do not have a valid input segment");
+    gst_buffer_unref (buffer);
+    TRACKS_UNLOCK (demux);
+    return GST_FLOW_OK;
+  }
+
   ts = GST_BUFFER_DTS_OR_PTS (buffer);
 
   /* Buffers coming out of parsebin *should* always be timestamped (it's the
@@ -530,12 +536,12 @@ _track_sink_chain_function (GstPad * pad, GstObject * parent,
    */
   if (!GST_CLOCK_TIME_IS_VALID (ts)) {
     if (GST_CLOCK_TIME_IS_VALID (track->input_segment.position)) {
-      GST_WARNING_OBJECT (pad,
+      GST_WARNING_ID (track->id,
           "buffer doesn't have any pts or dts, using segment position (%"
           GST_TIME_FORMAT ")", GST_TIME_ARGS (track->input_segment.position));
       ts = track->input_segment.position;
     } else {
-      GST_ERROR_OBJECT (pad, "initial buffer doesn't have any pts or dts !");
+      GST_ERROR_ID (track->id, "initial buffer doesn't have any pts or dts !");
       gst_buffer_unref (buffer);
       TRACKS_UNLOCK (demux);
       return GST_FLOW_ERROR;
@@ -549,7 +555,7 @@ _track_sink_chain_function (GstPad * pad, GstObject * parent,
     GstClockTime duration = ts - track->input_segment.position;
     GstEvent *gap = gst_event_new_gap (track->input_segment.position, duration);
     /* Insert gap event to ensure coherent interleave */
-    GST_DEBUG_OBJECT (pad,
+    GST_DEBUG_ID (track->id,
         "Inserting gap for %" GST_TIME_FORMAT " vs %" GST_TIME_FORMAT,
         GST_TIME_ARGS (ts), GST_TIME_ARGS (track->input_segment.position));
     track_queue_data_locked (demux, track, (GstMiniObject *) gap, 0,
@@ -579,7 +585,7 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
   gboolean drop = FALSE;
   gboolean is_discont = FALSE;
 
-  GST_DEBUG_OBJECT (pad, "event %" GST_PTR_FORMAT, event);
+  GST_DEBUG_ID (track->id, "event %" GST_PTR_FORMAT, event);
 
   TRACKS_LOCK (demux);
 
@@ -587,13 +593,13 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
     case GST_EVENT_STREAM_COLLECTION:
     {
       /* Replace upstream collection with demux collection */
-      GST_DEBUG_OBJECT (pad, "Dropping stream-collection, we send our own");
+      GST_DEBUG_ID (track->id, "Dropping stream-collection, we send our own");
       drop = TRUE;
       break;
     }
     case GST_EVENT_STREAM_START:
     {
-      GST_DEBUG_OBJECT (pad, "Dropping stream-start, we send our own");
+      GST_DEBUG_ID (track->id, "Dropping stream-start, we send our own");
       if (track->eos) {
         gint i, len;
         /* Find and drop latest EOS if present */
@@ -604,8 +610,8 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
           if (GST_IS_EVENT (item->item)
               && GST_EVENT_TYPE (item->item) == GST_EVENT_EOS) {
             TrackQueueItem sub;
-            GST_DEBUG_OBJECT (pad, "Removing previously received EOS (pos:%d)",
-                i);
+            GST_DEBUG_ID (track->id,
+                "Removing previously received EOS (pos:%d)", i);
             if (gst_queue_array_drop_struct (track->queue, i, &sub))
               gst_mini_object_unref (sub.item);
             break;
@@ -619,7 +625,7 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
     case GST_EVENT_EOS:
     {
       if (track->pending_srcpad != NULL) {
-        GST_DEBUG_OBJECT (pad,
+        GST_DEBUG_ID (track->id,
             "Dropping EOS because we have a pending pad switch");
         drop = TRUE;
       } else {
@@ -639,7 +645,7 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
   }
 
   if (drop || !GST_EVENT_IS_SERIALIZED (event)) {
-    GST_DEBUG_OBJECT (pad, "dropping event %s", GST_EVENT_TYPE_NAME (event));
+    GST_DEBUG_ID (track->id, "dropping event %s", GST_EVENT_TYPE_NAME (event));
     gst_event_unref (event);
     TRACKS_UNLOCK (demux);
     /* Silently "accept" them */
@@ -652,7 +658,15 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
       guint64 seg_seqnum = gst_event_get_seqnum (event);
 
       if (track->input_segment_seqnum == seg_seqnum) {
-        GST_DEBUG_OBJECT (pad, "Ignoring duplicate segment");
+        GST_DEBUG_ID (track->id, "Ignoring duplicate segment");
+        gst_event_unref (event);
+        TRACKS_UNLOCK (demux);
+
+        return TRUE;
+      }
+
+      if (seg_seqnum != demux->priv->segment_seqnum) {
+        GST_DEBUG_OBJECT (pad, "Ignoring non-current segment");
         gst_event_unref (event);
         TRACKS_UNLOCK (demux);
 
@@ -665,8 +679,8 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
         track->input_segment.position = track->input_segment.start;
       else
         track->input_segment.position = track->input_segment.stop;
-      GST_DEBUG_OBJECT (pad, "track %s stored segment %" GST_SEGMENT_FORMAT,
-          track->stream_id, &track->input_segment);
+      GST_DEBUG_ID (track->id, "stored segment %" GST_SEGMENT_FORMAT,
+          &track->input_segment);
       timestamp = track->input_segment.position;
       is_discont = TRUE;
 
@@ -677,7 +691,7 @@ _track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
       gst_event_parse_gap (event, &timestamp, &duration);
 
       if (!GST_CLOCK_TIME_IS_VALID (timestamp)) {
-        GST_DEBUG_OBJECT (pad, "Dropping gap event with invalid timestamp");
+        GST_DEBUG_ID (track->id, "Dropping gap event with invalid timestamp");
         goto drop_ok;
       }
 
@@ -746,8 +760,8 @@ gst_adaptive_demux_track_update_next_position (GstAdaptiveDemuxTrack * track)
     TrackQueueItem *item = gst_queue_array_peek_nth_struct (track->queue, i);
 
     if (item->runningtime != GST_CLOCK_STIME_NONE) {
-      GST_DEBUG_OBJECT (track->demux,
-          "Track '%s' next position %" GST_STIME_FORMAT, track->stream_id,
+      GST_DEBUG_ID (track->id,
+          "next position %" GST_STIME_FORMAT,
           GST_STIME_ARGS (item->runningtime));
       track->next_position = item->runningtime;
       return;
@@ -755,8 +769,7 @@ gst_adaptive_demux_track_update_next_position (GstAdaptiveDemuxTrack * track)
   }
   track->next_position = GST_CLOCK_STIME_NONE;
 
-  GST_DEBUG_OBJECT (track->demux,
-      "Track '%s' doesn't have any pending timed data", track->stream_id);
+  GST_DEBUG_ID (track->id, "Track doesn't have any pending timed data");
 }
 
 /* TRACKS_LOCK held. Recomputes the level_time for the track */
@@ -776,21 +789,21 @@ gst_adaptive_demux_track_update_level_locked (GstAdaptiveDemuxTrack * track)
   else
     track->level_time = 0;
 
-  GST_LOG_OBJECT (track->sinkpad,
-      "track %s (period %u) input_time:%" GST_STIME_FORMAT " output_time:%"
+  GST_LOG_ID (track->id,
+      "input_time:%" GST_STIME_FORMAT " output_time:%"
       GST_STIME_FORMAT " level:%" GST_TIME_FORMAT,
-      track->stream_id, track->period_num, GST_STIME_ARGS (track->input_time),
+      GST_STIME_ARGS (track->input_time),
       GST_STIME_ARGS (track->output_time), GST_TIME_ARGS (track->level_time));
 }
 
 static void
 _demux_track_free (GstAdaptiveDemuxTrack * track)
 {
-  GST_DEBUG_OBJECT (track->demux, "freeing track %p '%s'", track,
-      track->stream_id);
+  GST_DEBUG_ID (track->id, "freeing track");
 
   g_free (track->stream_id);
   g_free (track->upstream_stream_id);
+  g_free (track->id);
 
   if (track->pending_srcpad)
     gst_object_unref (track->pending_srcpad);
@@ -816,7 +829,7 @@ GstAdaptiveDemuxTrack *
 gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack * track)
 {
   g_return_val_if_fail (track != NULL, NULL);
-  GST_TRACE ("%p %d -> %d", track, track->ref_count, track->ref_count + 1);
+  GST_TRACE_ID (track->id, "%d -> %d", track->ref_count, track->ref_count + 1);
   g_atomic_int_inc (&track->ref_count);
 
   return track;
@@ -827,7 +840,7 @@ gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack * track)
 {
   g_return_if_fail (track != NULL);
 
-  GST_TRACE ("%p %d -> %d", track, track->ref_count, track->ref_count - 1);
+  GST_TRACE_ID (track->id, "%d -> %d", track->ref_count, track->ref_count - 1);
   if (g_atomic_int_dec_and_test (&track->ref_count)) {
     _demux_track_free (track);
   }
@@ -848,31 +861,24 @@ gst_adaptive_demux_track_add_elements (GstAdaptiveDemuxTrack * track,
     guint period_num)
 {
   GstAdaptiveDemux *demux = track->demux;
-  gchar *internal_name;
+  gchar *tmpid;
   guint i, len;
 
   /* Store the period number for debugging output */
   track->period_num = period_num;
 
-  internal_name =
-      g_strdup_printf ("track-period%d-%s", period_num, track->stream_id);
-  len = strlen (internal_name);
-  for (i = 0; i < len; i++)
-    if (internal_name[i] == ' ')
-      internal_name[i] = '_';
-  track->element = gst_bin_new (internal_name);
-  g_free (internal_name);
-
-  internal_name =
-      g_strdup_printf ("track-period%d-sink-%s", period_num, track->stream_id);
-  len = strlen (internal_name);
+  tmpid = g_strdup_printf ("%s-period%d", track->id, period_num);
+  g_free (track->id);
+  track->id = tmpid;
+  len = strlen (track->id);
   for (i = 0; i < len; i++)
-    if (internal_name[i] == ' ')
-      internal_name[i] = '_';
-  track->sinkpad = gst_pad_new (internal_name, GST_PAD_SINK);
+    if (track->id[i] == ' ')
+      track->id[i] = '_';
+  track->element = gst_bin_new (track->id);
+
+  track->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
   g_signal_connect (track->sinkpad, "unlinked",
       (GCallback) track_sinkpad_unlinked_cb, track);
-  g_free (internal_name);
   gst_element_add_pad (GST_ELEMENT_CAST (track->element), track->sinkpad);
   gst_pad_set_element_private (track->sinkpad, track);
   gst_pad_set_chain_function (track->sinkpad, _track_sink_chain_function);
@@ -920,13 +926,15 @@ gst_adaptive_demux_track_new (GstAdaptiveDemux * demux,
   track->demux = demux;
   track->type = type;
   track->flags = flags;
-  track->stream_id = g_strdup (stream_id);
+  track->stream_id =
+      gst_element_decorate_stream_id (GST_ELEMENT (demux), stream_id);
+  track->id = g_strdup_printf ("track-%s", stream_id);
   track->period_num = (guint) (-1);
   track->generic_caps = caps;
-  track->stream_object = gst_stream_new (stream_id, caps, type, flags);
+  track->stream_object = gst_stream_new (track->stream_id, caps, type, flags);
   if (tags) {
-    track->tags = gst_tag_list_ref (tags);
     gst_stream_set_tags (track->stream_object, tags);
+    track->tags = tags;
   }
 
   track->selected = FALSE;
diff --git a/ext/adaptivedemux2/gstadaptivedemux.c b/ext/adaptivedemux2/gstadaptivedemux.c
index 817348d294824bba11e593f1e042e479aba759a0..ed991d787d015f4eeffb338c07589173560eec6e 100644
--- a/ext/adaptivedemux2/gstadaptivedemux.c
+++ b/ext/adaptivedemux2/gstadaptivedemux.c
@@ -129,10 +129,6 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
 #define DEFAULT_CURRENT_LEVEL_TIME_VIDEO 0
 #define DEFAULT_CURRENT_LEVEL_TIME_AUDIO 0
 
-#define GST_API_GET_LOCK(d) (&(GST_ADAPTIVE_DEMUX_CAST(d)->priv->api_lock))
-#define GST_API_LOCK(d)   g_mutex_lock (GST_API_GET_LOCK (d));
-#define GST_API_UNLOCK(d) g_mutex_unlock (GST_API_GET_LOCK (d));
-
 enum
 {
   PROP_0,
@@ -557,7 +553,6 @@ gst_adaptive_demux_init (GstAdaptiveDemux * demux,
 
   demux->priv->scheduler_task = gst_adaptive_demux_loop_new ();
 
-  g_mutex_init (&demux->priv->api_lock);
   g_mutex_init (&demux->priv->segment_lock);
 
   g_mutex_init (&demux->priv->tracks_lock);
@@ -622,7 +617,6 @@ gst_adaptive_demux_finalize (GObject * object)
   downloadhelper_free (demux->download_helper);
 
   g_rec_mutex_clear (&demux->priv->manifest_lock);
-  g_mutex_clear (&demux->priv->api_lock);
   g_mutex_clear (&demux->priv->segment_lock);
 
   g_mutex_clear (&demux->priv->buffering_lock);
@@ -691,18 +685,14 @@ gst_adaptive_demux_change_state (GstElement * element,
 
       gst_task_join (demux->priv->output_task);
 
-      GST_API_LOCK (demux);
       gst_adaptive_demux_reset (demux);
-      GST_API_UNLOCK (demux);
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      GST_API_LOCK (demux);
       gst_adaptive_demux_reset (demux);
 
       gst_adaptive_demux_loop_start (demux->priv->scheduler_task);
       if (g_atomic_int_get (&demux->priv->have_manifest))
         gst_adaptive_demux_start_manifest_update_task (demux);
-      GST_API_UNLOCK (demux);
       if (g_atomic_int_compare_and_exchange (&demux->running, FALSE, TRUE))
         GST_DEBUG_OBJECT (demux, "demuxer has started running");
       /* gst_task_start (demux->priv->output_task); */
@@ -745,18 +735,18 @@ gst_adaptive_demux_output_slot_free (GstAdaptiveDemux * demux,
   if (slot->pending_track)
     gst_adaptive_demux_track_unref (slot->pending_track);
 
-  g_slice_free (OutputSlot, slot);
+  g_free (slot);
 }
 
 static OutputSlot *
 gst_adaptive_demux_output_slot_new (GstAdaptiveDemux * demux,
-    GstStreamType streamtype)
+    GstAdaptiveDemuxTrack * track)
 {
   OutputSlot *slot;
   GstPadTemplate *tmpl;
   gchar *name;
 
-  switch (streamtype) {
+  switch (track->type) {
     case GST_STREAM_TYPE_AUDIO:
       name = g_strdup_printf ("audio_%02u", demux->priv->n_audio_streams++);
       tmpl =
@@ -779,8 +769,9 @@ gst_adaptive_demux_output_slot_new (GstAdaptiveDemux * demux,
       return NULL;
   }
 
-  slot = g_slice_new0 (OutputSlot);
-  slot->type = streamtype;
+  slot = g_new0 (OutputSlot, 1);
+  slot->type = track->type;
+  slot->track = gst_adaptive_demux_track_ref (track);
   slot->pushed_timed_data = FALSE;
 
   /* Create and activate new pads */
@@ -788,10 +779,6 @@ gst_adaptive_demux_output_slot_new (GstAdaptiveDemux * demux,
   g_free (name);
   gst_object_unref (tmpl);
 
-  gst_element_add_pad (GST_ELEMENT_CAST (demux), slot->pad);
-  gst_flow_combiner_add_pad (demux->priv->flowcombiner, slot->pad);
-  gst_pad_set_active (slot->pad, TRUE);
-
   gst_pad_set_query_function (slot->pad,
       GST_DEBUG_FUNCPTR (gst_adaptive_demux_src_query));
   gst_pad_set_event_function (slot->pad,
@@ -799,11 +786,50 @@ gst_adaptive_demux_output_slot_new (GstAdaptiveDemux * demux,
 
   gst_pad_set_element_private (slot->pad, slot);
 
+  gst_element_add_pad (GST_ELEMENT_CAST (demux), slot->pad);
+  gst_flow_combiner_add_pad (demux->priv->flowcombiner, slot->pad);
+  gst_pad_set_active (slot->pad, TRUE);
+
   GST_INFO_OBJECT (demux, "Created output slot %s:%s",
       GST_DEBUG_PAD_NAME (slot->pad));
   return slot;
 }
 
+static gboolean
+gst_adaptive_demux_scheduler_unblock_fragment_downloads_cb (GstAdaptiveDemux *
+    demux)
+{
+  GList *iter;
+
+  GST_INFO_OBJECT (demux, "Unblocking streams' fragment downloads");
+  demux->priv->streams_can_download_fragments = TRUE;
+
+  iter = demux->input_period->streams;
+
+  for (; iter; iter = g_list_next (iter)) {
+    GstAdaptiveDemux2Stream *stream = iter->data;
+    gst_adaptive_demux2_stream_on_can_download_fragments (stream);
+  }
+
+  return FALSE;
+}
+
+/* must be called with the scheduler lock */
+static void
+gst_adaptive_demux_set_streams_can_download_fragments (GstAdaptiveDemux * demux,
+    gboolean streams_can_download_fragments)
+{
+  if (streams_can_download_fragments) {
+    gst_adaptive_demux_loop_call (demux->priv->scheduler_task, (GSourceFunc)
+        gst_adaptive_demux_scheduler_unblock_fragment_downloads_cb, demux,
+        NULL);
+  } else {
+    demux->priv->streams_can_download_fragments =
+        streams_can_download_fragments;
+  }
+
+}
+
 /* Called:
  * * After `process_manifest` or when a period starts
  * * Or when all tracks have been created
@@ -908,6 +934,8 @@ gst_adaptive_demux_post_collection (GstAdaptiveDemux * demux)
   return TRUE;
 }
 
+/* Called from the sinkpad's input thread with
+ * the SCHEDULER lock held */
 static gboolean
 handle_incoming_manifest (GstAdaptiveDemux * demux)
 {
@@ -918,7 +946,6 @@ handle_incoming_manifest (GstAdaptiveDemux * demux)
   gsize available;
   GstBuffer *manifest_buffer;
 
-  GST_API_LOCK (demux);
   GST_MANIFEST_LOCK (demux);
 
   demux_class = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
@@ -953,6 +980,17 @@ handle_incoming_manifest (GstAdaptiveDemux * demux)
 
     GST_DEBUG_OBJECT (demux, "Fetched manifest at URI: %s (base: %s)",
         demux->manifest_uri, GST_STR_NULL (demux->manifest_base_uri));
+
+    if (!g_str_has_prefix (demux->manifest_uri, "data:")
+        && !g_str_has_prefix (demux->manifest_uri, "http://")
+        && !g_str_has_prefix (demux->manifest_uri, "https://")) {
+      GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
+          (_("Invalid manifest URI")),
+          ("Manifest URI needs to use either data:, http:// or https://"));
+      gst_query_unref (query);
+      ret = FALSE;
+      goto unlock_out;
+    }
   } else {
     GST_WARNING_OBJECT (demux, "Upstream URI query failed.");
   }
@@ -1018,14 +1056,15 @@ handle_incoming_manifest (GstAdaptiveDemux * demux)
       gst_adaptive_demux_post_collection (demux);
   TRACKS_UNLOCK (demux);
 
+  gst_adaptive_demux_set_streams_can_download_fragments (demux, FALSE);
   gst_adaptive_demux_prepare_streams (demux,
       gst_adaptive_demux_is_live (demux));
+  gst_adaptive_demux_set_streams_can_download_fragments (demux, TRUE);
   gst_adaptive_demux_start_tasks (demux);
   gst_adaptive_demux_start_manifest_update_task (demux);
 
 unlock_out:
   GST_MANIFEST_UNLOCK (demux);
-  GST_API_UNLOCK (demux);
 
   return ret;
 
@@ -1051,7 +1090,6 @@ no_streams:
 invalid_manifest:
   {
     GST_MANIFEST_UNLOCK (demux);
-    GST_API_UNLOCK (demux);
 
     /* In most cases, this will happen if we set a wrong url in the
      * source element and we have received the 404 HTML response instead of
@@ -1173,7 +1211,6 @@ gst_adaptive_demux_sink_event (GstPad * pad, GstObject * parent,
 
   switch (event->type) {
     case GST_EVENT_FLUSH_STOP:{
-      GST_API_LOCK (demux);
       GST_MANIFEST_LOCK (demux);
 
       gst_adaptive_demux_reset (demux);
@@ -1181,7 +1218,6 @@ gst_adaptive_demux_sink_event (GstPad * pad, GstObject * parent,
       ret = gst_pad_event_default (pad, parent, event);
 
       GST_MANIFEST_UNLOCK (demux);
-      GST_API_UNLOCK (demux);
 
       return ret;
     }
@@ -1378,38 +1414,6 @@ gst_adaptive_demux_reset (GstAdaptiveDemux * demux)
   gst_flow_combiner_reset (demux->priv->flowcombiner);
 }
 
-static gboolean
-gst_adaptive_demux_query (GstElement * element, GstQuery * query)
-{
-  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (element);
-
-  GST_LOG_OBJECT (demux, "%" GST_PTR_FORMAT, query);
-
-  switch (GST_QUERY_TYPE (query)) {
-    case GST_QUERY_BUFFERING:
-    {
-      GstFormat format;
-      gst_query_parse_buffering_range (query, &format, NULL, NULL, NULL);
-
-      if (!demux->output_period) {
-        if (format != GST_FORMAT_TIME) {
-          GST_DEBUG_OBJECT (demux,
-              "No period setup yet, can't answer non-TIME buffering queries");
-          return FALSE;
-        }
-
-        GST_DEBUG_OBJECT (demux,
-            "No period setup yet, but still answering buffering query");
-        return TRUE;
-      }
-    }
-    default:
-      break;
-  }
-
-  return GST_ELEMENT_CLASS (parent_class)->query (element, query);
-}
-
 static gboolean
 gst_adaptive_demux_send_event (GstElement * element, GstEvent * event)
 {
@@ -1630,8 +1634,12 @@ gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
 
   for (iter = new_streams; iter; iter = g_list_next (iter)) {
     GstAdaptiveDemux2Stream *stream = iter->data;
+    gboolean is_selected =
+        gst_adaptive_demux2_stream_is_selected_locked (stream);
 
-    GST_DEBUG_OBJECT (stream, "Preparing stream");
+    GST_DEBUG_OBJECT (stream,
+        "Preparing stream. Is selected: %d pending_tracks: %d", is_selected,
+        stream->pending_tracks);
 
     stream->need_header = TRUE;
     stream->discont = TRUE;
@@ -1640,13 +1648,25 @@ gst_adaptive_demux_prepare_streams (GstAdaptiveDemux * demux,
      * * If the stream is selected
      * * Or it provides dynamic tracks (in which case we need to force an update)
      */
-    if (first_and_live
-        && (gst_adaptive_demux2_stream_is_selected_locked (stream)
-            || stream->pending_tracks)) {
+    if (first_and_live && (is_selected || stream->pending_tracks)) {
       /* TODO we only need the first timestamp, maybe create a simple function to
        * get the current PTS of a fragment ? */
       GST_DEBUG_OBJECT (stream, "Calling update_fragment_info");
-      gst_adaptive_demux2_stream_update_fragment_info (stream);
+      GstFlowReturn flow_ret =
+          gst_adaptive_demux2_stream_update_fragment_info (stream);
+
+      /* Handle fragment info waiting on BUSY */
+      while (flow_ret == GST_ADAPTIVE_DEMUX_FLOW_BUSY) {
+        if (!gst_adaptive_demux2_stream_wait_prepared (stream))
+          break;
+        flow_ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
+      }
+
+      if (flow_ret != GST_FLOW_OK) {
+        GST_WARNING_OBJECT (stream, "Could not update fragment info. flow: %s",
+            gst_flow_get_name (flow_ret));
+        continue;
+      }
 
       GST_DEBUG_OBJECT (stream,
           "Got stream time %" GST_STIME_FORMAT,
@@ -1734,10 +1754,10 @@ demux_update_buffering_locked (GstAdaptiveDemux * demux)
   for (tmp = demux->output_period->tracks; tmp; tmp = tmp->next) {
     GstAdaptiveDemuxTrack *track = (GstAdaptiveDemuxTrack *) tmp->data;
 
-    GST_LOG_OBJECT (demux,
-        "Checking track '%s' (period %u) active:%d selected:%d eos:%d level:%"
+    GST_LOG_ID (track->id,
+        "Checking track active:%d selected:%d eos:%d level:%"
         GST_TIME_FORMAT " buffering_threshold:%" GST_TIME_FORMAT,
-        track->stream_id, track->period_num, track->active, track->selected,
+        track->active, track->selected,
         track->eos, GST_TIME_ARGS (track->level_time),
         GST_TIME_ARGS (track->buffering_threshold));
 
@@ -1835,7 +1855,7 @@ demux_post_buffering_locked (GstAdaptiveDemux * demux)
 }
 
 /* MANIFEST_LOCK and TRACKS_LOCK hold */
-GstAdaptiveDemux2Stream *
+static GstAdaptiveDemux2Stream *
 find_stream_for_track_locked (GstAdaptiveDemux * demux,
     GstAdaptiveDemuxTrack * track)
 {
@@ -1902,14 +1922,14 @@ gst_adaptive_demux_seek_to_input_period (GstAdaptiveDemux * demux)
     if (slot->pending_track != NULL) {
       GST_DEBUG_OBJECT (demux,
           "Removing track '%s' as pending from output of current track '%s'",
-          slot->pending_track->stream_id, slot->track->stream_id);
+          slot->pending_track->id, slot->track->id);
       gst_adaptive_demux_track_unref (slot->pending_track);
       slot->pending_track = NULL;
     }
   }
 }
 
-/* must be called with manifest_lock taken */
+/* must be called with scheduler lock taken */
 gboolean
 gst_adaptive_demux_get_live_seek_range (GstAdaptiveDemux * demux,
     gint64 * range_start, gint64 * range_stop)
@@ -1923,7 +1943,7 @@ gst_adaptive_demux_get_live_seek_range (GstAdaptiveDemux * demux,
   return klass->get_live_seek_range (demux, range_start, range_stop);
 }
 
-/* must be called with manifest_lock taken */
+/* must be called from scheduler task */
 gboolean
 gst_adaptive_demux2_stream_in_live_seek_range (GstAdaptiveDemux * demux,
     GstAdaptiveDemux2Stream * stream)
@@ -2000,19 +2020,16 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
   gint64 start, stop;
   guint32 seqnum;
   gboolean update;
-  gboolean ret;
+  gboolean ret = FALSE;
   GstSegment oldsegment;
   GstEvent *flush_event;
 
   GST_INFO_OBJECT (demux, "Received seek event");
 
-  GST_API_LOCK (demux);
-
   gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
       &stop_type, &stop);
 
   if (format != GST_FORMAT_TIME) {
-    GST_API_UNLOCK (demux);
     GST_WARNING_OBJECT (demux,
         "Adaptive demuxers only support TIME-based seeking");
     gst_event_unref (event);
@@ -2021,7 +2038,6 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
 
   if (flags & GST_SEEK_FLAG_SEGMENT) {
     GST_FIXME_OBJECT (demux, "Handle segment seeks");
-    GST_API_UNLOCK (demux);
     gst_event_unref (event);
     return FALSE;
   }
@@ -2050,9 +2066,7 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
       GST_ERROR_OBJECT (demux,
           "Instant rate change seeks only supported in the "
           "same direction, without flushing and position change");
-      GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-      GST_API_UNLOCK (demux);
-      return FALSE;
+      goto unlock_return;
     }
 
     rate_multiplier = rate / demux->segment.rate;
@@ -2068,32 +2082,17 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
       demux->instant_rate_multiplier = rate_multiplier;
       GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
     }
-
-    GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-    GST_API_UNLOCK (demux);
-    gst_event_unref (event);
-
-    return ret;
+    goto unlock_return;
   }
 
-  if (!gst_adaptive_demux_can_seek (demux)) {
-    GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-
-    GST_API_UNLOCK (demux);
-    gst_event_unref (event);
-    return FALSE;
-  }
+  if (!gst_adaptive_demux_can_seek (demux))
+    goto unlock_return;
 
   /* We can only accept flushing seeks from this point onward */
   if (!(flags & GST_SEEK_FLAG_FLUSH)) {
     GST_ERROR_OBJECT (demux,
         "Non-flushing non-instant-rate seeks are not possible");
-
-    GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-
-    GST_API_UNLOCK (demux);
-    gst_event_unref (event);
-    return FALSE;
+    goto unlock_return;
   }
 
   if (gst_adaptive_demux_is_live (demux)) {
@@ -2103,11 +2102,8 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
 
     if (!gst_adaptive_demux_get_live_seek_range (demux, &range_start,
             &range_stop)) {
-      GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-      GST_API_UNLOCK (demux);
-      gst_event_unref (event);
       GST_WARNING_OBJECT (demux, "Failure getting the live seek ranges");
-      return FALSE;
+      goto unlock_return;
     }
 
     GST_DEBUG_OBJECT (demux,
@@ -2169,12 +2165,8 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
     }
 
     /* If the seek position is still outside of the seekable range, refuse the seek */
-    if (!start_valid || !stop_valid) {
-      GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-      GST_API_UNLOCK (demux);
-      gst_event_unref (event);
-      return FALSE;
-    }
+    if (!start_valid || !stop_valid)
+      goto unlock_return;
 
     /* Re-create seek event with changed/updated values */
     if (changed) {
@@ -2202,72 +2194,97 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
 
   GST_ADAPTIVE_DEMUX_SEGMENT_LOCK (demux);
 
-  /*
-   * Handle snap seeks as follows:
-   * 1) do the snap seeking a (random) active stream
-   * 2) use the final position on this stream to seek
-   *    on the other streams to the same position
-   *
-   * We can't snap at all streams at the same time as they might end in
-   * different positions, so just pick one and align all others to that
-   * position.
-   */
-
-  GstAdaptiveDemux2Stream *stream = NULL;
-  GList *iter;
-  /* Pick a random active stream on which to do the stream seek */
-  for (iter = demux->output_period->streams; iter; iter = iter->next) {
-    GstAdaptiveDemux2Stream *cand = iter->data;
-    if (gst_adaptive_demux2_stream_is_selected_locked (cand)) {
-      stream = cand;
-      break;
-    }
+  if (!IS_SNAP_SEEK (flags) && !(flags & GST_SEEK_FLAG_ACCURATE)) {
+    /* If no accurate seeking was specified, we want to default to seeking to
+     * the previous segment for efficient/fast playback. */
+    flags |= GST_SEEK_FLAG_KEY_UNIT;
   }
 
-  if (stream && IS_SNAP_SEEK (flags)) {
-    GstClockTimeDiff ts;
-    GstSeekFlags stream_seek_flags = flags;
+  if (IS_SNAP_SEEK (flags)) {
+    GstAdaptiveDemux2Stream *default_stream = NULL;
+    GstAdaptiveDemux2Stream *stream = NULL;
+    GList *iter;
+    /*
+     * Handle snap seeks as follows:
+     * 1) do the snap seeking a (random) active stream
+     * 1.1) If none are active yet (early-seek), pick a random default one
+     * 2) use the final position on this stream to seek
+     *    on the other streams to the same position
+     *
+     * We can't snap at all streams at the same time as they might end in
+     * different positions, so just pick one and align all others to that
+     * position.
+     */
 
-    /* snap-seek on the chosen stream and then
-     * use the resulting position to seek on all streams */
-    if (rate >= 0) {
-      if (start_type != GST_SEEK_TYPE_NONE)
-        ts = start;
-      else {
-        ts = gst_segment_position_from_running_time (&demux->segment,
-            GST_FORMAT_TIME, demux->priv->global_output_position);
-        start_type = GST_SEEK_TYPE_SET;
+    /* Pick a random active stream on which to do the stream seek */
+    for (iter = demux->output_period->streams; iter; iter = iter->next) {
+      GstAdaptiveDemux2Stream *cand = iter->data;
+      if (gst_adaptive_demux2_stream_is_selected_locked (cand)) {
+        stream = cand;
+        break;
       }
-    } else {
-      if (stop_type != GST_SEEK_TYPE_NONE)
-        ts = stop;
-      else {
-        stop_type = GST_SEEK_TYPE_SET;
-        ts = gst_segment_position_from_running_time (&demux->segment,
-            GST_FORMAT_TIME, demux->priv->global_output_position);
+      if (default_stream == NULL
+          && gst_adaptive_demux2_stream_is_default_locked (cand))
+        default_stream = cand;
+    }
+
+    if (stream == NULL)
+      stream = default_stream;
+
+    if (stream) {
+      GstClockTimeDiff ts;
+      GstSeekFlags stream_seek_flags = flags;
+
+      /* snap-seek on the chosen stream and then
+       * use the resulting position to seek on all streams */
+      if (rate >= 0) {
+        if (start_type != GST_SEEK_TYPE_NONE)
+          ts = start;
+        else {
+          ts = gst_segment_position_from_running_time (&demux->segment,
+              GST_FORMAT_TIME, demux->priv->global_output_position);
+          start_type = GST_SEEK_TYPE_SET;
+        }
+      } else {
+        if (stop_type != GST_SEEK_TYPE_NONE)
+          ts = stop;
+        else {
+          stop_type = GST_SEEK_TYPE_SET;
+          ts = gst_segment_position_from_running_time (&demux->segment,
+              GST_FORMAT_TIME, demux->priv->global_output_position);
+        }
       }
-    }
 
-    if (gst_adaptive_demux2_stream_seek (stream, rate >= 0, stream_seek_flags,
-            ts, &ts) != GST_FLOW_OK) {
-      GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
+      GstFlowReturn flow_ret =
+          gst_adaptive_demux2_stream_seek (stream, rate >= 0, stream_seek_flags,
+          ts, &ts);
 
-      GST_API_UNLOCK (demux);
-      gst_event_unref (event);
-      return FALSE;
-    }
+      /* Handle fragment info waiting on BUSY */
+      while (flow_ret == GST_ADAPTIVE_DEMUX_FLOW_BUSY) {
+        if (!gst_adaptive_demux2_stream_wait_prepared (stream))
+          break;
+        flow_ret = gst_adaptive_demux2_stream_update_fragment_info (stream);
+      }
 
-    /* replace event with a new one without snapping to seek on all streams */
-    gst_event_unref (event);
-    if (rate >= 0) {
-      start = ts;
-    } else {
-      stop = ts;
+      if (flow_ret != GST_FLOW_OK) {
+        GST_DEBUG_OBJECT (demux,
+            "Seek on stream %" GST_PTR_FORMAT " failed with flow return %s",
+            stream, gst_flow_get_name (flow_ret));
+        GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
+        goto unlock_return;
+      }
+      /* replace event with a new one without snapping to seek on all streams */
+      gst_event_unref (event);
+      if (rate >= 0) {
+        start = ts;
+      } else {
+        stop = ts;
+      }
+      event =
+          gst_event_new_seek (rate, format, REMOVE_SNAP_FLAGS (flags),
+          start_type, start, stop_type, stop);
+      GST_DEBUG_OBJECT (demux, "Adapted snap seek to %" GST_PTR_FORMAT, event);
     }
-    event =
-        gst_event_new_seek (rate, format, REMOVE_SNAP_FLAGS (flags),
-        start_type, start, stop_type, stop);
-    GST_DEBUG_OBJECT (demux, "Adapted snap seek to %" GST_PTR_FORMAT, event);
   }
 
   ret = gst_segment_do_seek (&demux->segment, rate, format, flags, start_type,
@@ -2317,32 +2334,16 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux,
   GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
 
   /* Restart the demux */
+  gst_adaptive_demux_set_streams_can_download_fragments (demux, TRUE);
   gst_adaptive_demux_start_tasks (demux);
 
+unlock_return:
   GST_ADAPTIVE_SCHEDULER_UNLOCK (demux);
-  GST_API_UNLOCK (demux);
   gst_event_unref (event);
 
   return ret;
 }
 
-/* Returns TRUE if the stream has at least one selected track */
-static gboolean
-gst_adaptive_demux2_stream_has_selected_tracks (GstAdaptiveDemux2Stream *
-    stream)
-{
-  GList *tmp;
-
-  for (tmp = stream->tracks; tmp; tmp = tmp->next) {
-    GstAdaptiveDemuxTrack *track = tmp->data;
-
-    if (track->selected)
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
 static gboolean
 handle_stream_selection (GstAdaptiveDemux * demux, GList * streams,
     guint32 seqnum)
@@ -2411,7 +2412,7 @@ handle_stream_selection (GstAdaptiveDemux * demux, GList * streams,
 
     gboolean is_running = gst_adaptive_demux2_stream_is_running (stream);
     gboolean should_be_running =
-        gst_adaptive_demux2_stream_has_selected_tracks (stream);
+        gst_adaptive_demux2_stream_is_selected_locked (stream);
 
     if (!is_running && should_be_running) {
       GstClockTime output_running_ts = demux->priv->global_output_position;
@@ -2535,7 +2536,7 @@ gst_adaptive_demux_src_event (GstPad * pad, GstObject * parent,
       gst_event_parse_qos (event, NULL, NULL, &diff, &timestamp);
       /* Only take into account lateness if late */
       if (diff > 0)
-        earliest_time = timestamp + 2 * diff;
+        earliest_time = timestamp + MIN (2 * diff, GST_SECOND);
       else
         earliest_time = timestamp;
 
@@ -2560,6 +2561,54 @@ gst_adaptive_demux_src_event (GstPad * pad, GstObject * parent,
   return gst_pad_event_default (pad, parent, event);
 }
 
+static gboolean
+gst_adaptive_demux_handle_query_seeking (GstAdaptiveDemux * demux,
+    GstQuery * query)
+{
+  GstFormat fmt = GST_FORMAT_UNDEFINED;
+  gint64 stop = -1;
+  gint64 start = 0;
+  gboolean ret = FALSE;
+
+  if (!g_atomic_int_get (&demux->priv->have_manifest)) {
+    GST_INFO_OBJECT (demux,
+        "Don't have manifest yet, can't answer seeking query");
+    return FALSE;               /* can't answer without manifest */
+  }
+
+  GST_MANIFEST_LOCK (demux);
+
+  gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+  GST_INFO_OBJECT (demux, "Received GST_QUERY_SEEKING with format %d", fmt);
+  if (fmt == GST_FORMAT_TIME) {
+    GstClockTime duration;
+    gboolean can_seek = gst_adaptive_demux_can_seek (demux);
+
+    ret = TRUE;
+    if (can_seek) {
+      if (gst_adaptive_demux_is_live (demux)) {
+        ret = gst_adaptive_demux_get_live_seek_range (demux, &start, &stop);
+
+        if (!ret) {
+          GST_MANIFEST_UNLOCK (demux);
+          GST_INFO_OBJECT (demux, "can't answer seeking query");
+          return FALSE;
+        }
+      } else {
+        duration = demux->priv->duration;
+        if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0)
+          stop = duration;
+      }
+    }
+    gst_query_set_seeking (query, fmt, can_seek, start, stop);
+    GST_INFO_OBJECT (demux, "GST_QUERY_SEEKING returning with start : %"
+        GST_TIME_FORMAT ", stop : %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+  }
+  GST_MANIFEST_UNLOCK (demux);
+  return ret;
+}
+
 static gboolean
 gst_adaptive_demux_src_query (GstPad * pad, GstObject * parent,
     GstQuery * query)
@@ -2570,7 +2619,6 @@ gst_adaptive_demux_src_query (GstPad * pad, GstObject * parent,
   if (query == NULL)
     return FALSE;
 
-
   switch (query->type) {
     case GST_QUERY_DURATION:{
       GstFormat fmt;
@@ -2602,53 +2650,25 @@ gst_adaptive_demux_src_query (GstPad * pad, GstObject * parent,
           GST_TIME_FORMAT, ret ? "TRUE" : "FALSE", GST_TIME_ARGS (duration));
       break;
     }
+    case GST_QUERY_CAPS:
+    {
+      OutputSlot *slot = gst_pad_get_element_private (pad);
+      if (slot->track && slot->track->generic_caps) {
+        GST_DEBUG_OBJECT (demux, "Answering caps query %" GST_PTR_FORMAT,
+            slot->track->generic_caps);
+        gst_query_set_caps_result (query, slot->track->generic_caps);
+        ret = TRUE;
+      }
+      break;
+    }
     case GST_QUERY_LATENCY:{
       gst_query_set_latency (query, FALSE, 0, -1);
       ret = TRUE;
       break;
     }
-    case GST_QUERY_SEEKING:{
-      GstFormat fmt;
-      gint64 stop = -1;
-      gint64 start = 0;
-
-      if (!g_atomic_int_get (&demux->priv->have_manifest)) {
-        GST_INFO_OBJECT (demux,
-            "Don't have manifest yet, can't answer seeking query");
-        return FALSE;           /* can't answer without manifest */
-      }
-
-      GST_MANIFEST_LOCK (demux);
-
-      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
-      GST_INFO_OBJECT (demux, "Received GST_QUERY_SEEKING with format %d", fmt);
-      if (fmt == GST_FORMAT_TIME) {
-        GstClockTime duration;
-        gboolean can_seek = gst_adaptive_demux_can_seek (demux);
-
-        ret = TRUE;
-        if (can_seek) {
-          if (gst_adaptive_demux_is_live (demux)) {
-            ret = gst_adaptive_demux_get_live_seek_range (demux, &start, &stop);
-            if (!ret) {
-              GST_MANIFEST_UNLOCK (demux);
-              GST_INFO_OBJECT (demux, "can't answer seeking query");
-              return FALSE;
-            }
-          } else {
-            duration = demux->priv->duration;
-            if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0)
-              stop = duration;
-          }
-        }
-        gst_query_set_seeking (query, fmt, can_seek, start, stop);
-        GST_INFO_OBJECT (demux, "GST_QUERY_SEEKING returning with start : %"
-            GST_TIME_FORMAT ", stop : %" GST_TIME_FORMAT,
-            GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
-      }
-      GST_MANIFEST_UNLOCK (demux);
+    case GST_QUERY_SEEKING:
+      ret = gst_adaptive_demux_handle_query_seeking (demux, query);
       break;
-    }
     case GST_QUERY_URI:
 
       GST_MANIFEST_LOCK (demux);
@@ -2678,6 +2698,45 @@ gst_adaptive_demux_src_query (GstPad * pad, GstObject * parent,
   return ret;
 }
 
+static gboolean
+gst_adaptive_demux_query (GstElement * element, GstQuery * query)
+{
+  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (element);
+
+  GST_LOG_OBJECT (demux, "%" GST_PTR_FORMAT, query);
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_BUFFERING:
+    {
+      GstFormat format;
+      gst_query_parse_buffering_range (query, &format, NULL, NULL, NULL);
+
+      if (!demux->output_period) {
+        if (format != GST_FORMAT_TIME) {
+          GST_DEBUG_OBJECT (demux,
+              "No period setup yet, can't answer non-TIME buffering queries");
+          return FALSE;
+        }
+
+        GST_DEBUG_OBJECT (demux,
+            "No period setup yet, but still answering buffering query");
+        return TRUE;
+      }
+      break;
+    }
+    case GST_QUERY_SEEKING:
+    {
+      /* Source pads might not be present early on which would cause the default
+       * element query handler to fail, yet we can answer this query */
+      return gst_adaptive_demux_handle_query_seeking (demux, query);
+    }
+    default:
+      break;
+  }
+
+  return GST_ELEMENT_CLASS (parent_class)->query (element, query);
+}
+
 gboolean
 gst_adaptive_demux_handle_lost_sync (GstAdaptiveDemux * demux)
 {
@@ -2718,7 +2777,7 @@ gst_adaptive_demux_scheduler_start_cb (GstAdaptiveDemux * demux)
   return FALSE;
 }
 
-/* must be called with manifest_lock taken */
+/* must be called with the scheduler lock */
 static void
 gst_adaptive_demux_start_tasks (GstAdaptiveDemux * demux)
 {
@@ -2743,6 +2802,7 @@ static void
 gst_adaptive_demux_stop_manifest_update_task (GstAdaptiveDemux * demux)
 {
   GST_DEBUG_OBJECT (demux, "requesting stop of the manifest update task");
+  demux->priv->manifest_updates_enabled = FALSE;
   if (demux->priv->manifest_updates_cb != 0) {
     gst_adaptive_demux_loop_cancel_call (demux->priv->scheduler_task,
         demux->priv->manifest_updates_cb);
@@ -2757,6 +2817,12 @@ static void
 gst_adaptive_demux_start_manifest_update_task (GstAdaptiveDemux * demux)
 {
   GstAdaptiveDemuxClass *demux_class = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
+  demux->priv->manifest_updates_enabled = TRUE;
+
+  if (demux->priv->need_manual_manifest_update) {
+    gst_adaptive_demux2_manual_manifest_update (demux);
+    demux->priv->need_manual_manifest_update = FALSE;
+  }
 
   if (gst_adaptive_demux_is_live (demux)) {
     /* Task to periodically update the manifest */
@@ -2834,6 +2900,7 @@ gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream,
 }
 
 /* must be called with manifest_lock taken */
+/* @tags: transfer full */
 void
 gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream,
     GstTagList * tags)
@@ -2903,6 +2970,12 @@ gst_adaptive_demux_manifest_update_cb (GstAdaptiveDemux * demux)
       }
       demux->priv->stream_waiting_for_manifest = FALSE;
     }
+  } else if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC) {
+    schedule_again = FALSE;
+    gst_adaptive_demux_handle_lost_sync (demux);
+  } else if (ret == GST_ADAPTIVE_DEMUX_FLOW_BUSY) {
+    /* This is not an error, we'll just try again later */
+    GST_LOG_OBJECT (demux, "Manifest update returned BUSY / ongoing");
   } else {
     demux->priv->update_failed_count++;
 
@@ -2917,9 +2990,6 @@ gst_adaptive_demux_manifest_update_cb (GstAdaptiveDemux * demux)
     }
   }
 
-  if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC)
-    gst_adaptive_demux_handle_lost_sync (demux);
-
   if (schedule_again) {
     GstAdaptiveDemuxClass *klass = GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
 
@@ -3081,7 +3151,7 @@ check_and_handle_selection_update_locked (GstAdaptiveDemux * demux)
     if (slot->pending_track != NULL && !slot->pending_track->selected) {
       GST_DEBUG_OBJECT (demux,
           "Removing deselected track '%s' as pending from output of current track '%s'",
-          slot->pending_track->stream_id, slot->track->stream_id);
+          slot->pending_track->id, slot->track->id);
       gst_adaptive_demux_track_unref (slot->pending_track);
       slot->pending_track = NULL;
     }
@@ -3097,7 +3167,7 @@ check_and_handle_selection_update_locked (GstAdaptiveDemux * demux)
       /* 0. Track is selected and has a slot. Nothing to do */
       if (slot) {
         GST_DEBUG_OBJECT (demux, "Track '%s' is already being outputted",
-            track->stream_id);
+            track->id);
         continue;
       }
 
@@ -3108,21 +3178,18 @@ check_and_handle_selection_update_locked (GstAdaptiveDemux * demux)
         g_assert (slot->pending_track == NULL || slot->pending_track == track);
         if (slot->pending_track == NULL) {
           slot->pending_track = gst_adaptive_demux_track_ref (track);
-          GST_DEBUG_OBJECT (demux,
-              "Track '%s' (period %u) will be used on output of track '%s' (period %u)",
-              track->stream_id, track->period_num,
-              slot->track->stream_id, slot->track->period_num);
+          GST_DEBUG_ID (track->id,
+              "Track will be used on output of track '%s' (period %u)",
+              slot->track->id, slot->track->period_num);
         }
       } else {
         /* 2. There is no compatible replacement slot, create a new one */
-        slot = gst_adaptive_demux_output_slot_new (demux, track->type);
-        GST_DEBUG_OBJECT (demux, "Created slot for track '%s'",
-            track->stream_id);
+        slot = gst_adaptive_demux_output_slot_new (demux, track);
+        GST_DEBUG_OBJECT (demux, "Created slot for track '%s'", track->id);
         demux->priv->outputs = g_list_append (demux->priv->outputs, slot);
 
         track->update_next_segment = TRUE;
 
-        slot->track = gst_adaptive_demux_track_ref (track);
         track->active = TRUE;
         gst_adaptive_demux_send_initial_events (demux, slot);
       }
@@ -3141,7 +3208,7 @@ check_and_handle_selection_update_locked (GstAdaptiveDemux * demux)
       GstAdaptiveDemux2Stream *stream;
 
       GST_DEBUG_OBJECT (demux, "Output for track '%s' is no longer used",
-          slot->track->stream_id);
+          slot->track->id);
       slot->track->active = FALSE;
 
       /* If the stream feeding this track is stopped, flush and clear
@@ -3237,7 +3304,7 @@ handle_slot_pending_track_switch_locked (GstAdaptiveDemux * demux,
   if (!pending_is_ready && gst_queue_array_get_length (track->queue) > 0) {
     GST_DEBUG_OBJECT (demux,
         "Replacement track '%s' doesn't have enough data for switching yet",
-        slot->pending_track->stream_id);
+        slot->pending_track->id);
     return;
   }
 
@@ -3350,9 +3417,8 @@ restart:
       gst_adaptive_demux_track_update_next_position (track);
     }
 
-    GST_TRACE_OBJECT (demux,
-        "Looking at track %s (period %u). next_position %" GST_STIME_FORMAT,
-        track->stream_id, track->period_num,
+    GST_TRACE_ID (track->id,
+        "Looking at track, next_position %" GST_STIME_FORMAT,
         GST_STIME_ARGS (track->next_position));
 
     if (track->next_position != GST_CLOCK_STIME_NONE) {
@@ -3364,14 +3430,11 @@ restart:
       track->waiting_add = FALSE;
       all_tracks_empty = FALSE;
     } else if (!track->eos) {
-      GST_DEBUG_OBJECT (demux, "Need timed data on track %s (period %u)",
-          track->stream_id, track->period_num);
+      GST_DEBUG_ID (track->id, "Need timed data");
       all_tracks_empty = FALSE;
       wait_for_data = track->waiting_add = TRUE;
     } else {
-      GST_DEBUG_OBJECT (demux,
-          "Track %s (period %u) is EOS, not waiting for timed data",
-          track->stream_id, track->period_num);
+      GST_DEBUG_ID (track->id, "Track is EOS, not waiting for timed data");
 
       if (gst_queue_array_get_length (track->queue) > 0) {
         all_tracks_empty = FALSE;
@@ -3427,13 +3490,13 @@ restart:
         || ((track->next_position != GST_CLOCK_STIME_NONE)
             && track->next_position <= global_output_position)
         || ((track->next_position == GST_CLOCK_STIME_NONE) && track->eos)) {
-      GstMiniObject *mo = track_dequeue_data_locked (demux, track, TRUE);
+      GstMiniObject *mo =
+          gst_adaptive_demux_track_dequeue_data_locked (demux, track, TRUE);
 
       if (!mo) {
-        GST_DEBUG_OBJECT (demux,
-            "Track '%s' (period %u) doesn't have any pending data (eos:%d pushed_timed_data:%d)",
-            track->stream_id, track->period_num, track->eos,
-            slot->pushed_timed_data);
+        GST_DEBUG_ID (track->id,
+            "Track doesn't have any pending data (eos:%d pushed_timed_data:%d)",
+            track->eos, slot->pushed_timed_data);
         /* This should only happen if the track is EOS, or exactly in between
          * the parser outputting segment/caps before buffers. */
         g_assert (track->eos || !slot->pushed_timed_data);
@@ -3441,9 +3504,8 @@ restart:
         /* If we drained the track, but there's a pending track on the slot
          * loop again to activate it */
         if (slot->pending_track) {
-          GST_DEBUG_OBJECT (demux,
-              "Track '%s' (period %u) drained, but has a pending track to activate",
-              track->stream_id, track->period_num);
+          GST_DEBUG_ID (track->id,
+              "Track drained, but has a pending track to activate");
           goto restart;
         }
         break;
@@ -3453,9 +3515,7 @@ restart:
       demux_post_buffering_locked (demux);
       TRACKS_UNLOCK (demux);
 
-      GST_DEBUG_OBJECT (demux,
-          "Track '%s' (period %u) dequeued %" GST_PTR_FORMAT, track->stream_id,
-          track->period_num, mo);
+      GST_DEBUG_ID (track->id, "Track dequeued %" GST_PTR_FORMAT, mo);
 
       if (GST_IS_EVENT (mo)) {
         GstEvent *event = (GstEvent *) mo;
@@ -3464,9 +3524,7 @@ restart:
         } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
           /* If there is a pending next period, don't send the EOS */
           if (demux->output_period->has_next_period) {
-            GST_LOG_OBJECT (demux,
-                "Dropping EOS on track '%s' (period %u) before next period",
-                track->stream_id, track->period_num);
+            GST_LOG_OBJECT (track->id, "Dropping EOS before next period");
             gst_event_store_mark_delivered (&track->sticky_events, event);
             gst_event_unref (event);
             event = NULL;
@@ -3489,8 +3547,7 @@ restart:
           if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
             buffer = gst_buffer_make_writable (buffer);
             GST_DEBUG_OBJECT (slot->pad,
-                "track %s marking discont %" GST_PTR_FORMAT, track->stream_id,
-                buffer);
+                "track %s marking discont %" GST_PTR_FORMAT, track->id, buffer);
             GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
           }
           track->output_discont = FALSE;
@@ -3500,8 +3557,8 @@ restart:
             gst_flow_combiner_update_pad_flow (demux->priv->flowcombiner,
             slot->pad, slot->flow_ret);
         GST_DEBUG_OBJECT (slot->pad,
-            "track %s (period %u) push returned %s (combined %s)",
-            track->stream_id, track->period_num,
+            "track %s push returned %s (combined %s)",
+            track->id,
             gst_flow_get_name (slot->flow_ret), gst_flow_get_name (ret));
         slot->pushed_timed_data = TRUE;
       } else {
@@ -3517,7 +3574,7 @@ restart:
   }
 
   /* Store global output position */
-  if (global_output_position != GST_CLOCK_STIME_NONE) {
+  if (global_output_position >= 0) {
     demux->priv->global_output_position = global_output_position;
 
     /* And see if any streams need to be woken for more input */
@@ -3584,6 +3641,12 @@ gst_adaptive_demux_is_live (GstAdaptiveDemux * demux)
   return FALSE;
 }
 
+const gchar *
+gst_adaptive_demux_get_manifest_ref_uri (GstAdaptiveDemux * d)
+{
+  return d->manifest_base_uri ? d->manifest_base_uri : d->manifest_uri;
+}
+
 static void
 handle_manifest_download_complete (DownloadRequest * request,
     DownloadRequestState state, GstAdaptiveDemux * demux)
@@ -3692,6 +3755,35 @@ gst_adaptive_demux_update_manifest (GstAdaptiveDemux * demux)
   return ret;
 }
 
+static gboolean
+gst_adaptive_demux2_manual_manifest_update_cb (GstAdaptiveDemux * demux)
+{
+  GST_MANIFEST_LOCK (demux);
+  gst_adaptive_demux_update_manifest (demux);
+  GST_MANIFEST_UNLOCK (demux);
+
+  return G_SOURCE_REMOVE;
+}
+
+/* called by a subclass that needs a callback to 'update_manifest'
+ * done with with MANIFEST_LOCK held */
+void
+gst_adaptive_demux2_manual_manifest_update (GstAdaptiveDemux * demux)
+{
+  if (demux->priv->manifest_updates_cb != 0)
+    return;                     /* Callback already pending */
+
+  if (!demux->priv->manifest_updates_enabled) {
+    GST_LOG_OBJECT (demux, "Marking manual manifest update pending");
+    demux->priv->need_manual_manifest_update = TRUE;
+    return;
+  }
+
+  demux->priv->manifest_updates_cb =
+      gst_adaptive_demux_loop_call (demux->priv->scheduler_task,
+      (GSourceFunc) gst_adaptive_demux2_manual_manifest_update_cb, demux, NULL);
+}
+
 /* must be called with manifest_lock taken */
 gboolean
 gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux)
@@ -3705,7 +3797,7 @@ gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux)
   return ret;
 }
 
-/* must be called with manifest_lock taken */
+/* must be called from the scheduler task */
 void
 gst_adaptive_demux_advance_period (GstAdaptiveDemux * demux)
 {
@@ -3811,20 +3903,14 @@ gst_adaptive_demux2_add_stream (GstAdaptiveDemux * demux,
     return FALSE;
   }
   stream->demux = demux;
-  stream->period = demux->input_period;
-  demux->input_period->streams =
-      g_list_append (demux->input_period->streams, stream);
 
-  if (stream->tracks) {
-    GList *iter;
-    for (iter = stream->tracks; iter; iter = iter->next)
-      if (!gst_adaptive_demux_period_add_track (demux->input_period,
-              (GstAdaptiveDemuxTrack *) iter->data)) {
-        GST_ERROR_OBJECT (demux, "Failed to add track elements");
-        TRACKS_UNLOCK (demux);
-        return FALSE;
-      }
+  /* Takes ownership of the stream and adds the tracks */
+  if (!gst_adaptive_demux_period_add_stream (demux->input_period, stream)) {
+    GST_ERROR_OBJECT (demux, "Failed to add stream to period");
+    TRACKS_UNLOCK (demux);
+    return FALSE;
   }
+
   TRACKS_UNLOCK (demux);
   return TRUE;
 }
@@ -3839,3 +3925,9 @@ gst_adaptive_demux_play_rate (GstAdaptiveDemux * demux)
   GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK (demux);
   return rate;
 }
+
+GstAdaptiveDemuxLoop *
+gst_adaptive_demux_get_loop (GstAdaptiveDemux * demux)
+{
+  return gst_adaptive_demux_loop_ref (demux->priv->scheduler_task);
+}
diff --git a/ext/adaptivedemux2/gstadaptivedemux.h b/ext/adaptivedemux2/gstadaptivedemux.h
index d1ad49ade398bae25c586b66d1d3830b946451b4..e74069908170ade34a3478c62ef13b4c729553f5 100644
--- a/ext/adaptivedemux2/gstadaptivedemux.h
+++ b/ext/adaptivedemux2/gstadaptivedemux.h
@@ -85,6 +85,10 @@ G_BEGIN_DECLS
 /* The live stream has lost synchronization and the demuxer needs to be resetted */
 #define GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_2 + 1
 
+/* The stream sub-class is busy and can't supply information for
+ * ::update_fragment_info() right now */
+#define GST_ADAPTIVE_DEMUX_FLOW_BUSY (GST_FLOW_CUSTOM_SUCCESS_2 + 3)
+
 typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate;
 
 struct _GstAdaptiveDemuxTrack
@@ -100,6 +104,9 @@ struct _GstAdaptiveDemuxTrack
   /* Stream flags */
   GstStreamFlags flags;
 
+  /* Unique identifier (for naming and debugging) */
+  gchar *id;
+
   /* Unique identifier */
   gchar *stream_id;
 
@@ -457,6 +464,7 @@ GstAdaptiveDemuxTrack *gst_adaptive_demux_track_new (GstAdaptiveDemux *demux,
 GstAdaptiveDemuxTrack *gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack *track);
 void                   gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack *track);
 
+const gchar *gst_adaptive_demux_get_manifest_ref_uri (GstAdaptiveDemux * demux);
 
 gboolean gst_adaptive_demux_start_new_period (GstAdaptiveDemux * demux);
 
@@ -471,6 +479,9 @@ GstCaps * gst_codec_utils_caps_from_iso_rfc6831 (gchar * codec);
 
 gdouble gst_adaptive_demux_play_rate (GstAdaptiveDemux *demux);
 
+void gst_adaptive_demux2_manual_manifest_update (GstAdaptiveDemux * demux);
+GstAdaptiveDemuxLoop *gst_adaptive_demux_get_loop (GstAdaptiveDemux *demux);
+
 G_END_DECLS
 
 #endif
diff --git a/ext/adaptivedemux2/gstadaptivedemuxelement.c b/ext/adaptivedemux2/gstadaptivedemuxelement.c
index 1d8f53769c55153c60c4faca287b58784f330725..79f980080988f828fb5fb3e054e35b895d0af124 100644
--- a/ext/adaptivedemux2/gstadaptivedemuxelement.c
+++ b/ext/adaptivedemux2/gstadaptivedemuxelement.c
@@ -36,7 +36,7 @@ adaptivedemux2_base_element_init (GstPlugin * plugin)
         "adaptivedemux2");
     g_once_init_leave (&res, TRUE);
   }
-#ifndef STATIC_SOUP
+#ifndef LINK_SOUP
   if (!gst_soup_load_library ()) {
     GST_WARNING ("Failed to load libsoup library");
     return FALSE;
diff --git a/ext/adaptivedemux2/gstadaptivedemuxutils.c b/ext/adaptivedemux2/gstadaptivedemuxutils.c
index f9598194e28d1be062d1901dee05bb9b7a24dd32..b127d10f1224b6614dc4ba286164e6cdabd35525 100644
--- a/ext/adaptivedemux2/gstadaptivedemuxutils.c
+++ b/ext/adaptivedemux2/gstadaptivedemuxutils.c
@@ -59,7 +59,7 @@ struct _GstAdaptiveDemuxLoop
 GstAdaptiveDemuxClock *
 gst_adaptive_demux_clock_new (void)
 {
-  GstAdaptiveDemuxClock *clock = g_slice_new0 (GstAdaptiveDemuxClock);
+  GstAdaptiveDemuxClock *clock = g_new0 (GstAdaptiveDemuxClock, 1);
   GstClockType clock_type = GST_CLOCK_TYPE_OTHER;
   GObjectClass *gobject_class;
 
@@ -102,7 +102,7 @@ gst_adaptive_demux_clock_unref (GstAdaptiveDemuxClock * clock)
   g_return_if_fail (clock != NULL);
   if (g_atomic_int_dec_and_test (&clock->ref_count)) {
     gst_object_unref (clock->gst_clock);
-    g_slice_free (GstAdaptiveDemuxClock, clock);
+    g_free (clock);
   }
 }
 
@@ -152,7 +152,7 @@ gst_adaptive_demux_clock_set_utc_time (GstAdaptiveDemuxClock * clock,
 GstAdaptiveDemuxLoop *
 gst_adaptive_demux_loop_new (void)
 {
-  GstAdaptiveDemuxLoop *loop = g_slice_new0 (GstAdaptiveDemuxLoop);
+  GstAdaptiveDemuxLoop *loop = g_new0 (GstAdaptiveDemuxLoop, 1);
   g_atomic_int_set (&loop->ref_count, 1);
 
   g_mutex_init (&loop->lock);
@@ -184,7 +184,7 @@ gst_adaptive_demux_loop_unref (GstAdaptiveDemuxLoop * loop)
     g_rec_mutex_clear (&loop->context_lock);
     g_cond_clear (&loop->cond);
 
-    g_slice_free (GstAdaptiveDemuxLoop, loop);
+    g_free (loop);
   }
 }
 
@@ -310,6 +310,9 @@ gst_adaptive_demux_loop_pause_and_lock (GstAdaptiveDemuxLoop * loop)
 
     g_rec_mutex_lock (&loop->context_lock);
   }
+
+  if (!loop->context)
+    return FALSE;
   g_main_context_push_thread_default (loop->context);
 
   return TRUE;
@@ -320,7 +323,8 @@ gst_adaptive_demux_loop_unlock_and_unpause (GstAdaptiveDemuxLoop * loop)
 {
   gboolean stopped;
 
-  g_main_context_pop_thread_default (loop->context);
+  if (loop->context)
+    g_main_context_pop_thread_default (loop->context);
   g_rec_mutex_unlock (&loop->context_lock);
 
   g_mutex_lock (&loop->lock);
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c b/ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c
new file mode 100644
index 0000000000000000000000000000000000000000..e5da0ba371e7f00ca108a925dd1285ad6fd6a7de
--- /dev/null
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.c
@@ -0,0 +1,821 @@
+/* GStreamer
+ *
+ * Copyright (C) 2014 Samsung Electronics. All rights reserved.
+ *   Author: Thiago Santos <thiagoss@osg.samsung.com>
+ *
+ * Copyright (C) 2021-2022 Centricular Ltd
+ *   Author: Edward Hervey <edward@centricular.com>
+ *   Author: Jan Schmidt <jan@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gsthlsdemux.h"
+#include "gsthlsdemux-playlist-loader.h"
+#include "m3u8.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_hls_demux2_debug);
+#define GST_CAT_DEFAULT gst_hls_demux2_debug
+
+#define MAX_DOWNLOAD_ERROR_COUNT 3
+
+typedef enum _PlaylistLoaderState PlaylistLoaderState;
+
+enum _PlaylistLoaderState
+{
+  PLAYLIST_LOADER_STATE_STOPPED = 0,
+  PLAYLIST_LOADER_STATE_STARTING,
+  PLAYLIST_LOADER_STATE_LOADING,
+  PLAYLIST_LOADER_STATE_WAITING,
+};
+
+struct _GstHLSDemuxPlaylistLoaderPrivate
+{
+  GstAdaptiveDemux *demux;
+
+  GstHLSDemuxPlaylistLoaderSuccessCallback success_cb;
+  GstHLSDemuxPlaylistLoaderErrorCallback error_cb;
+  gpointer userdata;
+
+  GstAdaptiveDemuxLoop *scheduler_task;
+  DownloadHelper *download_helper;
+  DownloadRequest *download_request;
+
+  PlaylistLoaderState state;
+  guint pending_cb_id;
+
+  gchar *base_uri;
+  gchar *target_playlist_uri;
+
+  gchar *loading_playlist_uri;
+
+  gboolean delta_merge_failed;
+  gchar *current_playlist_uri;
+  GstHLSMediaPlaylist *current_playlist;
+
+  gchar *current_playlist_redirect_uri;
+
+  guint download_error_count;
+};
+
+#define gst_hls_demux_playlist_loader_parent_class parent_class
+G_DEFINE_TYPE_WITH_PRIVATE (GstHLSDemuxPlaylistLoader,
+    gst_hls_demux_playlist_loader, GST_TYPE_OBJECT);
+
+static void gst_hls_demux_playlist_loader_finalize (GObject * object);
+static gboolean gst_hls_demux_playlist_loader_update (GstHLSDemuxPlaylistLoader
+    * pl);
+static void start_playlist_download (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderPrivate * priv);
+
+/* Takes ownership of the loop ref */
+GstHLSDemuxPlaylistLoader *
+gst_hls_demux_playlist_loader_new (GstAdaptiveDemux * demux,
+    DownloadHelper * download_helper)
+{
+  GstHLSDemuxPlaylistLoader *pl =
+      g_object_new (GST_TYPE_HLS_DEMUX_PLAYLIST_LOADER, NULL);
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  priv->demux = demux;
+  priv->scheduler_task = gst_adaptive_demux_get_loop (demux);
+  priv->download_helper = download_helper;
+
+  return pl;
+}
+
+void
+gst_hls_demux_playlist_loader_set_callbacks (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderSuccessCallback success_cb,
+    GstHLSDemuxPlaylistLoaderErrorCallback error_cb, gpointer userdata)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  priv->success_cb = success_cb;
+  priv->error_cb = error_cb;
+  priv->userdata = userdata;
+}
+
+static void
+gst_hls_demux_playlist_loader_class_init (GstHLSDemuxPlaylistLoaderClass *
+    klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = gst_hls_demux_playlist_loader_finalize;
+}
+
+static void
+gst_hls_demux_playlist_loader_init (GstHLSDemuxPlaylistLoader * pl)
+{
+  pl->priv = gst_hls_demux_playlist_loader_get_instance_private (pl);
+}
+
+static void
+gst_hls_demux_playlist_loader_finalize (GObject * object)
+{
+  GstHLSDemuxPlaylistLoader *pl = GST_HLS_DEMUX_PLAYLIST_LOADER (object);
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  if (priv->pending_cb_id != 0) {
+    gst_adaptive_demux_loop_cancel_call (priv->scheduler_task,
+        priv->pending_cb_id);
+    priv->pending_cb_id = 0;
+  }
+
+  if (priv->download_request) {
+    downloadhelper_cancel_request (priv->download_helper,
+        priv->download_request);
+    download_request_unref (priv->download_request);
+    priv->download_request = NULL;
+  }
+
+  if (priv->scheduler_task)
+    gst_adaptive_demux_loop_unref (priv->scheduler_task);
+
+  g_free (priv->base_uri);
+  g_free (priv->target_playlist_uri);
+  g_free (priv->loading_playlist_uri);
+
+  if (priv->current_playlist)
+    gst_hls_media_playlist_unref (priv->current_playlist);
+  g_free (priv->current_playlist_uri);
+  g_free (priv->current_playlist_redirect_uri);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+schedule_state_update (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderPrivate * priv)
+{
+  g_assert (priv->pending_cb_id == 0);
+  priv->pending_cb_id =
+      gst_adaptive_demux_loop_call (priv->scheduler_task,
+      (GSourceFunc) gst_hls_demux_playlist_loader_update,
+      gst_object_ref (pl), (GDestroyNotify) gst_object_unref);
+}
+
+static void
+schedule_next_playlist_load (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderPrivate * priv, GstClockTime next_load_interval)
+{
+  /* If we have a valid request time, compute a more accurate download time for
+   * the playlist.
+   *
+   * This better takes into account the time it took to actually get and process
+   * the current playlist.
+   */
+  if (priv->current_playlist
+      && GST_CLOCK_TIME_IS_VALID (priv->current_playlist->request_time)) {
+    GstClockTime now = gst_adaptive_demux2_get_monotonic_time (priv->demux);
+    GstClockTimeDiff load_diff = GST_CLOCK_DIFF (now,
+        priv->current_playlist->request_time + next_load_interval);
+    GST_LOG_OBJECT (pl,
+        "now %" GST_TIME_FORMAT " request_time %" GST_TIME_FORMAT
+        " next_load_interval %" GST_TIME_FORMAT, GST_TIME_ARGS (now),
+        GST_TIME_ARGS (priv->current_playlist->request_time),
+        GST_TIME_ARGS (next_load_interval));
+    if (load_diff < 0) {
+      GST_LOG_OBJECT (pl, "Playlist update already late by %" GST_STIME_FORMAT,
+          GST_STIME_ARGS (load_diff));
+    };
+    next_load_interval = MAX (0, load_diff);
+  }
+
+  GST_LOG_OBJECT (pl, "Scheduling next playlist reload in %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (next_load_interval));
+  g_assert (priv->pending_cb_id == 0);
+  priv->state = PLAYLIST_LOADER_STATE_WAITING;
+  priv->pending_cb_id =
+      gst_adaptive_demux_loop_call_delayed (priv->scheduler_task,
+      next_load_interval,
+      (GSourceFunc) gst_hls_demux_playlist_loader_update,
+      gst_object_ref (pl), (GDestroyNotify) gst_object_unref);
+}
+
+void
+gst_hls_demux_playlist_loader_start (GstHLSDemuxPlaylistLoader * pl)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  if (priv->state != PLAYLIST_LOADER_STATE_STOPPED) {
+    GST_LOG_OBJECT (pl, "Already started - state %d", priv->state);
+    return;                     /* Already active */
+  }
+
+  GST_DEBUG_OBJECT (pl, "Starting playlist loading");
+  priv->state = PLAYLIST_LOADER_STATE_STARTING;
+  schedule_state_update (pl, priv);
+}
+
+void
+gst_hls_demux_playlist_loader_stop (GstHLSDemuxPlaylistLoader * pl)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  if (priv->state == PLAYLIST_LOADER_STATE_STOPPED)
+    return;                     /* Not runnning */
+
+  GST_DEBUG_OBJECT (pl, "Stopping playlist loading");
+
+  if (priv->pending_cb_id != 0) {
+    gst_adaptive_demux_loop_cancel_call (priv->scheduler_task,
+        priv->pending_cb_id);
+    priv->pending_cb_id = 0;
+  }
+
+  if (priv->download_request) {
+    downloadhelper_cancel_request (priv->download_helper,
+        priv->download_request);
+    download_request_unref (priv->download_request);
+    priv->download_request = NULL;
+  }
+
+  priv->state = PLAYLIST_LOADER_STATE_STOPPED;
+}
+
+void
+gst_hls_demux_playlist_loader_set_playlist_uri (GstHLSDemuxPlaylistLoader * pl,
+    const gchar * base_uri, const gchar * new_playlist_uri)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  gboolean playlist_uri_change = (priv->target_playlist_uri == NULL
+      || g_strcmp0 (new_playlist_uri, priv->target_playlist_uri) != 0);
+
+  if (!playlist_uri_change)
+    return;
+
+  GST_DEBUG_OBJECT (pl, "Setting target playlist URI to %s", new_playlist_uri);
+
+  g_free (priv->base_uri);
+  g_free (priv->target_playlist_uri);
+
+  priv->base_uri = g_strdup (base_uri);
+  priv->target_playlist_uri = g_strdup (new_playlist_uri);
+  priv->delta_merge_failed = FALSE;
+
+  switch (priv->state) {
+    case PLAYLIST_LOADER_STATE_STOPPED:
+      return;                   /* Not runnning */
+    case PLAYLIST_LOADER_STATE_STARTING:
+    case PLAYLIST_LOADER_STATE_LOADING:
+      /* If there's no pending state check, trigger one */
+      if (priv->pending_cb_id == 0) {
+        GST_LOG_OBJECT (pl, "Scheduling state update from state %d",
+            priv->state);
+        schedule_state_update (pl, priv);
+      }
+      break;
+    case PLAYLIST_LOADER_STATE_WAITING:
+      /* Waiting for the next time to load a live playlist, but the playlist has changed so
+       * cancel that and trigger a new one */
+      g_assert (priv->pending_cb_id != 0);
+      gst_adaptive_demux_loop_cancel_call (priv->scheduler_task,
+          priv->pending_cb_id);
+      priv->pending_cb_id = 0;
+      schedule_state_update (pl, priv);
+      break;
+  }
+}
+
+/* Check that the current playlist matches the target URI, and return
+ * TRUE if so */
+gboolean
+gst_hls_demux_playlist_loader_has_current_uri (GstHLSDemuxPlaylistLoader * pl,
+    const gchar * target_playlist_uri)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  if (target_playlist_uri == NULL)
+    target_playlist_uri = priv->target_playlist_uri;
+
+  if (priv->current_playlist == NULL
+      || g_strcmp0 (target_playlist_uri, priv->current_playlist_uri))
+    return FALSE;
+
+  return TRUE;
+}
+
+enum PlaylistDownloadParamFlags
+{
+  PLAYLIST_DOWNLOAD_FLAG_SKIP_V1 = (1 << 0),
+  PLAYLIST_DOWNLOAD_FLAG_SKIP_V2 = (1 << 1),    /* V2 also skips date-ranges */
+  PLAYLIST_DOWNLOAD_FLAG_BLOCKING_REQUEST = (1 << 2),
+};
+
+struct PlaylistDownloadParams
+{
+  enum PlaylistDownloadParamFlags flags;
+  gint64 next_msn, next_part;
+};
+
+#define HLS_SKIP_QUERY_KEY "_HLS_skip"
+#define HLS_MSN_QUERY_KEY "_HLS_msn"
+#define HLS_PART_QUERY_KEY "_HLS_part"
+
+static gchar *
+remove_HLS_directives_from_uri (const gchar * playlist_uri)
+{
+
+  /* Catch the simple case and keep NULL as NULL */
+  if (playlist_uri == NULL)
+    return NULL;
+
+  GstUri *uri = gst_uri_from_string (playlist_uri);
+  gst_uri_remove_query_key (uri, HLS_SKIP_QUERY_KEY);
+  gst_uri_remove_query_key (uri, HLS_MSN_QUERY_KEY);
+  gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
+
+  GList *keys = gst_uri_get_query_keys (uri);
+  if (keys)
+    keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
+  gchar *out_uri = gst_uri_to_string_with_keys (uri, keys);
+  gst_uri_unref (uri);
+
+  return out_uri;
+}
+
+static gchar *
+apply_directives_to_uri (GstHLSDemuxPlaylistLoader * pl,
+    const gchar * playlist_uri, struct PlaylistDownloadParams *dl_params)
+{
+  /* Short-circuit URI parsing if nothing will change */
+  if (dl_params->flags == 0)
+    return g_strdup (playlist_uri);
+
+  GstUri *uri = gst_uri_from_string (playlist_uri);
+
+  if (dl_params->flags & PLAYLIST_DOWNLOAD_FLAG_SKIP_V1) {
+    GST_LOG_OBJECT (pl, "Doing HLS skip (v1) request");
+    gst_uri_set_query_value (uri, HLS_SKIP_QUERY_KEY, "YES");
+  } else if (dl_params->flags & PLAYLIST_DOWNLOAD_FLAG_SKIP_V2) {
+    GST_LOG_OBJECT (pl, "Doing HLS skip (v2) request");
+    gst_uri_set_query_value (uri, HLS_SKIP_QUERY_KEY, "v2");
+  } else {
+    gst_uri_remove_query_key (uri, HLS_SKIP_QUERY_KEY);
+  }
+
+  if (dl_params->flags & PLAYLIST_DOWNLOAD_FLAG_BLOCKING_REQUEST
+      && dl_params->next_msn != -1) {
+    GST_LOG_OBJECT (pl,
+        "Doing HLS blocking request for URI %s with MSN %" G_GINT64_FORMAT
+        " part %" G_GINT64_FORMAT, playlist_uri, dl_params->next_msn,
+        dl_params->next_part);
+
+    gchar *next_msn_str =
+        g_strdup_printf ("%" G_GINT64_FORMAT, dl_params->next_msn);
+    gst_uri_set_query_value (uri, HLS_MSN_QUERY_KEY, next_msn_str);
+    g_free (next_msn_str);
+
+    if (dl_params->next_part != -1) {
+      gchar *next_part_str =
+          g_strdup_printf ("%" G_GINT64_FORMAT, dl_params->next_part);
+      gst_uri_set_query_value (uri, HLS_PART_QUERY_KEY, next_part_str);
+      g_free (next_part_str);
+    } else {
+      gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
+    }
+  } else {
+    gst_uri_remove_query_key (uri, HLS_MSN_QUERY_KEY);
+    gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
+  }
+
+  /* Produce the resulting URI with query arguments in UTF-8 order
+   * as required by the HLS spec:
+   * `Clients using Delivery Directives (Section 6.2.5) MUST ensure that
+   * all query parameters appear in UTF-8 order within the URI.`
+   */
+  GList *keys = gst_uri_get_query_keys (uri);
+  if (keys)
+    keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
+  gchar *out_uri = gst_uri_to_string_with_keys (uri, keys);
+  gst_uri_unref (uri);
+
+  return out_uri;
+}
+
+static GstClockTime
+get_playlist_reload_interval (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderPrivate * priv, GstHLSMediaPlaylist * playlist)
+{
+  if (playlist == NULL)
+    return GST_CLOCK_TIME_NONE; /* No playlist yet */
+
+  /* Use the most recent segment (or part segment) duration, as per
+   * https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-11#section-6.3.4
+   */
+  GstClockTime target_duration = GST_CLOCK_TIME_NONE;
+  GstClockTime min_reload_interval = playlist->targetduration / 2;
+
+  if (playlist->segments->len) {
+    GstM3U8MediaSegment *last_seg =
+        g_ptr_array_index (playlist->segments, playlist->segments->len - 1);
+
+    if (last_seg->partial_segments) {
+      GstM3U8PartialSegment *last_part =
+          g_ptr_array_index (last_seg->partial_segments,
+          last_seg->partial_segments->len - 1);
+
+      target_duration = last_part->duration;
+      if (GST_CLOCK_TIME_IS_VALID (playlist->partial_targetduration)) {
+        min_reload_interval = playlist->partial_targetduration / 2;
+      } else {
+        min_reload_interval = target_duration / 2;
+      }
+    } else {
+      target_duration = last_seg->duration;
+      min_reload_interval = target_duration / 2;
+    }
+  } else if (GST_CLOCK_TIME_IS_VALID (playlist->partial_targetduration)) {
+    target_duration = playlist->partial_targetduration;
+    min_reload_interval = target_duration / 2;
+  } else if (playlist->version > 5) {
+    target_duration = playlist->targetduration;
+  }
+
+  if (playlist->reloaded && target_duration > min_reload_interval) {
+    GST_DEBUG_OBJECT (pl,
+        "Playlist didn't change previously, returning lower update interval (%"
+        GST_TIME_FORMAT " -> %" GST_TIME_FORMAT ")",
+        GST_TIME_ARGS (target_duration), GST_TIME_ARGS (min_reload_interval));
+    target_duration = min_reload_interval;
+  }
+
+  GST_DEBUG_OBJECT (pl, "Returning target duration %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (target_duration));
+
+  return target_duration;
+}
+
+static void
+handle_download_error (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderPrivate * priv)
+{
+  if (++priv->download_error_count > MAX_DOWNLOAD_ERROR_COUNT) {
+    GST_DEBUG_OBJECT (pl,
+        "Reached %d download failures on URI %s. Reporting the failure",
+        priv->download_error_count, priv->loading_playlist_uri);
+    if (priv->error_cb)
+      priv->error_cb (pl, priv->loading_playlist_uri, priv->userdata);
+  }
+
+  /* The error callback may have provided a new playlist to load, which
+   * will have scheduled a state update immediately. In that case,
+   * don't trigger our own delayed retry */
+  if (priv->pending_cb_id == 0)
+    schedule_next_playlist_load (pl, priv, 100 * GST_MSECOND);
+}
+
+static void
+on_download_complete (DownloadRequest * download, DownloadRequestState state,
+    GstHLSDemuxPlaylistLoader * pl)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  if (priv->state != PLAYLIST_LOADER_STATE_LOADING) {
+    GST_DEBUG_OBJECT (pl, "Loader state changed to %d. Aborting", priv->state);
+    return;
+  }
+
+  if (g_strcmp0 (priv->target_playlist_uri, priv->loading_playlist_uri)) {
+    /* This callback happened just as the playlist URI was updated. There should be
+     * a pending state update scheduled, but we can just kick off the new download
+     * immediately */
+    GST_DEBUG_OBJECT (pl,
+        "Target playlist URI changed from %s to %s. Discarding download",
+        priv->loading_playlist_uri, priv->target_playlist_uri);
+    start_playlist_download (pl, priv);
+    return;
+  }
+
+  GST_DEBUG_OBJECT (pl, "Handling completed playlist download for URI %s",
+      download->uri);
+
+  /* If we got a permanent redirect, use that as the new
+   * playlist URI, otherwise set the base URI of the playlist
+   * to the redirect target if any (NULL if there was no redirect) */
+  GstHLSMediaPlaylist *playlist = NULL;
+  gchar *base_uri, *uri;
+
+  if (download->redirect_uri) {
+    /* Strip HLS request params from the playlist and redirect URI */
+    uri = remove_HLS_directives_from_uri (download->redirect_uri);
+    base_uri = NULL;
+
+    if (download->redirect_permanent) {
+      /* Store this redirect as the future request URI for this playlist */
+      g_free (priv->current_playlist_redirect_uri);
+      priv->current_playlist_redirect_uri = g_strdup (uri);
+    }
+  } else {
+    /* Strip HLS request params from the playlist and redirect URI */
+    uri = remove_HLS_directives_from_uri (download->uri);
+    base_uri = remove_HLS_directives_from_uri (download->redirect_uri);
+  }
+
+  /* Calculate the newest time we know this playlist was valid to store on the HLS Media Playlist */
+  GstClockTime playlist_ts =
+      MAX (0, GST_CLOCK_DIFF (download_request_get_age (download),
+          download->download_start_time));
+
+  GstBuffer *buf = download_request_take_buffer (download);
+
+  /* there should be a buf if there wasn't an error (handled above) */
+  g_assert (buf);
+
+  gchar *playlist_data = gst_hls_buf_to_utf8_text (buf);
+  gst_buffer_unref (buf);
+
+  if (playlist_data == NULL) {
+    GST_WARNING_OBJECT (pl, "Couldn't validate playlist encoding");
+    goto error_retry_out;
+  }
+
+  GstHLSMediaPlaylist *current_playlist = priv->current_playlist;
+  gboolean playlist_uri_change = (current_playlist == NULL
+      || g_strcmp0 (priv->loading_playlist_uri,
+          priv->current_playlist_uri) != 0);
+
+  gboolean always_reload = FALSE;
+#if 0
+  /* Test code the reports a playlist load error if we load
+     the same playlist 2 times in a row and the URI contains "video.m3u8"
+     https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/redundant.m3u8
+     works as a test URL
+   */
+  static gint playlist_load_counter = 0;
+  if (playlist_uri_change)
+    playlist_load_counter = 0;
+  else if (strstr (priv->loading_playlist_uri, "video.m3u8") != NULL) {
+    playlist_load_counter++;
+    if (playlist_load_counter > 1) {
+      g_print ("Triggering playlist failure for %s\n",
+          priv->loading_playlist_uri);
+      goto error_retry_out;
+    }
+  }
+  always_reload = TRUE;
+#endif
+
+  if (!playlist_uri_change && current_playlist
+      && gst_hls_media_playlist_has_same_data (current_playlist,
+          playlist_data)) {
+    GST_DEBUG_OBJECT (pl, "playlist data was unchanged");
+    playlist = gst_hls_media_playlist_ref (current_playlist);
+    playlist->reloaded = TRUE;
+    playlist->request_time = GST_CLOCK_TIME_NONE;
+    g_free (playlist_data);
+  } else {
+    playlist =
+        gst_hls_media_playlist_parse (playlist_data, playlist_ts, uri,
+        base_uri);
+    if (!playlist) {
+      GST_WARNING_OBJECT (pl, "Couldn't parse playlist");
+      goto error_retry_out;
+    }
+    playlist->request_time = download->download_request_time;
+  }
+
+  /* Transfer over any skipped segments from the current playlist if
+   * we did a delta playlist update */
+  if (!playlist_uri_change && current_playlist && playlist
+      && playlist->skipped_segments > 0) {
+    if (!gst_hls_media_playlist_sync_skipped_segments (playlist,
+            current_playlist)) {
+      GST_DEBUG_OBJECT (pl,
+          "Could not merge delta update to playlist. Retrying with full request");
+
+      gst_hls_media_playlist_unref (playlist);
+
+      /* Delta playlist update failed. Load a full playlist */
+      priv->delta_merge_failed = TRUE;
+      start_playlist_download (pl, priv);
+      goto out;
+    }
+  }
+
+  g_free (priv->current_playlist_uri);
+  if (priv->current_playlist)
+    gst_hls_media_playlist_unref (priv->current_playlist);
+
+  priv->current_playlist_uri = g_strdup (priv->loading_playlist_uri);
+  priv->current_playlist = playlist;
+
+  /* Successfully loaded the playlist. Forget any prior failures */
+  priv->download_error_count = 0;
+
+  if (priv->success_cb)
+    priv->success_cb (pl, priv->current_playlist_uri, priv->current_playlist,
+        priv->userdata);
+
+  g_free (priv->loading_playlist_uri);
+  priv->loading_playlist_uri = NULL;
+
+  if (gst_hls_media_playlist_is_live (playlist) || always_reload) {
+    /* Schedule the next playlist load. If we can do a blocking load,
+     * do it immediately, otherwise delayed */
+    if (playlist->can_block_reload) {
+      start_playlist_download (pl, priv);
+    } else {
+      GstClockTime delay = get_playlist_reload_interval (pl, priv, playlist);
+      schedule_next_playlist_load (pl, priv, delay);
+    }
+  } else {
+    GST_LOG_OBJECT (pl, "Playlist is not live. Not scheduling a reload");
+    /* Go back to the starting state until/if the playlist uri is updated */
+    priv->state = PLAYLIST_LOADER_STATE_STARTING;
+  }
+
+out:
+  g_free (uri);
+  g_free (base_uri);
+  return;
+
+error_retry_out:
+  /* Got invalid playlist data, retry soon or error out */
+  handle_download_error (pl, priv);
+  goto out;
+}
+
+static void
+on_download_error (DownloadRequest * download, DownloadRequestState state,
+    GstHLSDemuxPlaylistLoader * pl)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  if (priv->state != PLAYLIST_LOADER_STATE_LOADING) {
+    GST_DEBUG_OBJECT (pl, "Loader state changed to %d. Aborting", priv->state);
+    return;
+  }
+
+  GST_WARNING_OBJECT (pl,
+      "Couldn't retrieve playlist, got HTTP status code %d",
+      download->status_code);
+
+  handle_download_error (pl, priv);
+}
+
+static void
+start_playlist_download (GstHLSDemuxPlaylistLoader * pl,
+    GstHLSDemuxPlaylistLoaderPrivate * priv)
+{
+  gboolean allow_skip = !priv->delta_merge_failed;
+  const gchar *orig_uri = priv->target_playlist_uri;
+
+  /* Can't download yet */
+  if (orig_uri == NULL)
+    return;
+
+  struct PlaylistDownloadParams dl_params;
+  memset (&dl_params, 0, sizeof (struct PlaylistDownloadParams));
+
+  GstHLSMediaPlaylist *current_playlist = priv->current_playlist;
+
+  /* If there's no previous playlist, or the URI changed this
+   * is not a refresh/update but a switch to a new playlist */
+  gboolean playlist_uri_change = (current_playlist == NULL
+      || g_strcmp0 (orig_uri, priv->current_playlist_uri) != 0);
+
+  if (!playlist_uri_change) {
+    GST_LOG_OBJECT (pl, "Updating the playlist");
+
+    /* If we have a redirect stored for this playlist URI, use that instead */
+    if (priv->current_playlist_redirect_uri) {
+      orig_uri = priv->current_playlist_redirect_uri;
+      GST_LOG_OBJECT (pl, "Using redirected playlist URI %s", orig_uri);
+    }
+
+    /* See if we can do a delta playlist update (if the playlist age is less than
+     * one half of the Skip Boundary */
+    if (GST_CLOCK_TIME_IS_VALID (current_playlist->skip_boundary) && allow_skip) {
+      GstClockTime now = gst_adaptive_demux2_get_monotonic_time (priv->demux);
+      GstClockTimeDiff playlist_age =
+          GST_CLOCK_DIFF (current_playlist->playlist_ts, now);
+
+      if (GST_CLOCK_TIME_IS_VALID (current_playlist->playlist_ts) &&
+          playlist_age <= current_playlist->skip_boundary / 2) {
+        if (current_playlist->can_skip_dateranges) {
+          dl_params.flags |= PLAYLIST_DOWNLOAD_FLAG_SKIP_V2;
+        } else {
+          dl_params.flags |= PLAYLIST_DOWNLOAD_FLAG_SKIP_V1;
+        }
+      }
+    } else if (GST_CLOCK_TIME_IS_VALID (current_playlist->skip_boundary)) {
+      GST_DEBUG_OBJECT (pl,
+          "Doing full playlist update after failed delta request");
+    }
+  } else {
+    /* This is the first time loading this playlist URI, clear the error counter
+     * and redirect URI */
+    if (!priv->loading_playlist_uri
+        || g_strcmp0 (orig_uri, priv->loading_playlist_uri))
+      priv->download_error_count = 0;
+    g_free (priv->current_playlist_redirect_uri);
+    priv->current_playlist_redirect_uri = NULL;
+  }
+
+  /* Blocking playlist reload check */
+  if (current_playlist != NULL && current_playlist->can_block_reload) {
+    if (playlist_uri_change) {
+      /* FIXME: We're changing playlist, but if there's a EXT-X-RENDITION-REPORT
+       * for the new playlist we might be able to use it to do a blocking request */
+    } else {
+      /* Get the next MSN (and/or possibly part number) for the request params */
+      gst_hls_media_playlist_get_next_msn_and_part (current_playlist,
+          &dl_params.next_msn, &dl_params.next_part);
+      dl_params.flags |= PLAYLIST_DOWNLOAD_FLAG_BLOCKING_REQUEST;
+    }
+  }
+
+  gchar *target_uri = apply_directives_to_uri (pl, orig_uri, &dl_params);
+
+  if (priv->download_request == NULL) {
+    priv->download_request = download_request_new_uri (target_uri);
+
+    download_request_set_callbacks (priv->download_request,
+        (DownloadRequestEventCallback) on_download_complete,
+        (DownloadRequestEventCallback) on_download_error,
+        (DownloadRequestEventCallback) NULL,
+        (DownloadRequestEventCallback) NULL, pl);
+  } else {
+    download_request_set_uri (priv->download_request, target_uri, 0, -1);
+  }
+
+  GST_DEBUG_OBJECT (pl, "Submitting playlist download request for URI %s",
+      target_uri);
+  g_free (target_uri);
+
+  g_free (priv->loading_playlist_uri);
+  priv->loading_playlist_uri = g_strdup (orig_uri);
+  priv->state = PLAYLIST_LOADER_STATE_LOADING;
+
+  if (!downloadhelper_submit_request (priv->download_helper,
+          NULL, DOWNLOAD_FLAG_COMPRESS | DOWNLOAD_FLAG_FORCE_REFRESH,
+          priv->download_request, NULL)) {
+    /* Failed to submit the download - could be invalid URI, but
+     * could just mean the download helper was stopped */
+    priv->state = PLAYLIST_LOADER_STATE_STOPPED;
+  }
+}
+
+static gboolean
+gst_hls_demux_playlist_loader_update (GstHLSDemuxPlaylistLoader * pl)
+{
+  GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
+
+  GST_LOG_OBJECT (pl, "Updating at state %d", priv->state);
+  priv->pending_cb_id = 0;
+
+  switch (priv->state) {
+    case PLAYLIST_LOADER_STATE_STOPPED:
+      break;
+    case PLAYLIST_LOADER_STATE_STARTING:
+      if (priv->target_playlist_uri)
+        start_playlist_download (pl, priv);
+      break;
+    case PLAYLIST_LOADER_STATE_LOADING:
+      /* A download is in progress, but if we reach here it's
+       * because the target playlist URI got updated, so check
+       * for cancelling the current download. */
+      if (!g_strcmp0 (priv->target_playlist_uri, priv->current_playlist_uri))
+        break;
+
+      /* A download is in progress. Cancel it and trigger a new one */
+      if (priv->download_request) {
+        GST_DEBUG_OBJECT (pl,
+            "Playlist URI changed from %s to %s. Cancelling current download",
+            priv->target_playlist_uri, priv->current_playlist_uri);
+        downloadhelper_cancel_request (priv->download_helper,
+            priv->download_request);
+        download_request_unref (priv->download_request);
+        priv->download_request = NULL;
+      }
+      start_playlist_download (pl, priv);
+      break;
+    case PLAYLIST_LOADER_STATE_WAITING:
+      /* We were waiting until time to load a playlist. Load it now */
+      start_playlist_download (pl, priv);
+      break;
+  }
+
+  return G_SOURCE_REMOVE;
+}
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h b/ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h
new file mode 100644
index 0000000000000000000000000000000000000000..c40f84e34b0b074735882c653c8751b5d0c42d83
--- /dev/null
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-playlist-loader.h
@@ -0,0 +1,80 @@
+/* GStreamer
+ *
+ * Copyright (C) 2014 Samsung Electronics. All rights reserved.
+ *   Author: Thiago Santos <thiagoss@osg.samsung.com>
+ *
+ * Copyright (C) 2021-2022 Centricular Ltd
+ *   Author: Edward Hervey <edward@centricular.com>
+ *   Author: Jan Schmidt <jan@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HLS_DEMUX_PLAYLIST_LOADER_H_
+#define _GST_HLS_DEMUX_PLAYLIST_LOADER_H_
+
+#include <gst/gst.h>
+#include "gstadaptivedemux.h"
+#include "gstadaptivedemuxutils.h"
+#include "downloadhelper.h"
+#include "downloadrequest.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HLS_DEMUX_PLAYLIST_LOADER (gst_hls_demux_playlist_loader_get_type())
+#define GST_HLS_DEMUX_PLAYLIST_LOADER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HLS_DEMUX_PLAYLIST_LOADER,GstHLSDemuxPlaylistLoader))
+#define GST_HLS_DEMUX_PLAYLIST_LOADER_CAST(obj) ((GstHLSDemuxPlaylistLoader *)obj)
+
+typedef struct _GstHLSDemuxPlaylistLoader GstHLSDemuxPlaylistLoader;
+typedef struct _GstHLSDemuxPlaylistLoaderClass GstHLSDemuxPlaylistLoaderClass;
+typedef struct _GstHLSDemuxPlaylistLoaderPrivate GstHLSDemuxPlaylistLoaderPrivate;
+
+typedef void (*GstHLSDemuxPlaylistLoaderSuccessCallback) (GstHLSDemuxPlaylistLoader *pl,
+    const gchar *playlist_uri, GstHLSMediaPlaylist *new_playlist, gpointer userdata);
+typedef void (*GstHLSDemuxPlaylistLoaderErrorCallback) (GstHLSDemuxPlaylistLoader *pl,
+    const gchar *playlist_uri, gpointer userdata);
+
+struct _GstHLSDemuxPlaylistLoaderClass
+{
+  GstObjectClass parent_class;
+};
+
+struct _GstHLSDemuxPlaylistLoader
+{
+  GstObject object;
+  GstHLSDemuxPlaylistLoaderPrivate *priv;
+};
+
+GType gst_hls_demux_playlist_loader_get_type(void);
+
+GstHLSDemuxPlaylistLoader *gst_hls_demux_playlist_loader_new(GstAdaptiveDemux *demux,
+    DownloadHelper *download_helper);
+
+void gst_hls_demux_playlist_loader_set_callbacks (GstHLSDemuxPlaylistLoader *pl,
+      GstHLSDemuxPlaylistLoaderSuccessCallback success_cb,
+      GstHLSDemuxPlaylistLoaderErrorCallback error_cb,
+      gpointer userdata);
+
+void gst_hls_demux_playlist_loader_start (GstHLSDemuxPlaylistLoader *pl);
+void gst_hls_demux_playlist_loader_stop (GstHLSDemuxPlaylistLoader *pl);
+
+void gst_hls_demux_playlist_loader_set_playlist_uri (GstHLSDemuxPlaylistLoader *pl,
+  const gchar *base_uri, const gchar *current_playlist_uri);
+gboolean gst_hls_demux_playlist_loader_has_current_uri (GstHLSDemuxPlaylistLoader *pl,
+  const gchar *target_playlist_uri);
+
+G_END_DECLS
+#endif
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-preloader.c b/ext/adaptivedemux2/hls/gsthlsdemux-preloader.c
new file mode 100644
index 0000000000000000000000000000000000000000..88ed46519b14c7602bc746776dde0b65a171a8a7
--- /dev/null
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-preloader.c
@@ -0,0 +1,598 @@
+/* GStreamer
+ Copyright (C) 2022 Jan Schmidt <jan@centricular.com>
+ *
+ * gsthlsdemux-preloader.c:
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gsthlsdemux-preloader.h"
+
+/* Everything is called from the scheduler thread, including
+ * download handling callbacks */
+
+GST_DEBUG_CATEGORY_EXTERN (gst_hls_demux2_debug);
+#define GST_CAT_DEFAULT gst_hls_demux2_debug
+
+typedef struct _GstHLSDemuxPreloadRequest GstHLSDemuxPreloadRequest;
+struct _GstHLSDemuxPreloadRequest
+{
+  GstHLSDemuxPreloader *preloader;      /* Parent preloader */
+  GstM3U8PreloadHint *hint;
+
+  /* Incoming download tracking for the resource */
+  DownloadRequest *download_request;
+  gboolean download_is_finished;        /* TRUE if the input download request completed / failed */
+  guint64 download_cur_offset;  /* offset of the next expected received data */
+  guint64 download_content_length;      /* Content length (filled in when response headers arrive) */
+  GstClockTime request_latency; /* original HTTP request to data latency */
+  GstClockTime download_first_data_time;        /* Arrival timestamp of the first data in the download chunk */
+  guint64 download_first_data_offset;   /* First data byte offset of the download chunk */
+
+  /* Target tracking for the stream download to deliver data blocks to */
+  /* Each active preload only needs one target to output to at a time,
+   * since we only download one segment at a time, and MAP requests are distinct from PART requests,
+   * so 1 preload = 1 download request by the stream */
+  guint64 target_cur_offset;    /* offset of the next delivered target data */
+  DownloadRequest *target_request;
+};
+
+static GstHLSDemuxPreloadRequest *
+gst_hls_demux_preload_request_new (GstHLSDemuxPreloader * preloader,
+    GstM3U8PreloadHint * hint)
+{
+  GstHLSDemuxPreloadRequest *req = g_new0 (GstHLSDemuxPreloadRequest, 1);
+  req->preloader = preloader;
+  req->hint = gst_m3u8_preload_hint_ref (hint);
+  req->request_latency = GST_CLOCK_TIME_NONE;
+  req->download_first_data_time = GST_CLOCK_TIME_NONE;
+  req->download_first_data_offset = GST_BUFFER_OFFSET_NONE;
+
+  return req;
+};
+
+static void
+gst_hls_demux_preload_request_free (GstHLSDemuxPreloadRequest * req)
+{
+  gst_m3u8_preload_hint_unref (req->hint);
+
+  if (req->download_request != NULL) {
+    /* The download request must have been cancelled by the preload helper,
+     * but cancellation is async, so we can't verify */
+    download_request_unref (req->download_request);
+  }
+
+  if (req->target_request != NULL) {
+    download_request_unref (req->target_request);
+  }
+
+  g_free (req);
+};
+
+static gboolean
+gst_hls_demux_preloader_submit (GstHLSDemuxPreloader * preloader,
+    GstHLSDemuxPreloadRequest * preload_req, const gchar * referrer_uri);
+static void gst_hls_demux_preloader_release_request (GstHLSDemuxPreloader *
+    preloader, GstHLSDemuxPreloadRequest * preload_req,
+    gboolean cancel_download);
+
+GstHLSDemuxPreloader *
+gst_hls_demux_preloader_new (DownloadHelper * download_helper)
+{
+  GstHLSDemuxPreloader *preloader = g_new0 (GstHLSDemuxPreloader, 1);
+
+  preloader->download_helper = download_helper;
+  preloader->active_preloads = g_ptr_array_new ();
+
+  return preloader;
+}
+
+void
+gst_hls_demux_preloader_free (GstHLSDemuxPreloader * preloader)
+{
+  gst_hls_demux_preloader_cancel (preloader, M3U8_PRELOAD_HINT_ALL);
+  g_ptr_array_free (preloader->active_preloads, TRUE);
+  g_free (preloader);
+}
+
+void
+gst_hls_demux_preloader_load (GstHLSDemuxPreloader * preloader,
+    GstM3U8PreloadHint * hint, const gchar * referrer_uri)
+{
+  /* Check if we have an active preload already for this hint */
+  guint idx;
+  for (idx = 0; idx < preloader->active_preloads->len; idx++) {
+    GstHLSDemuxPreloadRequest *req =
+        g_ptr_array_index (preloader->active_preloads, idx);
+    if (hint->hint_type == req->hint->hint_type) {
+      /* We already have an active hint of this type. If this new one is different, cancel
+       * the active preload before starting this one */
+      if (gst_m3u8_preload_hint_equal (hint, req->hint)) {
+        GST_LOG ("Ignoring pre-existing preload of type %d uri: %s, range:%"
+            G_GINT64_FORMAT " size %" G_GINT64_FORMAT, hint->hint_type,
+            hint->uri, hint->offset, hint->size);
+        return;                 /* Nothing to do */
+      }
+
+      gst_hls_demux_preloader_release_request (preloader, req, TRUE);
+      g_ptr_array_remove_index_fast (preloader->active_preloads, idx);
+      break;
+    }
+  }
+
+  /* If we get here, then there's no preload of this type. Create one */
+  GstHLSDemuxPreloadRequest *req =
+      gst_hls_demux_preload_request_new (preloader, hint);
+  /* Submit the request */
+
+  if (gst_hls_demux_preloader_submit (preloader, req, referrer_uri)) {
+    g_ptr_array_add (preloader->active_preloads, req);
+  } else {
+    /* Discard failed request */
+    gst_hls_demux_preloader_release_request (preloader, req, TRUE);
+  }
+}
+
+void
+gst_hls_demux_preloader_cancel (GstHLSDemuxPreloader * preloader,
+    GstM3U8PreloadHintType hint_types)
+{
+  /* Go through the active downloads and remove/cancel any with the matching type */
+  guint idx;
+  for (idx = 0; idx < preloader->active_preloads->len;) {
+    GstHLSDemuxPreloadRequest *req =
+        g_ptr_array_index (preloader->active_preloads, idx);
+    if (hint_types & req->hint->hint_type) {
+      gst_hls_demux_preloader_release_request (preloader, req, TRUE);
+      g_ptr_array_remove_index_fast (preloader->active_preloads, idx);
+      continue;                 /* Don't increment idx++, as we just removed an item */
+    }
+
+    idx++;
+  }
+}
+
+/* This function transfers any available data to the target request, and possibly
+ * completes it and removes it from the preload */
+static void
+gst_hls_demux_preloader_despatch (GstHLSDemuxPreloadRequest * preload_req,
+    gboolean input_is_finished)
+{
+  GstHLSDemuxPreloader *preloader = preload_req->preloader;
+  DownloadRequest *download_req = preload_req->download_request;
+
+  if (input_is_finished)
+    preload_req->download_is_finished = TRUE;
+  else
+    input_is_finished = preload_req->download_is_finished;
+
+  download_request_lock (download_req);
+
+  /* Update timestamp tracking */
+  if (preload_req->request_latency == GST_CLOCK_TIME_NONE) {
+    if (GST_CLOCK_TIME_IS_VALID (download_req->download_request_time) &&
+        GST_CLOCK_TIME_IS_VALID (download_req->download_start_time)) {
+
+      preload_req->request_latency =
+          download_req->download_start_time -
+          download_req->download_request_time;
+    }
+  }
+
+  if (preload_req->download_first_data_time == GST_CLOCK_TIME_NONE &&
+      download_request_get_bytes_available (download_req) > 0) {
+    /* Got the first data of this download burst */
+    preload_req->download_first_data_time = download_req->download_start_time;
+    preload_req->download_first_data_offset =
+        download_request_get_cur_offset (download_req);
+  }
+
+  download_request_unlock (download_req);
+
+  /* If there is a target request, see if any of our data should be
+   * transferred to it, and if it should be despatched as complete */
+  if (preload_req->target_request != NULL) {
+    gboolean output_is_finished = input_is_finished;
+    gboolean despatch_progress = FALSE;
+    DownloadRequest *target_req = preload_req->target_request;
+
+    download_request_lock (target_req);
+    download_request_lock (download_req);
+
+    DownloadRequestState target_state = download_req->state;
+
+    /* Transfer the http status code */
+    target_req->status_code = download_req->status_code;
+
+    GstBuffer *target_buf = download_request_take_buffer_range (download_req,
+        preload_req->target_cur_offset,
+        target_req->range_end);
+
+    if (target_buf != NULL) {
+      /* Deliver data to the target, and update our tracked output position */
+      preload_req->target_cur_offset =
+          GST_BUFFER_OFFSET (target_buf) + gst_buffer_get_size (target_buf);
+
+      GST_LOG ("Adding %" G_GSIZE_FORMAT " bytes at offset %" G_GUINT64_FORMAT
+          " to target download request uri %s range %" G_GINT64_FORMAT " - %"
+          G_GINT64_FORMAT, gst_buffer_get_size (target_buf),
+          GST_BUFFER_OFFSET (target_buf), target_req->uri,
+          target_req->range_start, target_req->range_end);
+
+      download_request_add_buffer (target_req, target_buf);
+      despatch_progress = TRUE; /* Added a buffer, despatch progress callback */
+
+      /* Transfer timing from the input download as best we can, so the receiver can
+       * calculate bitrates. If all preload requests filled one target download,
+       * we could just transfer the timestamps, but to handle the case of an
+       * ongoing chunked connection needs fancier accounting based on the
+       * arrival times of each data burst */
+      if (target_req->download_start_time == GST_CLOCK_TIME_NONE) {
+        if (preload_req->download_first_data_time >
+            preload_req->request_latency) {
+          target_req->download_request_time =
+              preload_req->download_first_data_time -
+              preload_req->request_latency;
+        } else {
+          target_req->download_request_time = 0;
+        }
+
+        target_req->download_start_time = preload_req->download_first_data_time;
+        target_req->download_newest_data_time =
+            download_req->download_newest_data_time;
+      }
+
+      if (target_req->range_end != -1
+          && preload_req->target_cur_offset > target_req->range_end) {
+        /* We've delivered all data to satisfy the requested byte range - the target request is complete */
+        if (target_state == DOWNLOAD_REQUEST_STATE_LOADING) {
+          target_state = DOWNLOAD_REQUEST_STATE_COMPLETE;
+          GST_LOG ("target download request uri %s range %" G_GINT64_FORMAT
+              " - %" G_GINT64_FORMAT " is fully satisfied. Completing",
+              target_req->uri, target_req->range_start, target_req->range_end);
+        }
+
+        output_is_finished = TRUE;
+
+        /* If there's unconsumed data left in the input download, then update
+         * our variable that tracks the first data arrival time in in a prorata
+         * fashion (because there's more partial segment data already downloaded
+         * and we need to preserve a reasonable bitrate estimate. If there's no
+         * data, but the connection is continuing, then it's returned to a blocking
+         * read state that'll send more data in the future when
+         * a new live segment becomes available, so reset our variable as if that
+         * download was starting again */
+        guint64 data_avail =
+            download_request_get_bytes_available (download_req);
+        if (data_avail > 0) {
+          /* burst first data offset must have been set by now */
+          g_assert (preload_req->download_first_data_offset !=
+              GST_BUFFER_OFFSET_NONE);
+
+          /* Calculate how long it took to download the data we have output/discarded
+           * based on the average bitrate so far.
+           * time_to_download = total_download_time * consumed_bytes / total_download_bytes */
+          guint64 new_cur_offset =
+              download_request_get_cur_offset (download_req);
+          GstClockTime data_time_offset =
+              gst_util_uint64_scale (download_req->download_newest_data_time -
+              preload_req->download_first_data_time,
+              new_cur_offset - preload_req->download_first_data_offset,
+              new_cur_offset + data_avail -
+              preload_req->download_first_data_offset);
+
+          preload_req->download_first_data_time += data_time_offset;
+          preload_req->download_first_data_offset = new_cur_offset;
+
+          GST_LOG ("Advancing request timing tracking by %" GST_TIMEP_FORMAT
+              " to time %" GST_TIMEP_FORMAT " @ offset %" G_GUINT64_FORMAT,
+              &data_time_offset,
+              &preload_req->download_first_data_time,
+              preload_req->download_first_data_offset);
+
+          /* Say that this target download finished when the first
+           * byte of the remaining data arrived */
+          target_req->download_end_time = preload_req->download_first_data_time;
+        } else {
+          /* Reset the download start time */
+          preload_req->download_first_data_time = GST_CLOCK_TIME_NONE;
+          preload_req->download_first_data_offset = GST_BUFFER_OFFSET_NONE;
+
+          /* Say that this request finished when the most recent data arrived */
+          target_req->download_end_time =
+              download_req->download_newest_data_time;
+        }
+      }
+    }
+
+    if (input_is_finished
+        && target_req->download_end_time == GST_CLOCK_TIME_NONE) {
+      /* No download end time was set yet - use the input download end time */
+      target_req->download_end_time = download_req->download_end_time;
+    }
+
+    /* Update the target request's state, which may have been adjusted from the
+     * input request's state */
+    target_req->state = target_state;
+
+    if (target_req->headers == NULL && download_req->headers != NULL) {
+      target_req->headers = gst_structure_copy (download_req->headers);
+    }
+
+    if (target_req->redirect_uri == NULL && download_req->redirect_uri != NULL) {
+      target_req->redirect_uri = g_strdup (download_req->redirect_uri);
+      target_req->redirect_permanent = download_req->redirect_permanent;
+    }
+
+    /* We're done with the input download request . */
+    download_request_unlock (download_req);
+
+    if (output_is_finished) {
+      GST_DEBUG ("Finishing target preload request uri: %s, start: %"
+          G_GINT64_FORMAT " end: %" G_GINT64_FORMAT, target_req->uri,
+          target_req->range_start, target_req->range_end);
+
+      download_request_despatch_completion (target_req);
+      download_request_unlock (target_req);
+
+      download_request_unref (target_req);
+      preload_req->target_request = NULL;
+    } else if (despatch_progress) {
+      download_request_despatch_progress (target_req);
+    }
+
+    /* Unlock if the target request didn't get released above */
+    if (preload_req->target_request != NULL) {
+      download_request_unlock (preload_req->target_request);
+    }
+  }
+
+  if (input_is_finished) {
+    if (download_req == NULL
+        || download_request_get_bytes_available (download_req)
+        == 0) {
+      GstM3U8PreloadHint *hint = preload_req->hint;
+      GST_DEBUG ("Removing finished+drained preload type %d uri: %s, start: %"
+          G_GINT64_FORMAT " size: %" G_GINT64_FORMAT, hint->hint_type,
+          hint->uri, hint->offset, hint->size);
+
+      /* The incoming request is complete and the data is drained. Remove this preload request from the list */
+      g_ptr_array_remove_fast (preloader->active_preloads, preload_req);
+      gst_hls_demux_preloader_release_request (preloader, preload_req, FALSE);
+    }
+  }
+}
+
+static void
+on_download_cancellation (DownloadRequest * request, DownloadRequestState state,
+    GstHLSDemuxPreloadRequest * preload_req)
+{
+  gst_hls_demux_preloader_despatch (preload_req, TRUE);
+}
+
+static void
+on_download_error (DownloadRequest * request, DownloadRequestState state,
+    GstHLSDemuxPreloadRequest * preload_req)
+{
+  GstM3U8PreloadHint *hint = preload_req->hint;
+  GST_DEBUG ("preload type %d uri: %s download error", hint->hint_type,
+      hint->uri);
+
+  /* FIXME: Should we attempt to re-request a preload? Should we check if
+   * any part was transferred to the target request already? Should we
+   * attempt to request a byte range with a new start position if we
+   * already despatched data to other requests?
+   */
+  gst_hls_demux_preloader_despatch (preload_req, TRUE);
+}
+
+static void
+on_download_progress (DownloadRequest * request, DownloadRequestState state,
+    GstHLSDemuxPreloadRequest * preload_req)
+{
+  GstM3U8PreloadHint *hint = preload_req->hint;
+
+  GST_DEBUG ("preload type %d uri: %s download progress. position %"
+      G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT " bytes", hint->hint_type,
+      hint->uri,
+      preload_req->download_cur_offset +
+      download_request_get_bytes_available (request), request->content_length);
+  preload_req->download_content_length = request->content_length;
+
+  gst_hls_demux_preloader_despatch (preload_req, FALSE);
+}
+
+static void
+on_download_complete (DownloadRequest * request, DownloadRequestState state,
+    GstHLSDemuxPreloadRequest * preload_req)
+{
+  GstM3U8PreloadHint *hint = preload_req->hint;
+  GST_DEBUG ("preload type %d uri: %s download complete. position %"
+      G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT " bytes", hint->hint_type,
+      hint->uri,
+      preload_req->download_cur_offset +
+      download_request_get_bytes_available (request), request->content_length);
+  preload_req->download_content_length = request->content_length;
+
+  gst_hls_demux_preloader_despatch (preload_req, TRUE);
+}
+
+static gboolean
+gst_hls_demux_preloader_submit (GstHLSDemuxPreloader * preloader,
+    GstHLSDemuxPreloadRequest * preload_req, const gchar * referrer_uri)
+{
+  g_assert (preload_req->download_request == NULL);
+
+  DownloadRequest *download_req = download_request_new ();
+  GstM3U8PreloadHint *hint = preload_req->hint;
+
+  /* Configure our download request */
+  gint64 end = RFC8673_LAST_BYTE_POS;
+  if (hint->size > 0) {
+    end = hint->offset + hint->size - 1;
+  }
+
+  download_request_set_uri (download_req, hint->uri, hint->offset, end);
+  download_request_set_callbacks (download_req,
+      (DownloadRequestEventCallback) on_download_complete,
+      (DownloadRequestEventCallback) on_download_error,
+      (DownloadRequestEventCallback) on_download_cancellation,
+      (DownloadRequestEventCallback) on_download_progress, preload_req);
+
+  GST_DEBUG ("Submitting preload type %d uri: %s, range:%" G_GINT64_FORMAT
+      " - %" G_GINT64_FORMAT, hint->hint_type, hint->uri, hint->offset, end);
+
+  if (!downloadhelper_submit_request (preloader->download_helper,
+          referrer_uri, DOWNLOAD_FLAG_NONE, download_req, NULL)) {
+    /* Abandon the request */
+    download_request_unref (download_req);
+    return FALSE;
+  }
+
+  /* Store the current read offset */
+  preload_req->download_cur_offset = hint->offset;
+  preload_req->download_request = download_req;
+  preload_req->download_is_finished = FALSE;
+  return TRUE;
+}
+
+static void
+gst_hls_demux_preloader_release_request (GstHLSDemuxPreloader * preloader,
+    GstHLSDemuxPreloadRequest * preload_req, gboolean cancel_download)
+{
+  if (preload_req->download_request) {
+    if (cancel_download) {
+      GstM3U8PreloadHint *hint = preload_req->hint;
+
+      GST_DEBUG ("Cancelling preload type %d uri: %s, range start:%"
+          G_GINT64_FORMAT " size %" G_GINT64_FORMAT, hint->hint_type, hint->uri,
+          hint->offset, hint->size);
+
+      /* We don't want any callbacks to happen after we cancel here */
+      download_request_set_callbacks (preload_req->download_request,
+          NULL, NULL, NULL, NULL, NULL);
+      downloadhelper_cancel_request (preloader->download_helper,
+          preload_req->download_request);
+    }
+  }
+
+  gst_hls_demux_preload_request_free (preload_req);
+}
+
+/* See if we can satisfy a download request from a preload, and fulfil it if so.
+ * There are several cases:
+ *   * The URI and range exactly match one of our preloads -> OK
+ *   * The URI matches, and the requested range is a subset of the preload -> OK
+ *   * The URI matches, but the requested range is outside what's available in the preload
+ *     and can't be provided.
+ *
+ * Within those options, there are sub-possibilities:
+ *   * The preload request is ongoing. It might have enough data already to completely provide
+ *     the requested range.
+ *   * The preload request is ongoing, but has already moved past the requested range (no longer available)
+ *   * The preload request is ongoing, will feed data to the target req as it arrives
+ *   * The preload request is complete already, so can either provide the requested range or not, but
+ *     also needs to mark the target_req as completed once it has passed the required data.
+ */
+gboolean
+gst_hls_demux_preloader_provide_request (GstHLSDemuxPreloader * preloader,
+    DownloadRequest * target_req)
+{
+  guint idx;
+  for (idx = 0; idx < preloader->active_preloads->len; idx++) {
+    GstHLSDemuxPreloadRequest *preload_req =
+        g_ptr_array_index (preloader->active_preloads, idx);
+    GstM3U8PreloadHint *hint = preload_req->hint;
+
+    if (g_strcmp0 (hint->uri, target_req->uri))
+      continue;
+
+    GST_LOG ("Possible matching preload type %d uri: %s, range start:%"
+        G_GINT64_FORMAT " size %" G_GINT64_FORMAT " (download position %"
+        G_GUINT64_FORMAT ") for req with range %" G_GINT64_FORMAT " to %"
+        G_GINT64_FORMAT, hint->hint_type, hint->uri, hint->offset, hint->size,
+        preload_req->download_cur_offset, target_req->range_start,
+        target_req->range_end);
+
+    if (target_req->range_start > preload_req->download_cur_offset) {
+      /* This preload request is for a byte range beyond the desired
+       * position (or something already consumed the target data) */
+      GST_LOG ("Range start didn't match");
+      continue;
+    }
+
+    if (target_req->range_end != -1) {
+      /* The target request does not want the entire rest of the preload
+       * stream, so check that the end is satisfiable */
+      gint64 content_length = preload_req->download_content_length;
+      if (content_length == 0) {
+        /* We don't have information from the preload download's response headers yet,
+         * so check against the requested length and error out later if the server
+         * doesn't provide all the desired response */
+        if (hint->size != -1)
+          content_length = hint->size;
+      }
+
+      if (content_length != 0) {
+        /* We have some idea of the content length. Check if it will provide the requested
+         * range */
+        if (target_req->range_end > hint->offset + content_length - 1) {
+          GST_LOG ("Range end %" G_GINT64_FORMAT " is beyond the end (%"
+              G_GINT64_FORMAT ") of this preload", target_req->range_end,
+              hint->offset + content_length - 1);
+          continue;
+        }
+      }
+    }
+
+    GST_DEBUG ("Found a matching preload type %d uri: %s, range start:%"
+        G_GINT64_FORMAT " size %" G_GINT64_FORMAT, hint->hint_type, hint->uri,
+        hint->offset, hint->size);
+
+    if (preload_req->target_request != NULL) {
+      DownloadRequest *old_request = preload_req->target_request;
+
+      /* Detach the existing target request */
+      if (old_request != target_req) {
+        download_request_lock (old_request);
+        old_request->state = DOWNLOAD_REQUEST_STATE_UNSENT;
+        download_request_despatch_completion (old_request);
+        download_request_unlock (old_request);
+      }
+
+      download_request_unref (old_request);
+      preload_req->target_request = NULL;
+    }
+
+    /* Attach the new target request and despatch any available data */
+    preload_req->target_cur_offset = target_req->range_start;
+    preload_req->target_request = download_request_ref (target_req);
+
+    download_request_lock (target_req);
+    target_req->state = DOWNLOAD_REQUEST_STATE_UNSENT;
+    download_request_begin_download (target_req);
+    download_request_unlock (target_req);
+
+    gst_hls_demux_preloader_despatch (preload_req, FALSE);
+    return TRUE;
+  }
+
+  return FALSE;
+}
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-preloader.h b/ext/adaptivedemux2/hls/gsthlsdemux-preloader.h
new file mode 100644
index 0000000000000000000000000000000000000000..506655ab796df7cfe059c009f081209929d13a3f
--- /dev/null
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-preloader.h
@@ -0,0 +1,48 @@
+/* GStreamer
+ Copyright (C) 2022 Jan Schmidt <jan@centricular.com>
+ *
+ * gsthlsdemux-preloader.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef __GST_HLS_DEMUX_PRELOADER_H__
+#define __GST_HLS_DEMUX_PRELOADER_H__
+
+#include <glib.h>
+
+#include "m3u8.h"
+
+#include "downloadrequest.h"
+#include "downloadhelper.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GstHLSDemuxPreloader GstHLSDemuxPreloader;
+
+struct _GstHLSDemuxPreloader {
+  DownloadHelper *download_helper; /* Owned by the demuxer */
+  GPtrArray *active_preloads;
+};
+
+GstHLSDemuxPreloader *gst_hls_demux_preloader_new (DownloadHelper *download_helper);
+void gst_hls_demux_preloader_free (GstHLSDemuxPreloader *preloader);
+void gst_hls_demux_preloader_load (GstHLSDemuxPreloader *preloader, GstM3U8PreloadHint *hint, const gchar *referrer_uri);
+void gst_hls_demux_preloader_cancel (GstHLSDemuxPreloader *preloader, GstM3U8PreloadHintType hint_types);
+
+gboolean gst_hls_demux_preloader_provide_request (GstHLSDemuxPreloader *preloader, DownloadRequest *target_req);
+
+G_END_DECLS
+#endif /* __GST_HLS_DEMUX_PRELOADER_H__ */
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-stream.c b/ext/adaptivedemux2/hls/gsthlsdemux-stream.c
new file mode 100644
index 0000000000000000000000000000000000000000..9cd82c7c64255b138146564a9e93ed4cab710d8e
--- /dev/null
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-stream.c
@@ -0,0 +1,2164 @@
+/* GStreamer
+ * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
+ * Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ *  Author: Youness Alaoui <youness.alaoui@collabora.co.uk>, Collabora Ltd.
+ *  Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
+ *
+ * Copyright (C) 2021-2022 Centricular Ltd
+ *   Author: Edward Hervey <edward@centricular.com>
+ *   Author: Jan Schmidt <jan@centricular.com>
+ *
+ * gsthlsdemux-stream.c:
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+#include <gst/base/gsttypefindhelper.h>
+#include <gst/tag/tag.h>
+#include <glib/gi18n-lib.h>
+
+#include "gsthlsdemux.h"
+#include "gsthlsdemux-stream.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_hls_demux2_debug);
+#define GST_CAT_DEFAULT gst_hls_demux2_debug
+
+/* Maximum values for mpeg-ts DTS values */
+#define MPEG_TS_MAX_PTS (((((guint64)1) << 33) * (guint64)100000) / 9)
+
+static GstBuffer *gst_hls_demux_decrypt_fragment (GstHLSDemux * demux,
+    GstHLSDemuxStream * stream, GstBuffer * encrypted_buffer, GError ** err);
+static gboolean
+gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
+    const guint8 * key_data, const guint8 * iv_data);
+static void gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream);
+
+static gboolean
+gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn
+gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream
+    * stream, GstBuffer * buffer);
+
+static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
+    * stream);
+static GstFlowReturn
+gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn
+gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
+static GstFlowReturn
+gst_hls_demux_stream_submit_request (GstAdaptiveDemux2Stream * stream,
+    DownloadRequest * download_req);
+static void gst_hls_demux_stream_start (GstAdaptiveDemux2Stream * stream);
+static void gst_hls_demux_stream_stop (GstAdaptiveDemux2Stream * stream);
+static void gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream *
+    stream);
+static gboolean gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
+    stream, guint64 bitrate);
+static GstClockTime
+gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
+
+static void gst_hls_demux_stream_finalize (GObject * object);
+
+#define gst_hls_demux_stream_parent_class stream_parent_class
+G_DEFINE_TYPE (GstHLSDemuxStream, gst_hls_demux_stream,
+    GST_TYPE_ADAPTIVE_DEMUX2_STREAM);
+
+static void
+gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
+      GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
+
+  gobject_class->finalize = gst_hls_demux_stream_finalize;
+
+  adaptivedemux2stream_class->update_fragment_info =
+      gst_hls_demux_stream_update_fragment_info;
+  adaptivedemux2stream_class->submit_request =
+      gst_hls_demux_stream_submit_request;
+  adaptivedemux2stream_class->has_next_fragment =
+      gst_hls_demux_stream_has_next_fragment;
+  adaptivedemux2stream_class->stream_seek = gst_hls_demux_stream_seek;
+  adaptivedemux2stream_class->advance_fragment =
+      gst_hls_demux_stream_advance_fragment;
+  adaptivedemux2stream_class->select_bitrate =
+      gst_hls_demux_stream_select_bitrate;
+  adaptivedemux2stream_class->start = gst_hls_demux_stream_start;
+  adaptivedemux2stream_class->stop = gst_hls_demux_stream_stop;
+  adaptivedemux2stream_class->create_tracks =
+      gst_hls_demux_stream_create_tracks;
+
+  adaptivedemux2stream_class->start_fragment =
+      gst_hls_demux_stream_start_fragment;
+  adaptivedemux2stream_class->finish_fragment =
+      gst_hls_demux_stream_finish_fragment;
+  adaptivedemux2stream_class->data_received =
+      gst_hls_demux_stream_data_received;
+  adaptivedemux2stream_class->get_presentation_offset =
+      gst_hls_demux_stream_get_presentation_offset;
+
+}
+
+static void
+gst_hls_demux_stream_init (GstHLSDemuxStream * stream)
+{
+  stream->parser_type = GST_HLS_PARSER_NONE;
+  stream->do_typefind = TRUE;
+  stream->reset_pts = TRUE;
+  stream->presentation_offset = 60 * GST_SECOND;
+  stream->pdt_tag_sent = FALSE;
+}
+
+void
+gst_hls_demux_stream_clear_pending_data (GstHLSDemuxStream * hls_stream,
+    gboolean force)
+{
+  GST_DEBUG_OBJECT (hls_stream, "force : %d", force);
+  if (hls_stream->pending_encrypted_data)
+    gst_adapter_clear (hls_stream->pending_encrypted_data);
+  gst_buffer_replace (&hls_stream->pending_decrypted_buffer, NULL);
+  gst_buffer_replace (&hls_stream->pending_typefind_buffer, NULL);
+  if (force || !hls_stream->pending_data_is_header) {
+    gst_buffer_replace (&hls_stream->pending_segment_data, NULL);
+    hls_stream->pending_data_is_header = FALSE;
+  }
+  hls_stream->current_offset = -1;
+  hls_stream->process_buffer_content = TRUE;
+  gst_hls_demux_stream_decrypt_end (hls_stream);
+}
+
+GstFlowReturn
+gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward,
+    GstSeekFlags flags, GstClockTimeDiff ts, GstClockTimeDiff * final_ts)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
+
+  GST_DEBUG_OBJECT (stream,
+      "is_variant:%d media:%p current_variant:%p forward:%d ts:%"
+      GST_TIME_FORMAT, hls_stream->is_variant, hls_stream->current_rendition,
+      hlsdemux->current_variant, forward, GST_TIME_ARGS (ts));
+
+  /* If this stream doesn't have a playlist yet, we can't seek on it */
+  if (!hls_stream->playlist_fetched) {
+    return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
+  }
+
+  /* Allow jumping to partial segments in the last 2 segments in LL-HLS */
+  if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (hls_stream->playlist))
+    flags |= GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL;
+
+  GstM3U8SeekResult seek_result;
+  if (gst_hls_media_playlist_seek (hls_stream->playlist, forward, flags, ts,
+          &seek_result)) {
+    if (hls_stream->current_segment)
+      gst_m3u8_media_segment_unref (hls_stream->current_segment);
+    hls_stream->current_segment = seek_result.segment;
+    hls_stream->in_partial_segments = seek_result.found_partial_segment;
+    hls_stream->part_idx = seek_result.part_idx;
+
+    hls_stream->reset_pts = TRUE;
+    if (final_ts)
+      *final_ts = seek_result.stream_time;
+  } else {
+    GST_WARNING_OBJECT (stream, "Seeking failed");
+    ret = GST_FLOW_ERROR;
+  }
+
+  return ret;
+}
+
+static GstCaps *
+get_caps_of_stream_type (GstCaps * full_caps, GstStreamType streamtype)
+{
+  GstCaps *ret = NULL;
+
+  guint i;
+  for (i = 0; i < gst_caps_get_size (full_caps); i++) {
+    GstStructure *st = gst_caps_get_structure (full_caps, i);
+
+    if (gst_hls_get_stream_type_from_structure (st) == streamtype) {
+      ret = gst_caps_new_empty ();
+      gst_caps_append_structure (ret, gst_structure_copy (st));
+      break;
+    }
+  }
+
+  return ret;
+}
+
+static GstHLSRenditionStream *
+find_uriless_rendition (GstHLSDemux * demux, GstStreamType stream_type)
+{
+  GList *tmp;
+
+  for (tmp = demux->master->renditions; tmp; tmp = tmp->next) {
+    GstHLSRenditionStream *media = tmp->data;
+    if (media->uri == NULL &&
+        gst_stream_type_from_hls_type (media->mtype) == stream_type)
+      return media;
+  }
+  return NULL;
+}
+
+static void
+gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
+  GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream;
+  guint i;
+  GstStreamType uriless_types = 0;
+  GstCaps *variant_caps = NULL;
+
+  GST_DEBUG_OBJECT (stream, "Update tracks of variant stream");
+
+  if (hlsdemux->master->have_codecs) {
+    variant_caps = gst_hls_master_playlist_get_common_caps (hlsdemux->master);
+  }
+
+  /* Use the stream->stream_collection and manifest to create the appropriate tracks */
+  for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
+      i++) {
+    GstStream *gst_stream =
+        gst_stream_collection_get_stream (stream->stream_collection, i);
+    GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
+    GstAdaptiveDemuxTrack *track;
+    GstHLSRenditionStream *embedded_media = NULL;
+    /* tracks from the variant streams should be prefered over those provided by renditions */
+    GstStreamFlags flags =
+        gst_stream_get_stream_flags (gst_stream) | GST_STREAM_FLAG_SELECT;
+    GstCaps *manifest_caps = NULL;
+
+    if (stream_type == GST_STREAM_TYPE_UNKNOWN)
+      continue;
+
+    if (variant_caps)
+      manifest_caps = get_caps_of_stream_type (variant_caps, stream_type);
+    hlsdemux_stream->rendition_type |= stream_type;
+
+    if ((uriless_types & stream_type) == 0) {
+      /* Do we have a uriless media for this stream type */
+      /* Find if there is a rendition without URI, it will be provided by this variant */
+      embedded_media = find_uriless_rendition (hlsdemux, stream_type);
+      /* Remember we used this type for a embedded media */
+      uriless_types |= stream_type;
+    }
+
+    if (embedded_media) {
+      GstTagList *tags = gst_stream_get_tags (gst_stream);
+      GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
+          embedded_media->name);
+      track =
+          gst_hls_demux_new_track_for_rendition (hlsdemux, embedded_media,
+          manifest_caps, flags,
+          tags ? gst_tag_list_make_writable (tags) : tags);
+    } else {
+      gchar *stream_id;
+      stream_id =
+          g_strdup_printf ("main-%s-%d", gst_stream_type_get_name (stream_type),
+          i);
+
+      GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
+          stream_id);
+      track =
+          gst_adaptive_demux_track_new (stream->demux, stream_type,
+          flags, stream_id, manifest_caps, NULL);
+      g_free (stream_id);
+    }
+    track->upstream_stream_id =
+        g_strdup (gst_stream_get_stream_id (gst_stream));
+    gst_adaptive_demux2_stream_add_track (stream, track);
+    gst_adaptive_demux_track_unref (track);
+  }
+
+  if (variant_caps)
+    gst_caps_unref (variant_caps);
+
+  /* Update the stream object with rendition types.
+   * FIXME: rendition_type could be removed */
+  stream->stream_type = hlsdemux_stream->rendition_type;
+}
+
+static gboolean
+gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
+  const GstHLSKey *key;
+  GstHLSMediaPlaylist *m3u8;
+
+  GST_DEBUG_OBJECT (stream, "Fragment starting");
+
+  gst_hls_demux_stream_clear_pending_data (hls_stream, FALSE);
+
+  /* If no decryption is needed, there's nothing to be done here */
+  if (hls_stream->current_key == NULL)
+    return TRUE;
+
+  m3u8 = hls_stream->playlist;
+
+  key = gst_hls_demux_get_key (hlsdemux, hls_stream->current_key,
+      m3u8->uri, m3u8->allowcache);
+
+  if (key == NULL)
+    goto key_failed;
+
+  if (!gst_hls_demux_stream_decrypt_start (hls_stream, key->data,
+          hls_stream->current_iv))
+    goto decrypt_start_failed;
+
+  return TRUE;
+
+key_failed:
+  {
+    GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT_NOKEY,
+        ("Couldn't retrieve key for decryption"), (NULL));
+    GST_WARNING_OBJECT (hlsdemux, "Failed to decrypt data");
+    return FALSE;
+  }
+decrypt_start_failed:
+  {
+    GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT, ("Failed to start decrypt"),
+        ("Couldn't set key and IV or plugin was built without crypto library"));
+    return FALSE;
+  }
+}
+
+static GstHLSParserType
+caps_to_parser_type (const GstCaps * caps)
+{
+  const GstStructure *s = gst_caps_get_structure (caps, 0);
+
+  if (gst_structure_has_name (s, "video/mpegts"))
+    return GST_HLS_PARSER_MPEGTS;
+  if (gst_structure_has_name (s, "application/x-id3"))
+    return GST_HLS_PARSER_ID3;
+  if (gst_structure_has_name (s, "application/x-subtitle-vtt"))
+    return GST_HLS_PARSER_WEBVTT;
+  if (gst_structure_has_name (s, "video/quicktime"))
+    return GST_HLS_PARSER_ISOBMFF;
+
+  return GST_HLS_PARSER_NONE;
+}
+
+/* Identify the nature of data for this stream
+ *
+ * Will also setup the appropriate parser (tsreader) if needed
+ *
+ * Consumes the input buffer when it returns FALSE, but
+ * replaces / returns the input buffer in the `buffer` parameter
+ * when it returns TRUE.
+ *
+ * Returns TRUE if we are done with typefinding */
+static gboolean
+gst_hls_demux_typefind_stream (GstHLSDemux * hlsdemux,
+    GstAdaptiveDemux2Stream * stream, GstBuffer ** out_buffer, gboolean at_eos,
+    GstFlowReturn * ret)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);   // FIXME: pass HlsStream into function
+  GstCaps *caps = NULL;
+  guint buffer_size;
+  GstTypeFindProbability prob = GST_TYPE_FIND_NONE;
+  GstMapInfo info;
+  GstBuffer *buffer = *out_buffer;
+
+  if (hls_stream->pending_typefind_buffer) {
+    /* Append to the existing typefind buffer and create a new one that
+     * we'll return (or consume below) */
+    buffer = *out_buffer =
+        gst_buffer_append (hls_stream->pending_typefind_buffer, buffer);
+    hls_stream->pending_typefind_buffer = NULL;
+  }
+
+  gst_buffer_map (buffer, &info, GST_MAP_READ);
+  buffer_size = info.size;
+
+  /* Typefind could miss if buffer is too small. In this case we
+   * will retry later */
+  if (buffer_size >= (2 * 1024) || at_eos) {
+    caps =
+        gst_type_find_helper_for_data (GST_OBJECT_CAST (hlsdemux), info.data,
+        info.size, &prob);
+  }
+
+  if (G_UNLIKELY (!caps)) {
+    /* Won't need this mapping any more all paths return inside this if() */
+    gst_buffer_unmap (buffer, &info);
+
+    /* Only fail typefinding if we already a good amount of data
+     * and we still don't know the type */
+    if (buffer_size > (2 * 1024 * 1024) || at_eos) {
+      GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
+          ("Could not determine type of stream"), (NULL));
+      gst_buffer_unref (buffer);
+      *ret = GST_FLOW_NOT_NEGOTIATED;
+    } else {
+      GST_LOG_OBJECT (stream, "Not enough data to typefind");
+      hls_stream->pending_typefind_buffer = buffer;     /* Transfer the ref */
+      *ret = GST_FLOW_OK;
+    }
+    *out_buffer = NULL;
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (stream,
+      "Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob);
+
+  if (hls_stream->parser_type == GST_HLS_PARSER_NONE) {
+    hls_stream->parser_type = caps_to_parser_type (caps);
+    if (hls_stream->parser_type == GST_HLS_PARSER_NONE) {
+      GST_WARNING_OBJECT (stream,
+          "Unsupported stream type %" GST_PTR_FORMAT, caps);
+      GST_MEMDUMP_OBJECT (stream, "unknown data", info.data,
+          MIN (info.size, 128));
+      gst_buffer_unref (buffer);
+      *ret = GST_FLOW_ERROR;
+      return FALSE;
+    }
+    if (hls_stream->parser_type == GST_HLS_PARSER_ISOBMFF)
+      hls_stream->presentation_offset = 0;
+  }
+
+  gst_adaptive_demux2_stream_set_caps (stream, caps);
+
+  hls_stream->do_typefind = FALSE;
+
+  gst_buffer_unmap (buffer, &info);
+
+  /* We are done with typefinding. Doesn't consume the input buffer */
+  *ret = GST_FLOW_OK;
+  return TRUE;
+}
+
+/* Compute the stream time for the given internal time, based on the provided
+ * time map.
+ *
+ * Will handle mpeg-ts wraparound. */
+GstClockTimeDiff
+gst_hls_internal_to_stream_time (GstHLSTimeMap * map,
+    GstClockTime internal_time)
+{
+  if (map->internal_time == GST_CLOCK_TIME_NONE)
+    return GST_CLOCK_STIME_NONE;
+
+  /* Handle MPEG-TS Wraparound */
+  if (internal_time < map->internal_time &&
+      map->internal_time - internal_time > (MPEG_TS_MAX_PTS / 2))
+    internal_time += MPEG_TS_MAX_PTS;
+
+  return (map->stream_time + internal_time - map->internal_time);
+}
+
+/* Handle the internal time discovered on a segment.
+ *
+ * This function is called by the individual buffer parsers once they have
+ * extracted that internal time (which is most of the time based on mpegts time,
+ * but can also be ISOBMFF pts).
+ *
+ * This will update the time map when appropriate.
+ *
+ * If a synchronization issue is detected, the appropriate steps will be taken
+ * and the RESYNC return value will be returned
+ */
+GstHLSParserResult
+gst_hlsdemux_stream_handle_internal_time (GstHLSDemuxStream * hls_stream,
+    GstClockTime internal_time)
+{
+  GstM3U8MediaSegment *current_segment = hls_stream->current_segment;
+  GstHLSTimeMap *map;
+  GstClockTimeDiff current_stream_time;
+  GstClockTimeDiff real_stream_time, difference;
+
+  g_return_val_if_fail (current_segment != NULL, GST_HLS_PARSER_RESULT_ERROR);
+
+  current_stream_time = current_segment->stream_time;
+  if (hls_stream->in_partial_segments) {
+    /* If the current partial segment is valid, update the stream current position to the partial
+     * segment stream_time, otherwise leave it alone and fix it up later when we resync */
+    if (current_segment->partial_segments
+        && hls_stream->part_idx < current_segment->partial_segments->len) {
+      GstM3U8PartialSegment *part =
+          g_ptr_array_index (current_segment->partial_segments,
+          hls_stream->part_idx);
+      current_stream_time = part->stream_time;
+    }
+  }
+
+  GST_DEBUG_OBJECT (hls_stream,
+      "Got internal time %" GST_TIME_FORMAT " for current segment stream time %"
+      GST_STIME_FORMAT, GST_TIME_ARGS (internal_time),
+      GST_STIME_ARGS (current_stream_time));
+
+  GstHLSDemux *demux =
+      GST_HLS_DEMUX_CAST (GST_ADAPTIVE_DEMUX2_STREAM_CAST (hls_stream)->demux);
+  map = gst_hls_demux_find_time_map (demux, current_segment->discont_sequence);
+
+  /* Time mappings will always be created upon initial parsing and when advancing */
+  g_assert (map);
+
+  /* Handle the first internal time of a discont sequence. We can only store/use
+   * those values for variant streams. */
+  if (!GST_CLOCK_TIME_IS_VALID (map->internal_time)) {
+    if (!hls_stream->is_variant) {
+      GST_WARNING_OBJECT (hls_stream,
+          "Got data from a new discont sequence on a rendition stream, can't validate stream time");
+      return GST_HLS_PARSER_RESULT_DONE;
+    }
+    GST_DEBUG_OBJECT (hls_stream,
+        "Updating time map dsn:%" G_GINT64_FORMAT " stream_time:%"
+        GST_STIME_FORMAT " internal_time:%" GST_TIME_FORMAT, map->dsn,
+        GST_STIME_ARGS (current_stream_time), GST_TIME_ARGS (internal_time));
+    /* The stream time for a mapping should always be positive ! */
+    g_assert (current_stream_time >= 0);
+
+    if (hls_stream->parser_type == GST_HLS_PARSER_ISOBMFF)
+      hls_stream->presentation_offset = internal_time - current_stream_time;
+
+    gst_time_map_set_values (map, current_stream_time, internal_time,
+        current_segment->datetime);
+
+    gst_hls_demux_start_rendition_streams (demux);
+    return GST_HLS_PARSER_RESULT_DONE;
+  }
+
+  /* The information in a discont is always valid */
+  if (current_segment->discont) {
+    GST_DEBUG_OBJECT (hls_stream,
+        "DISCONT segment, Updating time map to stream_time:%" GST_STIME_FORMAT
+        " internal_time:%" GST_TIME_FORMAT,
+        GST_STIME_ARGS (current_stream_time), GST_TIME_ARGS (internal_time));
+    gst_time_map_set_values (map, current_stream_time, internal_time,
+        current_segment->datetime);
+    return GST_HLS_PARSER_RESULT_DONE;
+  }
+
+  /* Check if the segment is the expected one */
+  real_stream_time = gst_hls_internal_to_stream_time (map, internal_time);
+  difference = current_stream_time - real_stream_time;
+  GST_DEBUG_OBJECT (hls_stream,
+      "Segment contains stream time %" GST_STIME_FORMAT
+      " difference against expected : %" GST_STIME_FORMAT,
+      GST_STIME_ARGS (real_stream_time), GST_STIME_ARGS (difference));
+
+  /* We allow a tolerance of 3-4 frames between the estimated and observed
+   * stream time. */
+  if (ABS (difference) > 100 * GST_MSECOND) {
+    GstClockTimeDiff wrong_position_threshold =
+        hls_stream->current_segment->duration / 2;
+
+    /* Update the value */
+    GST_DEBUG_OBJECT (hls_stream,
+        "Updating current stream time to %" GST_STIME_FORMAT,
+        GST_STIME_ARGS (real_stream_time));
+
+    /* For LL-HLS, make sure to update and recalculate stream time from
+     * the right partial segment if playing one */
+    if (hls_stream->in_partial_segments && hls_stream->part_idx != 0) {
+      if (current_segment->partial_segments
+          && hls_stream->part_idx < current_segment->partial_segments->len) {
+        GstM3U8PartialSegment *part =
+            g_ptr_array_index (current_segment->partial_segments,
+            hls_stream->part_idx);
+        part->stream_time = real_stream_time;
+
+        gst_hls_media_playlist_recalculate_stream_time_from_part
+            (hls_stream->playlist, hls_stream->current_segment,
+            hls_stream->part_idx);
+
+        /* When playing partial segments, the "Wrong position" threshold should be
+         * half the part duration */
+        wrong_position_threshold = part->duration / 2;
+      }
+    } else {
+      /* Aligned to the start of the segment, update there */
+      current_segment->stream_time = real_stream_time;
+      gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist,
+          hls_stream->current_segment);
+    }
+    gst_hls_media_playlist_dump (hls_stream->playlist);
+
+    if (ABS (difference) > wrong_position_threshold) {
+      GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) hls_stream;
+      GstM3U8SeekResult seek_result;
+
+      /* We are at the wrong segment, try to figure out the *actual* segment */
+      GST_DEBUG_OBJECT (hls_stream,
+          "Trying to find the correct segment in the playlist for %"
+          GST_STIME_FORMAT, GST_STIME_ARGS (current_stream_time));
+      if (gst_hls_media_playlist_find_position (hls_stream->playlist,
+              current_stream_time, hls_stream->in_partial_segments,
+              &seek_result)) {
+
+        GST_DEBUG_OBJECT (hls_stream, "Synced to position %" GST_STIME_FORMAT,
+            GST_STIME_ARGS (seek_result.stream_time));
+
+        gst_m3u8_media_segment_unref (hls_stream->current_segment);
+        hls_stream->current_segment = seek_result.segment;
+        hls_stream->in_partial_segments = seek_result.found_partial_segment;
+        hls_stream->part_idx = seek_result.part_idx;
+
+        /* Ask parent class to restart this fragment */
+        return GST_HLS_PARSER_RESULT_RESYNC;
+      }
+
+      GST_WARNING_OBJECT (hls_stream,
+          "Could not find a replacement stream, carrying on with segment");
+      stream->discont = TRUE;
+      stream->fragment.stream_time = current_stream_time;
+      gst_time_map_set_values (map, current_stream_time, internal_time,
+          hls_stream->current_segment->datetime);
+    }
+  }
+
+  return GST_HLS_PARSER_RESULT_DONE;
+}
+
+static GstHLSParserResult
+gst_hls_demux_handle_buffer_content (GstHLSDemux * demux,
+    GstHLSDemuxStream * hls_stream, gboolean draining, GstBuffer ** buffer)
+{
+  GstHLSTimeMap *map;
+  GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) hls_stream;
+  GstClockTimeDiff current_stream_time =
+      hls_stream->current_segment->stream_time;
+  GstClockTime current_duration = hls_stream->current_segment->duration;
+  GstHLSParserResult parser_ret;
+
+  GST_LOG_OBJECT (stream,
+      "stream_time:%" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT
+      " discont:%d draining:%d header:%d index:%d",
+      GST_STIME_ARGS (current_stream_time), GST_TIME_ARGS (current_duration),
+      hls_stream->current_segment->discont, draining,
+      stream->downloading_header, stream->downloading_index);
+
+  /* FIXME : Replace the boolean parser return value (and this function's return
+   *  value) by an enum which clearly specifies whether:
+   *
+   * * The content parsing happened succesfully and it no longer needs to be
+   *   called for the remainder of this fragment
+   * * More data is needed in order to parse the data
+   * * There was a fatal error parsing the contents (ex: invalid/incompatible
+   *   content)
+   * * The computed fragment stream time is out of sync
+   */
+
+  g_assert (demux->mappings);
+  map =
+      gst_hls_demux_find_time_map (demux,
+      hls_stream->current_segment->discont_sequence);
+  if (!map) {
+    /* For rendition streams, we can't do anything without time mapping */
+    if (!hls_stream->is_variant) {
+      GST_DEBUG_OBJECT (stream,
+          "No available time mapping for dsn:%" G_GINT64_FORMAT
+          " using estimated stream time",
+          hls_stream->current_segment->discont_sequence);
+      goto out_done;
+    }
+
+    /* Variants will be able to fill in the the time mapping, so we can carry on without a time mapping */
+  } else {
+    GST_DEBUG_OBJECT (stream,
+        "Using mapping dsn:%" G_GINT64_FORMAT " stream_time:%" GST_TIME_FORMAT
+        " internal_time:%" GST_TIME_FORMAT, map->dsn,
+        GST_TIME_ARGS (map->stream_time), GST_TIME_ARGS (map->internal_time));
+  }
+
+  switch (hls_stream->parser_type) {
+    case GST_HLS_PARSER_MPEGTS:
+      parser_ret =
+          gst_hlsdemux_handle_content_mpegts (demux, hls_stream, draining,
+          buffer);
+      break;
+    case GST_HLS_PARSER_ID3:
+      parser_ret =
+          gst_hlsdemux_handle_content_id3 (demux, hls_stream, draining, buffer);
+      break;
+    case GST_HLS_PARSER_WEBVTT:
+    {
+      /* Furthermore it will handle timeshifting itself */
+      parser_ret =
+          gst_hlsdemux_handle_content_webvtt (demux, hls_stream, draining,
+          buffer);
+      break;
+    }
+    case GST_HLS_PARSER_ISOBMFF:
+      parser_ret =
+          gst_hlsdemux_handle_content_isobmff (demux, hls_stream, draining,
+          buffer);
+      break;
+    case GST_HLS_PARSER_NONE:
+    default:
+    {
+      GST_ERROR_OBJECT (stream, "Unknown stream type");
+      goto out_error;
+    }
+  }
+
+  if (parser_ret == GST_HLS_PARSER_RESULT_NEED_MORE_DATA) {
+    if (stream->downloading_index || stream->downloading_header)
+      goto out_need_more;
+    /* Else if we're draining, it's an error */
+    if (draining)
+      goto out_error;
+    /* Else we just need more data */
+    goto out_need_more;
+  }
+
+  if (parser_ret == GST_HLS_PARSER_RESULT_ERROR)
+    goto out_error;
+
+  if (parser_ret == GST_HLS_PARSER_RESULT_RESYNC)
+    goto out_resync;
+
+out_done:
+  GST_DEBUG_OBJECT (stream, "Done. Finished parsing");
+  return GST_HLS_PARSER_RESULT_DONE;
+
+out_error:
+  GST_DEBUG_OBJECT (stream, "Done. Error while parsing");
+  return GST_HLS_PARSER_RESULT_ERROR;
+
+out_need_more:
+  GST_DEBUG_OBJECT (stream, "Done. Need more data");
+  return GST_HLS_PARSER_RESULT_NEED_MORE_DATA;
+
+out_resync:
+  GST_DEBUG_OBJECT (stream, "Done. Resync required");
+  return GST_HLS_PARSER_RESULT_RESYNC;
+}
+
+static GstFlowReturn
+gst_hls_demux_stream_handle_buffer (GstAdaptiveDemux2Stream * stream,
+    GstBuffer * buffer, gboolean at_eos)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);   // FIXME: pass HlsStream into function
+  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstBuffer *pending_header_data = NULL;
+
+  /* If current segment is not present, this means that a playlist update
+   * happened between the moment ::update_fragment_info() was called and the
+   * moment we received data. And that playlist update couldn't match the
+   * current position. This will happen in live playback when we are downloading
+   * too slowly, therefore we try to "catch up" back to live
+   */
+  if (hls_stream->current_segment == NULL) {
+    GST_WARNING_OBJECT (stream, "Lost sync");
+    /* Drop the buffer */
+    gst_buffer_unref (buffer);
+    return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
+  }
+
+  GST_DEBUG_OBJECT (stream,
+      "buffer:%p at_eos:%d do_typefind:%d uri:%s", buffer, at_eos,
+      hls_stream->do_typefind, GST_STR_NULL (stream->fragment.uri));
+
+  if (buffer == NULL)
+    goto out;
+
+  /* If we need to do typefind and we're not done with it (or we errored), return */
+  if (G_UNLIKELY (hls_stream->do_typefind) &&
+      !gst_hls_demux_typefind_stream (hlsdemux, stream, &buffer, at_eos,
+          &ret)) {
+    goto out;
+  }
+  g_assert (hls_stream->pending_typefind_buffer == NULL);
+
+  if (hls_stream->process_buffer_content) {
+    GstHLSParserResult parse_ret;
+
+    if (hls_stream->pending_segment_data) {
+      if (hls_stream->pending_data_is_header) {
+        /* Keep a copy of the header data in case we need to requeue it
+         * due to GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT below */
+        pending_header_data = gst_buffer_ref (hls_stream->pending_segment_data);
+      }
+      buffer = gst_buffer_append (hls_stream->pending_segment_data, buffer);
+      hls_stream->pending_segment_data = NULL;
+    }
+
+    /* Try to get the timing information */
+    parse_ret =
+        gst_hls_demux_handle_buffer_content (hlsdemux, hls_stream, at_eos,
+        &buffer);
+
+    switch (parse_ret) {
+      case GST_HLS_PARSER_RESULT_NEED_MORE_DATA:
+        /* If we don't have enough, store and return */
+        hls_stream->pending_segment_data = buffer;
+        hls_stream->pending_data_is_header =
+            (stream->downloading_header == TRUE);
+        if (hls_stream->pending_data_is_header)
+          stream->send_segment = TRUE;
+        goto out;
+      case GST_HLS_PARSER_RESULT_ERROR:
+        /* Error, drop buffer and return */
+        gst_buffer_unref (buffer);
+        ret = GST_FLOW_ERROR;
+        goto out;
+      case GST_HLS_PARSER_RESULT_RESYNC:
+        /* Resync, drop buffer and return */
+        gst_buffer_unref (buffer);
+        ret = GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT;
+        /* If we had a pending set of header data, requeue it */
+        if (pending_header_data != NULL) {
+          g_assert (hls_stream->pending_segment_data == NULL);
+
+          GST_DEBUG_OBJECT (hls_stream,
+              "Requeueing header data %" GST_PTR_FORMAT
+              " before returning RESTART_FRAGMENT", pending_header_data);
+          hls_stream->pending_segment_data = pending_header_data;
+          pending_header_data = NULL;
+        }
+        goto out;
+      case GST_HLS_PARSER_RESULT_DONE:
+        /* Done parsing, carry on */
+        hls_stream->process_buffer_content = FALSE;
+        break;
+    }
+  }
+
+  if (!buffer)
+    goto out;
+
+  buffer = gst_buffer_make_writable (buffer);
+
+  GST_BUFFER_OFFSET (buffer) = hls_stream->current_offset;
+  hls_stream->current_offset += gst_buffer_get_size (buffer);
+  GST_BUFFER_OFFSET_END (buffer) = hls_stream->current_offset;
+
+  GST_DEBUG_OBJECT (stream, "We have a buffer, pushing: %" GST_PTR_FORMAT,
+      buffer);
+
+  ret = gst_adaptive_demux2_stream_push_buffer (stream, buffer);
+
+out:
+  if (pending_header_data != NULL) {
+    /* Throw away the pending header data now. If it wasn't consumed above,
+     * we won't need it */
+    gst_buffer_unref (pending_header_data);
+  }
+
+  GST_DEBUG_OBJECT (stream, "Returning %s", gst_flow_get_name (ret));
+  return ret;
+}
+
+static GstFlowReturn
+gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);   // FIXME: pass HlsStream into function
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  GST_DEBUG_OBJECT (stream, "Finishing %ssegment uri:%s",
+      hls_stream->in_partial_segments ? "partial " : "",
+      GST_STR_NULL (stream->fragment.uri));
+
+  /* Drain all pending data */
+  if (hls_stream->current_key)
+    gst_hls_demux_stream_decrypt_end (hls_stream);
+
+  if (hls_stream->current_segment && stream->last_ret == GST_FLOW_OK) {
+    if (hls_stream->pending_decrypted_buffer) {
+      if (hls_stream->current_key) {
+        GstMapInfo info;
+        gssize unpadded_size;
+
+        /* Handle pkcs7 unpadding here */
+        gst_buffer_map (hls_stream->pending_decrypted_buffer, &info,
+            GST_MAP_READ);
+        unpadded_size = info.size - info.data[info.size - 1];
+        gst_buffer_unmap (hls_stream->pending_decrypted_buffer, &info);
+
+        gst_buffer_resize (hls_stream->pending_decrypted_buffer, 0,
+            unpadded_size);
+      }
+
+      ret =
+          gst_hls_demux_stream_handle_buffer (stream,
+          hls_stream->pending_decrypted_buffer, TRUE);
+      hls_stream->pending_decrypted_buffer = NULL;
+    }
+
+    if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) {
+      if (G_UNLIKELY (hls_stream->pending_typefind_buffer)) {
+        GstBuffer *buf = hls_stream->pending_typefind_buffer;
+        hls_stream->pending_typefind_buffer = NULL;
+
+        gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
+      }
+
+      if (hls_stream->pending_segment_data) {
+        GstBuffer *buf = hls_stream->pending_segment_data;
+        hls_stream->pending_segment_data = NULL;
+
+        ret = gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
+      }
+    }
+  }
+
+  gst_hls_demux_stream_clear_pending_data (hls_stream, FALSE);
+
+  if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
+    return GST_FLOW_OK;
+
+  if (hls_stream->current_segment == NULL) {
+    /* We can't advance, we just return OK for now and let the base class
+     * trigger a new download (or fail and resync itself) */
+    GST_DEBUG_OBJECT (stream, "Can't advance - current_segment is NULL");
+    return GST_FLOW_OK;
+  }
+
+  if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) {
+    GstClockTime duration = hls_stream->current_segment->duration;
+
+    /* We can update the stream current position with a more accurate value
+     * before advancing. Note that we don't have any period so we can set the
+     * stream_time as-is on the stream current position */
+    if (hls_stream->in_partial_segments) {
+      GstM3U8MediaSegment *cur_segment = hls_stream->current_segment;
+
+      /* If the current partial segment is valid, update the stream current position, otherwise
+       * leave it alone and fix it up later when we resync */
+      if (cur_segment->partial_segments
+          && hls_stream->part_idx < cur_segment->partial_segments->len) {
+        GstM3U8PartialSegment *part =
+            g_ptr_array_index (cur_segment->partial_segments,
+            hls_stream->part_idx);
+        stream->current_position = part->stream_time;
+        duration = part->duration;
+      }
+    } else {
+      stream->current_position = hls_stream->current_segment->stream_time;
+    }
+
+    return gst_adaptive_demux2_stream_advance_fragment (stream, duration);
+  }
+  return ret;
+}
+
+static GstFlowReturn
+gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
+    GstBuffer * buffer)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
+  GstM3U8MediaSegment *file = hls_stream->current_segment;
+
+  if (hls_stream->current_segment == NULL)
+    return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
+
+  if (hls_stream->current_offset == -1)
+    hls_stream->current_offset = 0;
+
+  /* Is it encrypted? */
+  if (hls_stream->current_key) {
+    GError *err = NULL;
+    gsize size;
+    GstBuffer *decrypted_buffer;
+    GstBuffer *tmp_buffer;
+
+    if (hls_stream->pending_encrypted_data == NULL)
+      hls_stream->pending_encrypted_data = gst_adapter_new ();
+
+    gst_adapter_push (hls_stream->pending_encrypted_data, buffer);
+    size = gst_adapter_available (hls_stream->pending_encrypted_data);
+
+    /* must be a multiple of 16 */
+    size &= (~0xF);
+
+    if (size == 0) {
+      return GST_FLOW_OK;
+    }
+
+    buffer = gst_adapter_take_buffer (hls_stream->pending_encrypted_data, size);
+    decrypted_buffer =
+        gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err);
+    if (err) {
+      GST_ELEMENT_ERROR (hlsdemux, STREAM, DECODE, ("Failed to decrypt buffer"),
+          ("decryption failed %s", err->message));
+      g_error_free (err);
+      return GST_FLOW_ERROR;
+    }
+
+    tmp_buffer = hls_stream->pending_decrypted_buffer;
+    hls_stream->pending_decrypted_buffer = decrypted_buffer;
+    buffer = tmp_buffer;
+    if (!buffer)
+      return GST_FLOW_OK;
+  }
+
+  if (!hls_stream->pdt_tag_sent && file != NULL && file->datetime != NULL) {
+    GstDateTime *pdt_time = gst_date_time_new_from_g_date_time (g_date_time_ref
+        (file->datetime));
+    gst_adaptive_demux2_stream_set_tags (stream,
+        gst_tag_list_new (GST_TAG_DATE_TIME, pdt_time, NULL));
+    gst_date_time_unref (pdt_time);
+    hls_stream->pdt_tag_sent = TRUE;
+  }
+
+
+  return gst_hls_demux_stream_handle_buffer (stream, buffer, FALSE);
+}
+
+static void
+gst_hls_demux_stream_finalize (GObject * object)
+{
+  GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) object;
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (object);
+  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
+
+  if (hls_stream == hlsdemux->main_stream)
+    hlsdemux->main_stream = NULL;
+
+  g_free (hls_stream->lang);
+  g_free (hls_stream->name);
+
+  if (hls_stream->playlist) {
+    gst_hls_media_playlist_unref (hls_stream->playlist);
+    hls_stream->playlist = NULL;
+  }
+
+  if (hls_stream->init_file) {
+    gst_m3u8_init_file_unref (hls_stream->init_file);
+    hls_stream->init_file = NULL;
+  }
+
+  if (hls_stream->pending_encrypted_data)
+    g_object_unref (hls_stream->pending_encrypted_data);
+
+  gst_buffer_replace (&hls_stream->pending_decrypted_buffer, NULL);
+  gst_buffer_replace (&hls_stream->pending_typefind_buffer, NULL);
+  gst_buffer_replace (&hls_stream->pending_segment_data, NULL);
+
+  if (hls_stream->playlistloader) {
+    gst_hls_demux_playlist_loader_stop (hls_stream->playlistloader);
+    gst_object_unparent (GST_OBJECT (hls_stream->playlistloader));
+    gst_object_unref (hls_stream->playlistloader);
+  }
+
+  if (hls_stream->preloader) {
+    gst_hls_demux_preloader_free (hls_stream->preloader);
+    hls_stream->preloader = NULL;
+  }
+
+  if (hls_stream->moov)
+    gst_isoff_moov_box_free (hls_stream->moov);
+
+  if (hls_stream->current_key) {
+    g_free (hls_stream->current_key);
+    hls_stream->current_key = NULL;
+  }
+  if (hls_stream->current_iv) {
+    g_free (hls_stream->current_iv);
+    hls_stream->current_iv = NULL;
+  }
+  if (hls_stream->current_rendition) {
+    gst_hls_rendition_stream_unref (hls_stream->current_rendition);
+    hls_stream->current_rendition = NULL;
+  }
+  if (hls_stream->pending_rendition) {
+    gst_hls_rendition_stream_unref (hls_stream->pending_rendition);
+    hls_stream->pending_rendition = NULL;
+  }
+
+  if (hls_stream->current_segment) {
+    gst_m3u8_media_segment_unref (hls_stream->current_segment);
+    hls_stream->current_segment = NULL;
+  }
+  gst_hls_demux_stream_decrypt_end (hls_stream);
+
+  G_OBJECT_CLASS (stream_parent_class)->finalize (object);
+}
+
+static gboolean
+gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
+
+  GST_DEBUG_OBJECT (stream, "has next ?");
+
+  if (hls_stream->current_segment == NULL)
+    return FALSE;
+
+  return gst_hls_media_playlist_has_next_fragment (hls_stream->playlist,
+      hls_stream->current_segment, stream->demux->segment.rate > 0);
+}
+
+static GstFlowReturn
+gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
+  GstM3U8MediaSegment *new_segment = NULL;
+
+  /* If we're playing partial segments, we need to continue
+   * doing that. We can only swap back to a full segment on a
+   * segment boundary */
+  if (hlsdemux_stream->in_partial_segments) {
+    /* Check if there's another partial segment in this fragment */
+    GstM3U8MediaSegment *cur_segment = hlsdemux_stream->current_segment;
+    guint avail_segments =
+        cur_segment->partial_segments !=
+        NULL ? cur_segment->partial_segments->len : 0;
+
+    if (hlsdemux_stream->part_idx + 1 < avail_segments) {
+      /* Advance to the next partial segment */
+      hlsdemux_stream->part_idx += 1;
+
+      GstM3U8PartialSegment *part =
+          g_ptr_array_index (cur_segment->partial_segments,
+          hlsdemux_stream->part_idx);
+
+      GST_DEBUG_OBJECT (stream,
+          "Advanced to partial segment sn:%" G_GINT64_FORMAT
+          " part %d stream_time:%" GST_STIME_FORMAT " uri:%s",
+          hlsdemux_stream->current_segment->sequence, hlsdemux_stream->part_idx,
+          GST_STIME_ARGS (part->stream_time), GST_STR_NULL (part->uri));
+
+      return GST_FLOW_OK;
+    } else if (cur_segment->partial_only) {
+      /* There's no partial segment available, because we're at the live edge */
+      GST_DEBUG_OBJECT (stream,
+          "Hit live edge playing partial segments. Will wait for playlist update.");
+      hlsdemux_stream->part_idx += 1;
+      return GST_FLOW_OK;
+    } else {
+      /* At the end of the partial segments for this full segment. Advance to the next full segment */
+      hlsdemux_stream->in_partial_segments = FALSE;
+      GST_DEBUG_OBJECT (stream,
+          "No more partial segments in current segment. Advancing");
+    }
+  }
+
+  GST_DEBUG_OBJECT (stream,
+      "Current segment sn:%" G_GINT64_FORMAT " stream_time:%" GST_STIME_FORMAT
+      " uri:%s", hlsdemux_stream->current_segment->sequence,
+      GST_STIME_ARGS (hlsdemux_stream->current_segment->stream_time),
+      GST_STR_NULL (hlsdemux_stream->current_segment->uri));
+
+  new_segment =
+      gst_hls_media_playlist_advance_fragment (hlsdemux_stream->playlist,
+      hlsdemux_stream->current_segment, stream->demux->segment.rate > 0);
+
+  if (new_segment) {
+    hlsdemux_stream->reset_pts = FALSE;
+    if (new_segment->discont_sequence !=
+        hlsdemux_stream->current_segment->discont_sequence)
+      gst_hls_demux_add_time_mapping (hlsdemux, new_segment->discont_sequence,
+          new_segment->stream_time, new_segment->datetime);
+    gst_m3u8_media_segment_unref (hlsdemux_stream->current_segment);
+    hlsdemux_stream->current_segment = new_segment;
+
+    /* In LL-HLS, handle advancing into the partial-only segment */
+    if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (hlsdemux_stream->playlist)
+        && new_segment->partial_only) {
+      hlsdemux_stream->in_partial_segments = TRUE;
+      hlsdemux_stream->part_idx = 0;
+
+      GstM3U8PartialSegment *new_part =
+          g_ptr_array_index (new_segment->partial_segments,
+          hlsdemux_stream->part_idx);
+
+      GST_DEBUG_OBJECT (stream,
+          "Advanced to partial segment sn:%" G_GINT64_FORMAT
+          " part %u stream_time:%" GST_STIME_FORMAT " uri:%s",
+          new_segment->sequence, hlsdemux_stream->part_idx,
+          GST_STIME_ARGS (new_part->stream_time), GST_STR_NULL (new_part->uri));
+      return GST_FLOW_OK;
+    }
+
+    GST_DEBUG_OBJECT (stream,
+        "Advanced to segment sn:%" G_GINT64_FORMAT " stream_time:%"
+        GST_STIME_FORMAT " uri:%s", hlsdemux_stream->current_segment->sequence,
+        GST_STIME_ARGS (hlsdemux_stream->current_segment->stream_time),
+        GST_STR_NULL (hlsdemux_stream->current_segment->uri));
+    return GST_FLOW_OK;
+  }
+
+  GST_LOG_OBJECT (stream, "Could not advance to next fragment");
+  if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (hlsdemux_stream->playlist)) {
+    gst_m3u8_media_segment_unref (hlsdemux_stream->current_segment);
+    hlsdemux_stream->current_segment = NULL;
+    hlsdemux_stream->in_partial_segments = FALSE;
+    return GST_FLOW_OK;
+  }
+
+  return GST_FLOW_EOS;
+}
+
+static void
+gst_hls_demux_stream_update_preloads (GstHLSDemuxStream * hlsdemux_stream)
+{
+  GstHLSMediaPlaylist *playlist = hlsdemux_stream->playlist;
+  gboolean preloads_allowed = GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist);
+
+  if (playlist->preload_hints == NULL || !preloads_allowed) {
+    if (hlsdemux_stream->preloader != NULL) {
+      /* Cancel any preloads, the new playlist doesn't have them */
+      gst_hls_demux_preloader_cancel (hlsdemux_stream->preloader,
+          M3U8_PRELOAD_HINT_ALL);
+    }
+    /* Nothing to preload */
+    return;
+  }
+
+  if (hlsdemux_stream->preloader == NULL) {
+    GstAdaptiveDemux *demux =
+        GST_ADAPTIVE_DEMUX2_STREAM (hlsdemux_stream)->demux;
+    hlsdemux_stream->preloader =
+        gst_hls_demux_preloader_new (demux->download_helper);
+    if (hlsdemux_stream->preloader == NULL) {
+      GST_WARNING_OBJECT (hlsdemux_stream, "Failed to create preload handler");
+      return;
+    }
+  }
+
+  /* The HLS spec says any extra preload hint of each type should be ignored */
+  GstM3U8PreloadHintType seen_types = 0;
+  guint idx;
+  for (idx = 0; idx < playlist->preload_hints->len; idx++) {
+    GstM3U8PreloadHint *hint = g_ptr_array_index (playlist->preload_hints, idx);
+    switch (hint->hint_type) {
+      case M3U8_PRELOAD_HINT_MAP:
+      case M3U8_PRELOAD_HINT_PART:
+        if (seen_types & hint->hint_type) {
+          continue;             /* Ignore preload hint type we've already seen */
+        }
+        seen_types |= hint->hint_type;
+        break;
+      default:
+        GST_FIXME_OBJECT (hlsdemux_stream, "Ignoring unknown preload type %d",
+            hint->hint_type);
+        continue;               /* Unknown hint type, ignore it */
+    }
+    gst_hls_demux_preloader_load (hlsdemux_stream->preloader, hint,
+        playlist->uri);
+  }
+}
+
+static GstFlowReturn
+gst_hls_demux_stream_submit_request (GstAdaptiveDemux2Stream * stream,
+    DownloadRequest * download_req)
+{
+  GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+
+  /* See if the request can be satisfied from a preload */
+  if (hlsdemux_stream->preloader != NULL) {
+    if (gst_hls_demux_preloader_provide_request (hlsdemux_stream->preloader,
+            download_req))
+      return GST_FLOW_OK;
+
+    /* We're about to request something, but it wasn't the active preload,
+     * so make sure that's been stopped / cancelled so we're not downloading
+     * two things in parallel. This usually means the playlist refresh
+     * took too long and the preload became obsolete */
+    if (stream->downloading_header) {
+      gst_hls_demux_preloader_cancel (hlsdemux_stream->preloader,
+          M3U8_PRELOAD_HINT_MAP);
+    } else {
+      gst_hls_demux_preloader_cancel (hlsdemux_stream->preloader,
+          M3U8_PRELOAD_HINT_PART);
+    }
+  }
+
+  return
+      GST_ADAPTIVE_DEMUX2_STREAM_CLASS (stream_parent_class)->submit_request
+      (stream, download_req);
+}
+
+static void
+gst_hls_demux_stream_handle_playlist_update (GstHLSDemuxStream * stream,
+    const gchar * new_playlist_uri, GstHLSMediaPlaylist * new_playlist)
+{
+  GstHLSDemux *demux = GST_HLS_DEMUX_STREAM_GET_DEMUX (stream);
+  gboolean found_segment_discont = FALSE;
+
+  /* Synchronize playlist with previous one. If we can't update the playlist
+   * timing and inform the base class that we lost sync */
+  if (stream->playlist) {
+    if (!gst_hls_media_playlist_sync_to_playlist (new_playlist,
+            stream->playlist, &found_segment_discont)) {
+      /* Failure to synchronize with the previous media playlist is only fatal for
+       * variant streams. */
+      if (stream->is_variant) {
+        GST_DEBUG_OBJECT (stream,
+            "Could not synchronize new variant playlist with previous one !");
+        goto lost_sync;
+      }
+
+      /* For rendition streams, we can attempt synchronization against the
+       * variant playlist which is constantly updated */
+      if (demux->main_stream->playlist
+          && !gst_hls_media_playlist_sync_to_playlist (new_playlist,
+              demux->main_stream->playlist, &found_segment_discont)) {
+        GST_DEBUG_OBJECT (stream,
+            "Could not do fallback synchronization of rendition stream to variant stream");
+        goto lost_sync;
+      }
+    }
+  } else {
+    found_segment_discont = TRUE;
+    if (!stream->is_variant && demux->main_stream->playlist) {
+      /* For initial rendition media playlist, attempt to synchronize the playlist
+       * against the variant stream. This is non-fatal if it fails. */
+      GST_DEBUG_OBJECT (stream,
+          "Attempting to synchronize initial rendition stream with variant stream");
+      gst_hls_media_playlist_sync_to_playlist (new_playlist,
+          demux->main_stream->playlist, NULL);
+    }
+  }
+
+  GST_DEBUG_OBJECT (stream, "Synchronized playlist. Update is discont : %d",
+      found_segment_discont);
+  if (found_segment_discont) {
+    stream->pending_discont = TRUE;
+  }
+
+  if (stream->current_segment) {
+    GstM3U8MediaSegment *new_segment;
+    GST_DEBUG_OBJECT (stream,
+        "Current segment sn:%" G_GINT64_FORMAT " stream_time:%" GST_STIME_FORMAT
+        " uri:%s", stream->current_segment->sequence,
+        GST_STIME_ARGS (stream->current_segment->stream_time),
+        GST_STR_NULL (stream->current_segment->uri));
+
+    /* Use best-effort techniques to find the corresponding current media segment
+     * in the new playlist. This might be off in some cases, but it doesn't matter
+     * since we will be checking the embedded timestamp later */
+    new_segment =
+        gst_hls_media_playlist_sync_to_segment (new_playlist,
+        stream->current_segment);
+
+    /* Handle LL-HLS partial segment sync by checking our partial segment
+     * still makes sense */
+    if (stream->in_partial_segments && new_segment) {
+      /* We must be either playing the trailing open-ended partial segment,
+       * or if we're playing partials from a complete segment, check that we
+       * still have a) partial segments attached (didn't get too old and
+       * the server removed them from the playlist) and b) we didn't advance
+       * beyond the end of that partial segment (when we advance past the live
+       * edge and increment part_idx, then the segment completes without
+       * adding any more partial segments) */
+      if (!new_segment->partial_only) {
+        if (new_segment->partial_segments == NULL) {
+          GST_DEBUG_OBJECT (stream,
+              "Partial segments we were playing became unavailable. Will try and resync");
+          stream->in_partial_segments = FALSE;
+          gst_m3u8_media_segment_unref (new_segment);
+          new_segment = NULL;
+        } else if (stream->part_idx >= new_segment->partial_segments->len) {
+          GST_DEBUG_OBJECT (stream,
+              "After playlist reload, there are no more partial segments to play in the current segment. Resyncing");
+          stream->in_partial_segments = FALSE;
+          gst_m3u8_media_segment_unref (new_segment);
+          new_segment = NULL;
+        }
+      }
+    }
+
+    if (new_segment) {
+      if (new_segment->discont_sequence !=
+          stream->current_segment->discont_sequence)
+        gst_hls_demux_add_time_mapping (demux, new_segment->discont_sequence,
+            new_segment->stream_time, new_segment->datetime);
+      /* This can happen in case of misaligned variants/renditions. Only warn about it */
+      if (new_segment->stream_time != stream->current_segment->stream_time)
+        GST_WARNING_OBJECT (stream,
+            "Returned segment stream time %" GST_STIME_FORMAT
+            " differs from current stream time %" GST_STIME_FORMAT,
+            GST_STIME_ARGS (new_segment->stream_time),
+            GST_STIME_ARGS (stream->current_segment->stream_time));
+    } else {
+      /* Not finding a matching segment only happens in live (otherwise we would
+       * have found a match by stream time) when we are at the live edge. This is normal*/
+      GST_DEBUG_OBJECT (stream, "Could not find a matching segment");
+    }
+    gst_m3u8_media_segment_unref (stream->current_segment);
+    stream->current_segment = new_segment;
+  } else {
+    GST_DEBUG_OBJECT (stream, "No current segment");
+  }
+
+  if (stream->is_variant) {
+    /* Updates on the variant playlist have some special requirements to
+     * set up the time mapping and initial stream config */
+    gst_hls_demux_handle_variant_playlist_update (demux, new_playlist_uri,
+        new_playlist);
+  } else if (stream->pending_rendition) {
+    /* Switching rendition configures a new playlist on the loader,
+     * and we should never get a callback for a stale download URI */
+    g_assert (!g_strcmp0 (stream->pending_rendition->uri, new_playlist_uri));
+
+    gst_hls_rendition_stream_unref (stream->current_rendition);
+    /* Stealing ref */
+    stream->current_rendition = stream->pending_rendition;
+    stream->pending_rendition = NULL;
+  }
+
+  if (stream->playlist)
+    gst_hls_media_playlist_unref (stream->playlist);
+  stream->playlist = gst_hls_media_playlist_ref (new_playlist);
+  stream->playlist_fetched = TRUE;
+
+  if (!GST_HLS_MEDIA_PLAYLIST_IS_LIVE (stream->playlist)) {
+    /* Make sure to cancel any preloads if a playlist isn't live after reload */
+    gst_hls_demux_stream_update_preloads (stream);
+  }
+
+  if (stream->current_segment) {
+    GST_DEBUG_OBJECT (stream,
+        "After update, current segment now sn:%" G_GINT64_FORMAT
+        " stream_time:%" GST_STIME_FORMAT " uri:%s",
+        stream->current_segment->sequence,
+        GST_STIME_ARGS (stream->current_segment->stream_time),
+        GST_STR_NULL (stream->current_segment->uri));
+  } else {
+    GST_DEBUG_OBJECT (stream, "No current segment selected");
+  }
+
+  GST_DEBUG_OBJECT (stream, "done");
+  return;
+
+  /* ERRORS */
+lost_sync:
+  {
+    /* Set new playlist, lost sync handler will know what to do with it */
+    if (stream->playlist)
+      gst_hls_media_playlist_unref (stream->playlist);
+    stream->playlist = new_playlist;
+    stream->playlist = gst_hls_media_playlist_ref (new_playlist);
+    stream->playlist_fetched = TRUE;
+    stream->pending_discont = TRUE;
+
+    gst_hls_demux_reset_for_lost_sync (demux);
+  }
+}
+
+static void
+on_playlist_update_success (GstHLSDemuxPlaylistLoader * pl,
+    const gchar * new_playlist_uri, GstHLSMediaPlaylist * new_playlist,
+    gpointer userdata)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (userdata);
+
+  gst_hls_demux_stream_handle_playlist_update (hls_stream,
+      new_playlist_uri, new_playlist);
+  gst_adaptive_demux2_stream_mark_prepared (GST_ADAPTIVE_DEMUX2_STREAM_CAST
+      (hls_stream));
+}
+
+static void
+on_playlist_update_error (GstHLSDemuxPlaylistLoader * pl,
+    const gchar * playlist_uri, gpointer userdata)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (userdata);
+
+  /* FIXME: How to handle rendition playlist update errors? There's
+   * not much we can do about it except throw an error */
+  if (hls_stream->is_variant) {
+    GstHLSDemux *demux = GST_HLS_DEMUX_STREAM_GET_DEMUX (hls_stream);
+    gst_hls_demux_handle_variant_playlist_update_error (demux, playlist_uri);
+  } else {
+    GstHLSDemux *demux = GST_HLS_DEMUX_STREAM_GET_DEMUX (hls_stream);
+    GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+        (_("Internal data stream error.")),
+        ("Could not update rendition playlist"));
+  }
+}
+
+static GstHLSDemuxPlaylistLoader *
+gst_hls_demux_stream_get_playlist_loader (GstHLSDemuxStream * hls_stream)
+{
+  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX2_STREAM_CAST (hls_stream)->demux;
+  if (hls_stream->playlistloader == NULL) {
+    hls_stream->playlistloader =
+        gst_hls_demux_playlist_loader_new (demux, demux->download_helper);
+    gst_hls_demux_playlist_loader_set_callbacks (hls_stream->playlistloader,
+        on_playlist_update_success, on_playlist_update_error, hls_stream);
+  }
+
+  return hls_stream->playlistloader;
+}
+
+void
+gst_hls_demux_stream_set_playlist_uri (GstHLSDemuxStream * hls_stream,
+    gchar * uri)
+{
+  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX2_STREAM_CAST (hls_stream)->demux;
+  GstHLSDemuxPlaylistLoader *pl =
+      gst_hls_demux_stream_get_playlist_loader (hls_stream);
+
+  const gchar *main_uri = gst_adaptive_demux_get_manifest_ref_uri (demux);
+  gst_hls_demux_playlist_loader_set_playlist_uri (pl, main_uri, uri);
+}
+
+void
+gst_hls_demux_stream_start_playlist_loading (GstHLSDemuxStream * hls_stream)
+{
+  GstHLSDemuxPlaylistLoader *pl =
+      gst_hls_demux_stream_get_playlist_loader (hls_stream);
+  gst_hls_demux_playlist_loader_start (pl);
+}
+
+GstFlowReturn
+gst_hls_demux_stream_check_current_playlist_uri (GstHLSDemuxStream * stream,
+    gchar * uri)
+{
+  GstHLSDemuxPlaylistLoader *pl =
+      gst_hls_demux_stream_get_playlist_loader (stream);
+
+  if (!gst_hls_demux_playlist_loader_has_current_uri (pl, uri)) {
+    GST_LOG_OBJECT (stream, "Target playlist not available yet");
+    return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
+  }
+
+  return GST_FLOW_OK;
+
+#if 0
+  /* Check if a redirect happened */
+  if (g_strcmp0 (*uri, new_playlist->uri)) {
+    GST_DEBUG_OBJECT (stream, "Playlist URI update : '%s'  =>  '%s'", *uri,
+        new_playlist->uri);
+    g_free (*uri);
+    *uri = g_strdup (new_playlist->uri);
+  }
+#endif
+}
+
+static GstFlowReturn
+gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+  GstAdaptiveDemux *demux = stream->demux;
+  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
+  GstM3U8MediaSegment *file;
+  GstM3U8PartialSegment *part = NULL;
+  gboolean discont;
+
+  /* Return BUSY if no playlist is loaded yet. Even if
+   * we switched an another playlist is loading, we'll keep*/
+  if (!hlsdemux_stream->playlist_fetched) {
+    gst_hls_demux_stream_start_playlist_loading (hlsdemux_stream);
+    return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
+  }
+  g_assert (hlsdemux_stream->playlist != NULL);
+  if ((ret =
+          gst_hls_demux_stream_check_current_playlist_uri (hlsdemux_stream,
+              NULL)) != GST_FLOW_OK) {
+    /* The URI of the playlist we have is not the target URI due
+     * to a bitrate switch - wait for it to load */
+    GST_DEBUG_OBJECT (hlsdemux_stream,
+        "Playlist is stale. Waiting for new playlist");
+    gst_hls_demux_stream_start_playlist_loading (hlsdemux_stream);
+    return ret;
+  }
+#ifndef GST_DISABLE_GST_DEBUG
+  GstClockTimeDiff live_edge_dist =
+      GST_CLOCK_TIME_IS_VALID (stream->current_position) ?
+      gst_hls_media_playlist_get_end_stream_time (hlsdemux_stream->playlist) -
+      stream->current_position : GST_CLOCK_TIME_NONE;
+  GstClockTime playlist_age =
+      gst_adaptive_demux2_get_monotonic_time (GST_ADAPTIVE_DEMUX (demux)) -
+      hlsdemux_stream->playlist->playlist_ts;
+  GST_DEBUG_OBJECT (stream,
+      "Updating fragment information, current_position:%" GST_TIME_FORMAT
+      " which is %" GST_STIME_FORMAT " from live edge. Playlist age %"
+      GST_TIME_FORMAT, GST_TIME_ARGS (stream->current_position),
+      GST_STIME_ARGS (live_edge_dist), GST_TIME_ARGS (playlist_age));
+#endif
+
+  /* Find the current segment if we don't already have it */
+  if (hlsdemux_stream->current_segment == NULL) {
+    GST_LOG_OBJECT (stream, "No current segment");
+    if (stream->current_position == GST_CLOCK_TIME_NONE) {
+      GstM3U8SeekResult seek_result;
+
+      GST_DEBUG_OBJECT (stream, "Setting up initial segment");
+
+      if (gst_hls_media_playlist_get_starting_segment
+          (hlsdemux_stream->playlist, &seek_result)) {
+        hlsdemux_stream->current_segment = seek_result.segment;
+        hlsdemux_stream->in_partial_segments =
+            seek_result.found_partial_segment;
+        hlsdemux_stream->part_idx = seek_result.part_idx;
+      }
+    } else {
+      if (gst_hls_media_playlist_has_lost_sync (hlsdemux_stream->playlist,
+              stream->current_position)) {
+        GST_WARNING_OBJECT (stream, "Lost SYNC !");
+        return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
+      }
+      GST_DEBUG_OBJECT (stream,
+          "Looking up segment for position %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (stream->current_position));
+
+      GstM3U8SeekResult seek_result;
+      if (!gst_hls_media_playlist_find_position (hlsdemux_stream->playlist,
+              stream->current_position, hlsdemux_stream->in_partial_segments,
+              &seek_result)) {
+        GST_INFO_OBJECT (stream, "At the end of the current media playlist");
+        gst_hls_demux_stream_update_preloads (hlsdemux_stream);
+        return GST_FLOW_EOS;
+      }
+
+      hlsdemux_stream->current_segment = seek_result.segment;
+      hlsdemux_stream->in_partial_segments = seek_result.found_partial_segment;
+      hlsdemux_stream->part_idx = seek_result.part_idx;
+
+      /* If on a full segment, update time mapping. If it already exists it will be ignored.
+       * Don't add time mappings for partial segments, wait for a full segment boundary */
+      if (!hlsdemux_stream->in_partial_segments
+          || hlsdemux_stream->part_idx == 0) {
+        gst_hls_demux_add_time_mapping (hlsdemux,
+            hlsdemux_stream->current_segment->discont_sequence,
+            hlsdemux_stream->current_segment->stream_time,
+            hlsdemux_stream->current_segment->datetime);
+      }
+    }
+  }
+
+  file = hlsdemux_stream->current_segment;
+
+  if (hlsdemux_stream->in_partial_segments) {
+    if (file->partial_segments == NULL) {
+      /* I think this can only happen if we reloaded the playlist
+       * and the segment we were in the middle of playing from
+       * removed its partial segments because we were playing
+       * too slowly */
+      GST_DEBUG_OBJECT (stream,
+          "Partial segment idx %d is not available in current playlist",
+          hlsdemux_stream->part_idx);
+      return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
+    }
+
+    if (hlsdemux_stream->part_idx >= file->partial_segments->len) {
+      /* Being beyond the available partial segments in the partial_only
+       * segment at the end of the playlist in LL-HLS means we've
+       * hit the live edge and need to wait for a playlist update */
+      if (file->partial_only) {
+        GST_INFO_OBJECT (stream, "At the end of the current media playlist");
+        gst_hls_demux_stream_update_preloads (hlsdemux_stream);
+        return GST_FLOW_EOS;
+      }
+
+      /* Otherwise, we reloaded the playlist and found that the partial_only segment we
+       * were playing from became a real segment and we overstepped the end of
+       * the parts. Reloading the playlist should have synced that up properly,
+       * so we should never get here. */
+      g_assert_not_reached ();
+    }
+
+    part =
+        g_ptr_array_index (file->partial_segments, hlsdemux_stream->part_idx);
+
+    GST_DEBUG_OBJECT (stream,
+        "Current partial segment %d stream_time %" GST_STIME_FORMAT,
+        hlsdemux_stream->part_idx, GST_STIME_ARGS (part->stream_time));
+    discont = stream->discont;
+    /* Use the segment discont flag only on the first partial segment */
+    if ((hlsdemux_stream->pending_discont || file->discont)
+        && hlsdemux_stream->part_idx == 0)
+      discont = TRUE;
+  } else {
+    GST_DEBUG_OBJECT (stream, "Current segment stream_time %" GST_STIME_FORMAT,
+        GST_STIME_ARGS (file->stream_time));
+    discont = file->discont || stream->discont
+        || hlsdemux_stream->pending_discont;
+  }
+
+  gboolean need_header = GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER (stream);
+
+  /* Check if the MAP header file changed and update it */
+  if (file->init_file != NULL
+      && !gst_m3u8_init_file_equal (hlsdemux_stream->init_file,
+          file->init_file)) {
+    GST_DEBUG_OBJECT (stream, "MAP header info changed. Updating");
+    if (hlsdemux_stream->init_file != NULL)
+      gst_m3u8_init_file_unref (hlsdemux_stream->init_file);
+    hlsdemux_stream->init_file = gst_m3u8_init_file_ref (file->init_file);
+    need_header = TRUE;
+  }
+
+  if (file->init_file && need_header) {
+    GstM3U8InitFile *header_file = file->init_file;
+    g_free (stream->fragment.header_uri);
+    stream->fragment.header_uri = g_strdup (header_file->uri);
+    stream->fragment.header_range_start = header_file->offset;
+    if (header_file->size != -1) {
+      stream->fragment.header_range_end =
+          header_file->offset + header_file->size - 1;
+    } else {
+      stream->fragment.header_range_end = -1;
+    }
+
+    stream->need_header = TRUE;
+
+    GST_DEBUG_OBJECT (stream, "Need header uri: %s %" G_GUINT64_FORMAT " %"
+        G_GINT64_FORMAT, stream->fragment.header_uri,
+        stream->fragment.header_range_start, stream->fragment.header_range_end);
+  }
+
+  /* set up our source for download */
+  stream->fragment.stream_time = GST_CLOCK_STIME_NONE;
+  g_free (stream->fragment.uri);
+  stream->fragment.range_start = 0;
+  stream->fragment.range_end = -1;
+
+  /* Encryption params always come from the parent segment */
+  g_free (hlsdemux_stream->current_key);
+  hlsdemux_stream->current_key = g_strdup (file->key);
+  g_free (hlsdemux_stream->current_iv);
+  hlsdemux_stream->current_iv = g_memdup2 (file->iv, sizeof (file->iv));
+
+  /* Other info could come from the part when playing partial segments */
+
+  if (part == NULL) {
+    if (hlsdemux_stream->reset_pts || discont || demux->segment.rate < 0.0) {
+      stream->fragment.stream_time = file->stream_time;
+    }
+    stream->fragment.uri = g_strdup (file->uri);
+    stream->fragment.range_start = file->offset;
+    if (file->size != -1)
+      stream->fragment.range_end = file->offset + file->size - 1;
+    stream->fragment.duration = file->duration;
+  } else {
+    if (hlsdemux_stream->reset_pts || discont || demux->segment.rate < 0.0) {
+      stream->fragment.stream_time = part->stream_time;
+    }
+    stream->fragment.uri = g_strdup (part->uri);
+    stream->fragment.range_start = part->offset;
+    if (part->size != -1)
+      stream->fragment.range_end = part->offset + part->size - 1;
+    stream->fragment.duration = part->duration;
+  }
+
+  GST_DEBUG_OBJECT (stream, "Stream URI now %s", stream->fragment.uri);
+
+  stream->recommended_buffering_threshold =
+      gst_hls_media_playlist_recommended_buffering_threshold
+      (hlsdemux_stream->playlist);
+
+  if (discont)
+    stream->discont = TRUE;
+  hlsdemux_stream->pending_discont = FALSE;
+
+  return ret;
+}
+
+static gboolean
+gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
+  GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
+  GList *tmp;
+
+  GST_DEBUG_OBJECT (stream, "is_variant:%d mappings:%p", hls_stream->is_variant,
+      hlsdemux->mappings);
+
+  /* Variant streams can always start straight away */
+  if (hls_stream->is_variant)
+    return TRUE;
+
+  /* Renditions of the exact same type as the variant are pure alternatives,
+   * they must be started. This can happen for example with audio-only manifests
+   * where the initial stream selected is a rendition and not a variant */
+  if (hls_stream->rendition_type == hlsdemux->main_stream->rendition_type)
+    return TRUE;
+
+  /* Rendition streams only require delaying if we don't have time mappings yet */
+  if (!hlsdemux->mappings)
+    return FALSE;
+
+  /* We can start if we have at least one internal time observation */
+  for (tmp = hlsdemux->mappings; tmp; tmp = tmp->next) {
+    GstHLSTimeMap *map = tmp->data;
+    if (map->internal_time != GST_CLOCK_TIME_NONE)
+      return TRUE;
+  }
+
+  /* Otherwise we have to wait */
+  return FALSE;
+}
+
+static void
+gst_hls_demux_stream_start (GstAdaptiveDemux2Stream * stream)
+{
+  if (!gst_hls_demux_stream_can_start (stream))
+    return;
+
+  /* Start the playlist loader */
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+
+  gst_hls_demux_stream_start_playlist_loading (hls_stream);
+
+  /* Chain up, to start the downloading */
+  GST_ADAPTIVE_DEMUX2_STREAM_CLASS (stream_parent_class)->start (stream);
+}
+
+static void
+gst_hls_demux_stream_stop (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+
+  if (hls_stream->playlistloader && !hls_stream->is_variant) {
+    /* Don't stop the loader for the variant stream, keep it running
+     * until the scheduler itself is stopped so we keep updating
+     * the live playlist timeline */
+    gst_hls_demux_playlist_loader_stop (hls_stream->playlistloader);
+  }
+
+  /* Chain up, to stop the downloading */
+  GST_ADAPTIVE_DEMUX2_STREAM_CLASS (stream_parent_class)->stop (stream);
+}
+
+/* Called when the variant is changed, to set a new rendition
+ * for this stream to download. Returns TRUE if the rendition
+ * stream switched group-id */
+static gboolean
+gst_hls_demux_update_rendition_stream_uri (GstHLSDemux * hlsdemux,
+    GstHLSDemuxStream * hls_stream, GError ** err)
+{
+  gchar *current_group_id, *requested_group_id;
+  GstHLSRenditionStream *replacement_media = NULL;
+  GList *tmp;
+
+  /* There always should be a current variant set */
+  g_assert (hlsdemux->current_variant);
+  /* There always is a GstHLSRenditionStream set for rendition streams */
+  g_assert (hls_stream->current_rendition);
+
+  requested_group_id =
+      hlsdemux->current_variant->media_groups[hls_stream->
+      current_rendition->mtype];
+  current_group_id = hls_stream->current_rendition->group_id;
+
+  GST_DEBUG_OBJECT (hlsdemux,
+      "Checking playlist change for variant stream %s lang: %s current group-id: %s / requested group-id: %s",
+      gst_stream_type_get_name (hls_stream->rendition_type), hls_stream->lang,
+      current_group_id, requested_group_id);
+
+
+  if (!g_strcmp0 (requested_group_id, current_group_id)) {
+    GST_DEBUG_OBJECT (hlsdemux, "No change needed");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (hlsdemux,
+      "group-id changed, looking for replacement playlist");
+
+  /* Need to switch/update */
+  for (tmp = hlsdemux->master->renditions; tmp; tmp = tmp->next) {
+    GstHLSRenditionStream *cand = tmp->data;
+
+    if (cand->mtype == hls_stream->current_rendition->mtype
+        && !g_strcmp0 (cand->lang, hls_stream->lang)
+        && !g_strcmp0 (cand->group_id, requested_group_id)) {
+      replacement_media = cand;
+      break;
+    }
+  }
+  if (!replacement_media) {
+    GST_ERROR_OBJECT (hlsdemux,
+        "Could not find a replacement playlist. Staying with previous one");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (hlsdemux, "Use replacement playlist %s",
+      replacement_media->name);
+  if (hls_stream->pending_rendition) {
+    GST_ERROR_OBJECT (hlsdemux,
+        "Already had a pending rendition switch to '%s'",
+        hls_stream->pending_rendition->name);
+    gst_hls_rendition_stream_unref (hls_stream->pending_rendition);
+  }
+  hls_stream->pending_rendition =
+      gst_hls_rendition_stream_ref (replacement_media);
+
+  gst_hls_demux_stream_set_playlist_uri (hls_stream, replacement_media->uri);
+
+  return TRUE;
+}
+
+static gboolean
+gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
+    guint64 bitrate)
+{
+  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux);
+  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
+  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+
+  /* Fast-Path, no changes possible */
+  if (hlsdemux->master == NULL || hlsdemux->master->is_simple)
+    return FALSE;
+
+  /* Currently playing partial segments, disallow bitrate
+   * switches and rendition playlist changes - except exactly
+   * at the first partial segment in a full segment (implying
+   * we are about to play a partial segment but didn't yet) */
+  if (hls_stream->in_partial_segments && hls_stream->part_idx > 0)
+    return FALSE;
+
+  if (hls_stream->is_variant) {
+    gdouble play_rate = gst_adaptive_demux_play_rate (demux);
+    gboolean changed = FALSE;
+
+    /* If not calculated yet, continue using start bitrate */
+    if (bitrate == 0)
+      bitrate = hlsdemux->start_bitrate;
+
+    /* Handle variant streams */
+    GST_DEBUG_OBJECT (hlsdemux,
+        "Checking playlist change for main variant stream");
+    if (!gst_hls_demux_change_variant_playlist (hlsdemux,
+            hlsdemux->current_variant->iframe,
+            bitrate / MAX (1.0, ABS (play_rate)), &changed)) {
+      GST_ERROR_OBJECT (hlsdemux, "Failed to choose a new variant to play");
+    }
+
+    GST_DEBUG_OBJECT (hlsdemux, "Returning changed: %d", changed);
+    return changed;
+  }
+
+  /* Handle rendition streams */
+  return gst_hls_demux_update_rendition_stream_uri (hlsdemux, hls_stream, NULL);
+}
+
+#if defined(HAVE_OPENSSL)
+static gboolean
+gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
+    const guint8 * key_data, const guint8 * iv_data)
+{
+  EVP_CIPHER_CTX *ctx;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+  EVP_CIPHER_CTX_init (&stream->aes_ctx);
+  ctx = &stream->aes_ctx;
+#else
+  stream->aes_ctx = EVP_CIPHER_CTX_new ();
+  ctx = stream->aes_ctx;
+#endif
+  if (!EVP_DecryptInit_ex (ctx, EVP_aes_128_cbc (), NULL, key_data, iv_data))
+    return FALSE;
+  EVP_CIPHER_CTX_set_padding (ctx, 0);
+  return TRUE;
+}
+
+static gboolean
+decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
+    const guint8 * encrypted_data, guint8 * decrypted_data)
+{
+  int len, flen = 0;
+  EVP_CIPHER_CTX *ctx;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+  ctx = &stream->aes_ctx;
+#else
+  ctx = stream->aes_ctx;
+#endif
+
+  if (G_UNLIKELY (length > G_MAXINT || length % 16 != 0))
+    return FALSE;
+
+  len = (int) length;
+  if (!EVP_DecryptUpdate (ctx, decrypted_data, &len, encrypted_data, len))
+    return FALSE;
+  EVP_DecryptFinal_ex (ctx, decrypted_data + len, &flen);
+  g_return_val_if_fail (len + flen == length, FALSE);
+  return TRUE;
+}
+
+static void
+gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+  EVP_CIPHER_CTX_cleanup (&stream->aes_ctx);
+#else
+  EVP_CIPHER_CTX_free (stream->aes_ctx);
+  stream->aes_ctx = NULL;
+#endif
+}
+
+#elif defined(HAVE_NETTLE)
+static gboolean
+gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
+    const guint8 * key_data, const guint8 * iv_data)
+{
+  aes128_set_decrypt_key (&stream->aes_ctx.ctx, key_data);
+  CBC_SET_IV (&stream->aes_ctx, iv_data);
+
+  return TRUE;
+}
+
+static gboolean
+decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
+    const guint8 * encrypted_data, guint8 * decrypted_data)
+{
+  if (length % 16 != 0)
+    return FALSE;
+
+  CBC_DECRYPT (&stream->aes_ctx, aes128_decrypt, length, decrypted_data,
+      encrypted_data);
+
+  return TRUE;
+}
+
+static void
+gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
+{
+  /* NOP */
+}
+
+#elif defined(HAVE_LIBGCRYPT)
+static gboolean
+gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
+    const guint8 * key_data, const guint8 * iv_data)
+{
+  gcry_error_t err = 0;
+  gboolean ret = FALSE;
+
+  err =
+      gcry_cipher_open (&stream->aes_ctx, GCRY_CIPHER_AES128,
+      GCRY_CIPHER_MODE_CBC, 0);
+  if (err)
+    goto out;
+  err = gcry_cipher_setkey (stream->aes_ctx, key_data, 16);
+  if (err)
+    goto out;
+  err = gcry_cipher_setiv (stream->aes_ctx, iv_data, 16);
+  if (!err)
+    ret = TRUE;
+
+out:
+  if (!ret)
+    if (stream->aes_ctx)
+      gcry_cipher_close (stream->aes_ctx);
+
+  return ret;
+}
+
+static gboolean
+decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
+    const guint8 * encrypted_data, guint8 * decrypted_data)
+{
+  gcry_error_t err = 0;
+
+  err = gcry_cipher_decrypt (stream->aes_ctx, decrypted_data, length,
+      encrypted_data, length);
+
+  return err == 0;
+}
+
+static void
+gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
+{
+  if (stream->aes_ctx) {
+    gcry_cipher_close (stream->aes_ctx);
+    stream->aes_ctx = NULL;
+  }
+}
+
+#else
+/* NO crypto available */
+static gboolean
+gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
+    const guint8 * key_data, const guint8 * iv_data)
+{
+  GST_ERROR ("No crypto available");
+  return FALSE;
+}
+
+static gboolean
+decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
+    const guint8 * encrypted_data, guint8 * decrypted_data)
+{
+  GST_ERROR ("Cannot decrypt fragment, no crypto available");
+  return FALSE;
+}
+
+static void
+gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
+{
+  return;
+}
+#endif
+
+static GstBuffer *
+gst_hls_demux_decrypt_fragment (GstHLSDemux * demux, GstHLSDemuxStream * stream,
+    GstBuffer * encrypted_buffer, GError ** err)
+{
+  GstBuffer *decrypted_buffer = NULL;
+  GstMapInfo encrypted_info, decrypted_info;
+
+  decrypted_buffer =
+      gst_buffer_new_allocate (NULL, gst_buffer_get_size (encrypted_buffer),
+      NULL);
+
+  gst_buffer_map (encrypted_buffer, &encrypted_info, GST_MAP_READ);
+  gst_buffer_map (decrypted_buffer, &decrypted_info, GST_MAP_WRITE);
+
+  if (!decrypt_fragment (stream, encrypted_info.size,
+          encrypted_info.data, decrypted_info.data))
+    goto decrypt_error;
+
+
+  gst_buffer_unmap (decrypted_buffer, &decrypted_info);
+  gst_buffer_unmap (encrypted_buffer, &encrypted_info);
+
+  gst_buffer_unref (encrypted_buffer);
+
+  return decrypted_buffer;
+
+decrypt_error:
+  GST_ERROR_OBJECT (demux, "Failed to decrypt fragment");
+  g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_DECRYPT,
+      "Failed to decrypt fragment");
+
+  gst_buffer_unmap (decrypted_buffer, &decrypted_info);
+  gst_buffer_unmap (encrypted_buffer, &encrypted_info);
+
+  gst_buffer_unref (encrypted_buffer);
+  gst_buffer_unref (decrypted_buffer);
+
+  return NULL;
+}
+
+static GstClockTime
+gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
+{
+  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
+  GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
+
+  GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (hls_stream->presentation_offset));
+
+  /* If this stream and the variant stream are ISOBMFF, returns the presentation
+   * offset of the variant stream */
+  if (hls_stream->parser_type == GST_HLS_PARSER_ISOBMFF
+      && hlsdemux->main_stream->parser_type == GST_HLS_PARSER_ISOBMFF)
+    return hlsdemux->main_stream->presentation_offset;
+  return hls_stream->presentation_offset;
+}
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-stream.h b/ext/adaptivedemux2/hls/gsthlsdemux-stream.h
new file mode 100644
index 0000000000000000000000000000000000000000..4096bb319a0972c51a89b5052647bb3936e9df7b
--- /dev/null
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-stream.h
@@ -0,0 +1,186 @@
+/* GStreamer
+ * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
+ * Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
+ * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
+ *
+ * gsthlsdemux-stream.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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef __GST_HLS_DEMUX_STREAM_H__
+#define __GST_HLS_DEMUX_STREAM_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include "m3u8.h"
+#include "gstisoff.h"
+#include <gstadaptivedemux.h>
+#include "gsthlsdemux.h"
+#include "gsthlsdemux-preloader.h"
+#include "gsthlsdemux-playlist-loader.h"
+
+#if defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+#elif defined(HAVE_NETTLE)
+#include <nettle/aes.h>
+#include <nettle/cbc.h>
+#elif defined(HAVE_LIBGCRYPT)
+#include <gcrypt.h>
+#endif
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HLS_DEMUX_STREAM \
+  (gst_hls_demux_stream_get_type())
+#define GST_HLS_DEMUX_STREAM(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HLS_DEMUX_STREAM,GstHLSDemuxStream))
+#define GST_HLS_DEMUX_STREAM_CAST(obj) ((GstHLSDemuxStream *)obj)
+
+#define GST_HLS_DEMUX_STREAM_GET_DEMUX(obj) (GST_HLS_DEMUX_CAST(GST_ADAPTIVE_DEMUX2_STREAM((obj))->demux))
+
+typedef struct _GstHLSDemuxStream GstHLSDemuxStream;
+typedef GstAdaptiveDemux2StreamClass GstHLSDemuxStreamClass;
+
+typedef enum {
+  GST_HLS_PARSER_NONE,
+  GST_HLS_PARSER_MPEGTS,
+  GST_HLS_PARSER_ID3,
+  GST_HLS_PARSER_WEBVTT,
+  GST_HLS_PARSER_ISOBMFF
+} GstHLSParserType;
+
+struct _GstHLSDemuxStream
+{
+  GstAdaptiveDemux2Stream adaptive_demux_stream;
+
+  /* A stream either variants or renditions */
+  gboolean is_variant;
+
+  /* Rendition-specific fields */
+  GstStreamType rendition_type;	/* FIXME: Also used by variant streams */
+  gchar *lang;
+  gchar *name;
+  GstHLSRenditionStream *current_rendition;
+  /* rendition to switch to */
+  GstHLSRenditionStream *pending_rendition;
+  /* End of Rendition-specific fields */
+
+  /* Whether the underlying playlist was fetched on creation */
+  gboolean playlist_fetched;
+  GstClockTime playlist_last_update_time;
+
+  /* Playlist loading helper */
+  GstHLSDemuxPlaylistLoader *playlistloader;
+
+  /* The media playlist currently used */
+  GstHLSMediaPlaylist *playlist;
+
+  /* The current header / init_file data */
+  GstM3U8InitFile *init_file;
+
+  /* The segment (from the above playlist) currently being used */
+  GstM3U8MediaSegment *current_segment;
+  /* When playing partial segments in LL-HLS, in_partial_segments is TRUE,
+   * and part_no is the current part index in the current_segment */
+  gboolean in_partial_segments;
+  guint part_idx;
+
+  /* Preload helper, that manages blocking preload downloads */
+  GstHLSDemuxPreloader *preloader;
+
+  /* Whether we need to typefind the next buffer */
+  gboolean do_typefind;
+
+  /* for collecting data until typefind succeeds */
+  GstBuffer *pending_typefind_buffer;
+
+  /* for chunking data into 16 byte multiples for decryption */
+  GstAdapter *pending_encrypted_data;
+
+ /* last decrypted buffer for pkcs7 unpadding.  We only know that it is the last
+  * on ::finish_fragment() */
+  GstBuffer *pending_decrypted_buffer;
+
+  /* Current offset (in bytes) in fragment data we pushed downstream. Resets to
+   * -1 at every fragment start */
+  guint64 current_offset;
+
+  gboolean reset_pts;
+
+  /* decryption tooling */
+#if defined(HAVE_OPENSSL)
+# if OPENSSL_VERSION_NUMBER < 0x10100000L
+  EVP_CIPHER_CTX aes_ctx;
+# else
+  EVP_CIPHER_CTX *aes_ctx;
+# endif
+#elif defined(HAVE_NETTLE)
+  struct CBC_CTX (struct aes128_ctx, AES_BLOCK_SIZE) aes_ctx;
+#elif defined(HAVE_LIBGCRYPT)
+  gcry_cipher_hd_t aes_ctx;
+#endif
+
+  gchar     *current_key;
+  guint8    *current_iv;
+
+  /* The type of parser used for data handling */
+  GstHLSParserType parser_type;
+
+  /* Is content processing required ? */
+  gboolean process_buffer_content;
+  /* Data to be analyzed by  */
+  GstBuffer *pending_segment_data;
+  /* TRUE if pending_segment_data contains data from a header/index */
+  gboolean pending_data_is_header;
+
+  /* ISOBMFF */
+  GstMoovBox *moov;
+
+  /* Presentation offset to use and report. This value will be appended to all
+   * "output" stream times. Not enabled (i.e 0) if variant is ISOBMFF
+   */
+  GstClockTime presentation_offset;
+
+  gboolean pdt_tag_sent;
+
+  /* The next segment needs to have the discont flag set on it. This is set when
+   * a playlist update was detected as not being continuous/contiguous with the
+   * previous one. */
+  gboolean pending_discont;
+};
+
+GstFlowReturn
+gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward,
+    GstSeekFlags flags, GstClockTimeDiff ts, GstClockTimeDiff * final_ts);
+
+void
+gst_hls_demux_stream_set_playlist_uri (GstHLSDemuxStream * stream, gchar * uri);
+
+void
+gst_hls_demux_stream_start_playlist_loading (GstHLSDemuxStream * stream);
+
+GstFlowReturn gst_hls_demux_stream_check_current_playlist_uri (GstHLSDemuxStream * stream, gchar *uri);
+
+void
+gst_hls_demux_stream_clear_pending_data (GstHLSDemuxStream * hls_stream,
+    gboolean force);
+
+GstHLSParserResult gst_hlsdemux_stream_handle_internal_time (
+						      GstHLSDemuxStream *hls_stream,
+						      GstClockTime       internal_time);
+
+G_END_DECLS
+#endif /* __GST_HLS_DEMUX_STREAM_H__ */
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux-util.c b/ext/adaptivedemux2/hls/gsthlsdemux-util.c
index 704786a039c82bb0e5fde28651cb73806c39ef41..20b4a06672bddc7e09454627939a53269d2a2610 100644
--- a/ext/adaptivedemux2/hls/gsthlsdemux-util.c
+++ b/ext/adaptivedemux2/hls/gsthlsdemux-util.c
@@ -32,6 +32,7 @@
 #include <string.h>
 
 #include "gsthlsdemux.h"
+#include "gsthlsdemux-stream.h"
 
 GST_DEBUG_CATEGORY_EXTERN (gst_hls_demux2_debug);
 #define GST_CAT_DEFAULT gst_hls_demux2_debug
@@ -389,7 +390,7 @@ gst_hlsdemux_handle_content_mpegts (GstHLSDemux * demux,
     return GST_HLS_PARSER_RESULT_NEED_MORE_DATA;
 
   /* We have the first internal time, figure out if we are in sync or not */
-  return gst_hlsdemux_handle_internal_time (demux, hls_stream, internal_time);
+  return gst_hlsdemux_stream_handle_internal_time (hls_stream, internal_time);
 }
 
 GstHLSParserResult
@@ -496,13 +497,12 @@ out:
   gst_buffer_unmap (*buffer, &info);
 
   if (smallest_ts != GST_CLOCK_TIME_NONE) {
-    ret = gst_hlsdemux_handle_internal_time (demux, hls_stream, smallest_ts);
+    ret = gst_hlsdemux_stream_handle_internal_time (hls_stream, smallest_ts);
   }
 
   return ret;
 }
 
-
 GstHLSParserResult
 gst_hlsdemux_handle_content_id3 (GstHLSDemux * demux,
     GstHLSDemuxStream * hls_stream, gboolean draining, GstBuffer ** buffer)
@@ -539,7 +539,7 @@ gst_hlsdemux_handle_content_id3 (GstHLSDemux * demux,
   if (!gst_tag_list_get_sample (taglist, GST_TAG_PRIVATE_DATA, &priv_data))
     goto out;
 
-  if (!g_str_equal ("com.apple.streaming.transportStreamTimestamp",
+  if (g_strcmp0 ("com.apple.streaming.transportStreamTimestamp",
           gst_structure_get_string (gst_sample_get_info (priv_data), "owner")))
     goto out;
 
@@ -561,7 +561,7 @@ gst_hlsdemux_handle_content_id3 (GstHLSDemux * demux,
 
   gst_buffer_unmap (tag_buf, &info);
 
-  ret = gst_hlsdemux_handle_internal_time (demux, hls_stream, internal);
+  ret = gst_hlsdemux_stream_handle_internal_time (hls_stream, internal);
 
 out:
   if (priv_data)
@@ -831,8 +831,8 @@ gst_hlsdemux_handle_content_webvtt (GstHLSDemux * demux,
   gchar **original_lines;
   GstClockTime localtime = GST_CLOCK_TIME_NONE;
   GstClockTime mpegtime = GST_CLOCK_TIME_NONE;
-  GstClockTime low_stream_time = GST_CLOCK_STIME_NONE;
-  GstClockTime high_stream_time = GST_CLOCK_STIME_NONE;
+  GstClockTimeDiff low_stream_time = GST_CLOCK_STIME_NONE;
+  GstClockTimeDiff high_stream_time = GST_CLOCK_STIME_NONE;
   gboolean found_timing = FALSE;
   gboolean found_text = FALSE;
   GPtrArray *builder;
@@ -855,7 +855,7 @@ gst_hlsdemux_handle_content_webvtt (GstHLSDemux * demux,
   segment_end = segment_start + current_segment->duration;
   tolerance = MAX (current_segment->duration / 2, 500 * GST_MSECOND);
 
-  map = gst_hls_find_time_map (demux, current_segment->discont_sequence);
+  map = gst_hls_demux_find_time_map (demux, current_segment->discont_sequence);
 
   builder = g_ptr_array_new_with_free_func (g_free);
 
@@ -944,6 +944,10 @@ gst_hlsdemux_handle_content_webvtt (GstHLSDemux * demux,
 out:
   if (ret) {
     gchar *newfile;
+
+    /* Ensure file always ends with an empty newline by adding an empty
+     * line. This helps downstream parsers properly detect entries */
+    g_ptr_array_add (builder, g_strdup ("\n"));
     /* Add NULL-terminator to string list */
     g_ptr_array_add (builder, NULL);
     newfile = g_strjoinv ("\n", (gchar **) builder->pdata);
@@ -962,33 +966,43 @@ out:
   g_free (original_content);
 
   if (out_of_bounds) {
-    GstM3U8MediaSegment *candidate_segment;
 
     /* The computed stream time falls outside of the guesstimated stream time,
      * reassess which segment we really are in */
     GST_WARNING ("Cue %" GST_STIME_FORMAT " -> %" GST_STIME_FORMAT
         " is outside of segment %" GST_STIME_FORMAT " -> %"
-        GST_STIME_FORMAT, GST_STIME_ARGS (low_stream_time),
+        GST_STIME_FORMAT,
+        GST_STIME_ARGS (low_stream_time),
         GST_STIME_ARGS (high_stream_time),
         GST_STIME_ARGS (current_segment->stream_time),
-        GST_STIME_ARGS (current_segment->stream_time +
-            current_segment->duration));
-
-    candidate_segment =
-        gst_hls_media_playlist_seek (hls_stream->playlist, TRUE,
-        GST_SEEK_FLAG_SNAP_NEAREST, low_stream_time);
-    if (candidate_segment) {
-      g_assert (candidate_segment != current_segment);
+        GST_STIME_ARGS ((GstClockTimeDiff) (current_segment->stream_time +
+                current_segment->duration)));
+
+    GstM3U8SeekResult seek_result;
+
+    if (gst_hls_media_playlist_find_position (hls_stream->playlist,
+            low_stream_time, hls_stream->in_partial_segments, &seek_result)) {
+      g_assert (seek_result.segment != current_segment);
       GST_DEBUG_OBJECT (hls_stream,
           "Stream time corresponds to segment %" GST_STIME_FORMAT
           " duration %" GST_TIME_FORMAT,
-          GST_STIME_ARGS (candidate_segment->stream_time),
-          GST_TIME_ARGS (candidate_segment->duration));
+          GST_STIME_ARGS (seek_result.segment->stream_time),
+          GST_TIME_ARGS (seek_result.segment->duration));
+
+      /* When we land in the middle of a partial segment, actually
+       * use the full segment position to resync the playlist */
+      if (seek_result.found_partial_segment) {
+        hls_stream->current_segment->stream_time =
+            seek_result.segment->stream_time;
+      } else {
+        hls_stream->current_segment->stream_time = seek_result.stream_time;
+      }
+
       /* Recalculate everything and ask parent class to restart */
-      hls_stream->current_segment->stream_time = candidate_segment->stream_time;
       gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist,
           hls_stream->current_segment);
-      gst_m3u8_media_segment_unref (candidate_segment);
+      gst_m3u8_media_segment_unref (seek_result.segment);
+      ret = GST_HLS_PARSER_RESULT_RESYNC;
     }
   }
 
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux.c b/ext/adaptivedemux2/hls/gsthlsdemux.c
index 6e45c34b0fe1ed9e8cf6dbe65ac1e0de0ba02bfe..17cf67d30c57e1724a9874eeb175f7a24cbac7e0 100644
--- a/ext/adaptivedemux2/hls/gsthlsdemux.c
+++ b/ext/adaptivedemux2/hls/gsthlsdemux.c
@@ -49,10 +49,15 @@
 #include <string.h>
 #include <gst/base/gsttypefindhelper.h>
 #include <gst/tag/tag.h>
+#include <glib/gi18n-lib.h>
+
+/* FIXME: Only needed for scheduler-unlock/lock hack */
+#include <gstadaptivedemux-private.h>
 
 #include "gsthlselements.h"
 #include "gstadaptivedemuxelements.h"
 #include "gsthlsdemux.h"
+#include "gsthlsdemux-stream.h"
 
 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -71,9 +76,6 @@ enum
 
 #define DEFAULT_START_BITRATE 0
 
-/* Maximum values for mpeg-ts DTS values */
-#define MPEG_TS_MAX_PTS (((((guint64)1) << 33) * (guint64)100000) / 9)
-
 /* GObject */
 static void gst_hls_demux_finalize (GObject * obj);
 
@@ -82,119 +84,23 @@ static GstStateChangeReturn
 gst_hls_demux_change_state (GstElement * element, GstStateChange transition);
 
 /* GstHLSDemux */
-static GstFlowReturn gst_hls_demux_update_playlist (GstHLSDemux * demux,
-    gboolean update, GError ** err);
-
-/* FIXME: the return value is never used? */
-static gboolean gst_hls_demux_change_playlist (GstHLSDemux * demux,
-    guint max_bitrate, gboolean * changed);
-static GstBuffer *gst_hls_demux_decrypt_fragment (GstHLSDemux * demux,
-    GstHLSDemuxStream * stream, GstBuffer * encrypted_buffer, GError ** err);
-static gboolean
-gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
-    const guint8 * key_data, const guint8 * iv_data);
-static void gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream);
+static GstFlowReturn
+gst_hls_demux_check_variant_playlist_loaded (GstHLSDemux * demux);
 
 static gboolean gst_hls_demux_is_live (GstAdaptiveDemux * demux);
 static GstClockTime gst_hls_demux_get_duration (GstAdaptiveDemux * demux);
-static gint64 gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux *
-    demux);
-static gboolean gst_hls_demux_process_manifest (GstAdaptiveDemux * demux,
-    GstBuffer * buf);
-static GstFlowReturn gst_hls_demux_stream_update_rendition_playlist (GstHLSDemux
-    * demux, GstHLSDemuxStream * stream);
+static gboolean gst_hls_demux_process_initial_manifest (GstAdaptiveDemux *
+    demux, GstBuffer * buf);
 static GstFlowReturn gst_hls_demux_update_manifest (GstAdaptiveDemux * demux);
 
-static void setup_initial_playlist (GstHLSDemux * demux,
-    GstHLSMediaPlaylist * playlist);
-static void gst_hls_demux_add_time_mapping (GstHLSDemux * demux,
-    gint64 dsn, GstClockTimeDiff stream_time, GDateTime * pdt);
-static void
-gst_hls_update_time_mappings (GstHLSDemux * demux,
-    GstHLSMediaPlaylist * playlist);
-
 static void gst_hls_prune_time_mappings (GstHLSDemux * demux);
 
 static gboolean gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
 
-static GstFlowReturn gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream *
-    stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
-    GstClockTimeDiff * final_ts);
-
-static gboolean
-gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn
-gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream
-    * stream, GstBuffer * buffer);
-
-static gboolean gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
-    * stream);
-static GstFlowReturn
-gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream);
-static GstFlowReturn
-gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
-static gboolean gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream *
-    stream);
-static void gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream *
-    stream);
-static gboolean gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
-    stream, guint64 bitrate);
-static GstClockTime
-gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream);
-
-static void gst_hls_demux_stream_finalize (GObject * object);
-
-#define gst_hls_demux_stream_parent_class stream_parent_class
-G_DEFINE_TYPE (GstHLSDemuxStream, gst_hls_demux_stream,
-    GST_TYPE_ADAPTIVE_DEMUX2_STREAM);
-
 static gboolean hlsdemux2_element_init (GstPlugin * plugin);
 
 GST_ELEMENT_REGISTER_DEFINE_CUSTOM (hlsdemux2, hlsdemux2_element_init);
 
-static void
-gst_hls_demux_stream_class_init (GstHLSDemuxStreamClass * klass)
-{
-  GObjectClass *gobject_class = (GObjectClass *) klass;
-  GstAdaptiveDemux2StreamClass *adaptivedemux2stream_class =
-      GST_ADAPTIVE_DEMUX2_STREAM_CLASS (klass);
-
-  gobject_class->finalize = gst_hls_demux_stream_finalize;
-
-  adaptivedemux2stream_class->update_fragment_info =
-      gst_hls_demux_stream_update_fragment_info;
-  adaptivedemux2stream_class->has_next_fragment =
-      gst_hls_demux_stream_has_next_fragment;
-  adaptivedemux2stream_class->stream_seek = gst_hls_demux_stream_seek;
-  adaptivedemux2stream_class->advance_fragment =
-      gst_hls_demux_stream_advance_fragment;
-  adaptivedemux2stream_class->select_bitrate =
-      gst_hls_demux_stream_select_bitrate;
-  adaptivedemux2stream_class->can_start = gst_hls_demux_stream_can_start;
-  adaptivedemux2stream_class->create_tracks =
-      gst_hls_demux_stream_create_tracks;
-
-  adaptivedemux2stream_class->start_fragment =
-      gst_hls_demux_stream_start_fragment;
-  adaptivedemux2stream_class->finish_fragment =
-      gst_hls_demux_stream_finish_fragment;
-  adaptivedemux2stream_class->data_received =
-      gst_hls_demux_stream_data_received;
-  adaptivedemux2stream_class->get_presentation_offset =
-      gst_hls_demux_stream_get_presentation_offset;
-}
-
-static void
-gst_hls_demux_stream_init (GstHLSDemuxStream * stream)
-{
-  stream->parser_type = GST_HLS_PARSER_NONE;
-  stream->do_typefind = TRUE;
-  stream->reset_pts = TRUE;
-  stream->presentation_offset = 60 * GST_SECOND;
-  stream->pdt_tag_sent = FALSE;
-}
-
 typedef struct _GstHLSDemux2 GstHLSDemux2;
 typedef struct _GstHLSDemux2Class GstHLSDemux2Class;
 
@@ -255,6 +161,16 @@ gst_hls_demux_get_property (GObject * object, guint prop_id,
   }
 }
 
+static gboolean
+hlsdemux_requires_periodical_playlist_update_default (GstAdaptiveDemux *
+    demux G_GNUC_UNUSED)
+{
+  /* We don't need the base class to update our manifest periodically, the
+   * playlist loader for the main stream will do that and trigger
+   * an update manual */
+  return FALSE;
+}
+
 
 static void
 gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
@@ -285,17 +201,18 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
       "HLS Demuxer",
       "Codec/Demuxer/Adaptive",
       "HTTP Live Streaming demuxer",
-      "Edward Hervey <edward@centricular.com>\n"
+      "Edward Hervey <edward@centricular.com>, "
       "Jan Schmidt <jan@centricular.com>");
 
   adaptivedemux_class->is_live = gst_hls_demux_is_live;
   adaptivedemux_class->get_live_seek_range = gst_hls_demux_get_live_seek_range;
   adaptivedemux_class->get_duration = gst_hls_demux_get_duration;
-  adaptivedemux_class->get_manifest_update_interval =
-      gst_hls_demux_get_manifest_update_interval;
-  adaptivedemux_class->process_manifest = gst_hls_demux_process_manifest;
-  adaptivedemux_class->update_manifest = gst_hls_demux_update_manifest;
+  adaptivedemux_class->requires_periodical_playlist_update =
+      hlsdemux_requires_periodical_playlist_update_default;
+  adaptivedemux_class->process_manifest =
+      gst_hls_demux_process_initial_manifest;
   adaptivedemux_class->reset = gst_hls_demux_reset;
+  adaptivedemux_class->update_manifest = gst_hls_demux_update_manifest;
   adaptivedemux_class->seek = gst_hls_demux_seek;
 }
 
@@ -340,7 +257,10 @@ gst_hls_demux_get_bitrate (GstHLSDemux * hlsdemux)
 
   /* FIXME !!!
    *
-   * No, there isn't a single output :D */
+   * No, there isn't a single output :D.
+   * Until the download helper can do estimates,
+   * use the main variant, or a video stream if the
+   * main variant stream is not loading */
 
   /* Valid because hlsdemux only has a single output */
   if (demux->input_period->streams) {
@@ -351,24 +271,6 @@ gst_hls_demux_get_bitrate (GstHLSDemux * hlsdemux)
   return 0;
 }
 
-static void
-gst_hls_demux_stream_clear_pending_data (GstHLSDemuxStream * hls_stream,
-    gboolean force)
-{
-  GST_DEBUG_OBJECT (hls_stream, "force : %d", force);
-  if (hls_stream->pending_encrypted_data)
-    gst_adapter_clear (hls_stream->pending_encrypted_data);
-  gst_buffer_replace (&hls_stream->pending_decrypted_buffer, NULL);
-  gst_buffer_replace (&hls_stream->pending_typefind_buffer, NULL);
-  if (force || !hls_stream->pending_data_is_header) {
-    gst_buffer_replace (&hls_stream->pending_segment_data, NULL);
-    hls_stream->pending_data_is_header = FALSE;
-  }
-  hls_stream->current_offset = -1;
-  hls_stream->process_buffer_content = TRUE;
-  gst_hls_demux_stream_decrypt_end (hls_stream);
-}
-
 static void
 gst_hls_demux_clear_all_pending_data (GstHLSDemux * hlsdemux)
 {
@@ -384,6 +286,27 @@ gst_hls_demux_clear_all_pending_data (GstHLSDemux * hlsdemux)
   }
 }
 
+/* Wait until the current variant playlist finishes loading, only
+ * for use when called from an external thread - seeking or initial
+ * manifest. From the scheduler task it will just hang */
+static GstFlowReturn
+gst_hls_demux_wait_for_variant_playlist (GstHLSDemux * hlsdemux)
+{
+  GstFlowReturn flow_ret;
+
+  while ((flow_ret = gst_hls_demux_check_variant_playlist_loaded (hlsdemux)
+          == GST_ADAPTIVE_DEMUX_FLOW_BUSY)) {
+    if (!gst_adaptive_demux2_stream_wait_prepared (GST_ADAPTIVE_DEMUX2_STREAM
+            (hlsdemux->main_stream))) {
+      GST_DEBUG_OBJECT (hlsdemux,
+          "Interrupted waiting for stream to be prepared");
+      return GST_FLOW_FLUSHING;
+    }
+  }
+
+  return flow_ret;
+}
+
 #define SEEK_UPDATES_PLAY_POSITION(r, start_type, stop_type) \
   ((r >= 0 && start_type != GST_SEEK_TYPE_NONE) || \
    (r < 0 && stop_type != GST_SEEK_TYPE_NONE))
@@ -422,32 +345,26 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
   /* Use I-frame variants for trick modes */
   if (hlsdemux->master->iframe_variants != NULL
       && rate < -1.0 && old_rate >= -1.0 && old_rate <= 1.0) {
-    GError *err = NULL;
 
     /* Switch to I-frame variant */
-    gst_hls_demux_set_current_variant (hlsdemux,
-        hlsdemux->master->iframe_variants->data);
-
-    if (gst_hls_demux_update_playlist (hlsdemux, FALSE, &err) != GST_FLOW_OK) {
-      GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
+    if (!gst_hls_demux_change_variant_playlist (hlsdemux, TRUE,
+            bitrate / ABS (rate), NULL))
       return FALSE;
-    }
-    //hlsdemux->discont = TRUE;
 
-    gst_hls_demux_change_playlist (hlsdemux, bitrate / ABS (rate), NULL);
   } else if (rate > -1.0 && rate <= 1.0 && (old_rate < -1.0 || old_rate > 1.0)) {
-    GError *err = NULL;
     /* Switch to normal variant */
-    gst_hls_demux_set_current_variant (hlsdemux,
-        hlsdemux->master->variants->data);
-
-    if (gst_hls_demux_update_playlist (hlsdemux, FALSE, &err) != GST_FLOW_OK) {
-      GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
+    if (!gst_hls_demux_change_variant_playlist (hlsdemux, FALSE, bitrate, NULL))
       return FALSE;
-    }
-    //hlsdemux->discont = TRUE;
-    /* TODO why not continue using the same? that was being used up to now? */
-    gst_hls_demux_change_playlist (hlsdemux, bitrate, NULL);
+  }
+
+  /* Of course the playlist isn't loaded as soon as we ask - we need to wait */
+  GstFlowReturn flow_ret = gst_hls_demux_wait_for_variant_playlist (hlsdemux);
+  if (flow_ret == GST_FLOW_FLUSHING)
+    return FALSE;
+  if (flow_ret != GST_FLOW_OK) {
+    GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+        (_("Internal data stream error.")), ("Could not switch playlist"));
+    return FALSE;
   }
 
   target_pos = rate < 0 ? stop : start;
@@ -467,8 +384,20 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
     if (!gst_adaptive_demux2_stream_is_selected (stream))
       continue;
 
-    if (gst_hls_demux_stream_seek (stream, rate >= 0, flags, target_pos,
-            &current_pos) != GST_FLOW_OK) {
+    GstFlowReturn flow_ret;
+
+    while ((flow_ret =
+            gst_hls_demux_stream_seek (stream, rate >= 0, flags, target_pos,
+                &current_pos) == GST_ADAPTIVE_DEMUX_FLOW_BUSY)) {
+      if (!gst_adaptive_demux2_stream_wait_prepared (GST_ADAPTIVE_DEMUX2_STREAM
+              (stream))) {
+        GST_DEBUG_OBJECT (hlsdemux,
+            "Interrupted waiting for stream to be prepared for seek");
+        return FALSE;
+      }
+    }
+
+    if (flow_ret != GST_FLOW_OK) {
       GST_ERROR_OBJECT (stream, "Failed to seek on stream");
       return FALSE;
     }
@@ -490,68 +419,44 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
   return TRUE;
 }
 
-static GstFlowReturn
-gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward,
-    GstSeekFlags flags, GstClockTimeDiff ts, GstClockTimeDiff * final_ts)
+static GstAdaptiveDemux2Stream *
+create_common_hls_stream (GstHLSDemux * demux, const gchar * name)
 {
-  GstFlowReturn ret = GST_FLOW_OK;
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
-  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
-  GstM3U8MediaSegment *new_position;
-
-  GST_DEBUG_OBJECT (stream,
-      "is_variant:%d media:%p current_variant:%p forward:%d ts:%"
-      GST_TIME_FORMAT, hls_stream->is_variant, hls_stream->current_rendition,
-      hlsdemux->current_variant, forward, GST_TIME_ARGS (ts));
-
-  /* If the rendition playlist needs to be updated, do it now */
-  if (!hls_stream->is_variant && !hls_stream->playlist_fetched) {
-    ret = gst_hls_demux_stream_update_rendition_playlist (hlsdemux, hls_stream);
-    if (ret != GST_FLOW_OK) {
-      GST_WARNING_OBJECT (stream,
-          "Failed to update the rendition playlist before seeking");
-      return ret;
-    }
-  }
-
-  new_position =
-      gst_hls_media_playlist_seek (hls_stream->playlist, forward, flags, ts);
-  if (new_position) {
-    if (hls_stream->current_segment)
-      gst_m3u8_media_segment_unref (hls_stream->current_segment);
-    hls_stream->current_segment = new_position;
-    hls_stream->reset_pts = TRUE;
-    if (final_ts)
-      *final_ts = new_position->stream_time;
-  } else {
-    GST_WARNING_OBJECT (stream, "Seeking failed");
-    ret = GST_FLOW_ERROR;
-  }
+  GstAdaptiveDemux2Stream *stream;
 
-  return ret;
-}
+  stream = g_object_new (GST_TYPE_HLS_DEMUX_STREAM, "name", name, NULL);
 
-static GstFlowReturn
-gst_hls_demux_update_manifest (GstAdaptiveDemux * demux)
-{
-  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
+  gst_adaptive_demux2_add_stream ((GstAdaptiveDemux *) demux, stream);
 
-  return gst_hls_demux_update_playlist (hlsdemux, TRUE, NULL);
+  return stream;
 }
 
-static GstAdaptiveDemux2Stream *
-create_common_hls_stream (GstHLSDemux * demux, const gchar * name)
+static void
+create_main_variant_stream (GstHLSDemux * demux)
 {
   GstAdaptiveDemux2Stream *stream;
+  GstHLSDemuxStream *hlsdemux_stream;
 
-  stream = g_object_new (GST_TYPE_HLS_DEMUX_STREAM, "name", name, NULL);
-  gst_adaptive_demux2_add_stream ((GstAdaptiveDemux *) demux, stream);
+  GST_DEBUG_OBJECT (demux, "Creating main variant stream");
 
-  return stream;
+  stream = create_common_hls_stream (demux, "hlsstream-variant");
+  demux->main_stream = hlsdemux_stream = (GstHLSDemuxStream *) stream;
+
+  hlsdemux_stream->is_variant = TRUE;
+
+  /* Due to HLS manifest information being so unreliable/inconsistent, we will
+   * create the actual tracks once we have information about the streams present
+   * in the variant data stream */
+  stream->pending_tracks = TRUE;
+
+  gst_hls_demux_stream_set_playlist_uri (hlsdemux_stream,
+      demux->current_variant->uri);
+  gst_hls_demux_stream_start_playlist_loading (hlsdemux_stream);
 }
 
-static GstAdaptiveDemuxTrack *
-new_track_for_rendition (GstHLSDemux * demux, GstHLSRenditionStream * rendition,
+GstAdaptiveDemuxTrack *
+gst_hls_demux_new_track_for_rendition (GstHLSDemux * demux,
+    GstHLSRenditionStream * rendition,
     GstCaps * caps, GstStreamFlags flags, GstTagList * tags)
 {
   GstAdaptiveDemuxTrack *track;
@@ -594,134 +499,6 @@ new_track_for_rendition (GstHLSDemux * demux, GstHLSRenditionStream * rendition,
   return track;
 }
 
-static GstHLSRenditionStream *
-find_uriless_rendition (GstHLSDemux * demux, GstStreamType stream_type)
-{
-  GList *tmp;
-
-  for (tmp = demux->master->renditions; tmp; tmp = tmp->next) {
-    GstHLSRenditionStream *media = tmp->data;
-    if (media->uri == NULL &&
-        gst_stream_type_from_hls_type (media->mtype) == stream_type)
-      return media;
-  }
-  return NULL;
-}
-
-static GstCaps *
-get_caps_of_stream_type (GstCaps * full_caps, GstStreamType streamtype)
-{
-  GstCaps *ret = NULL;
-
-  guint i;
-  for (i = 0; i < gst_caps_get_size (full_caps); i++) {
-    GstStructure *st = gst_caps_get_structure (full_caps, i);
-
-    if (gst_hls_get_stream_type_from_structure (st) == streamtype) {
-      ret = gst_caps_new_empty ();
-      gst_caps_append_structure (ret, gst_structure_copy (st));
-      break;
-    }
-  }
-
-  return ret;
-}
-
-static void
-gst_hls_demux_stream_create_tracks (GstAdaptiveDemux2Stream * stream)
-{
-  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
-  GstHLSDemuxStream *hlsdemux_stream = (GstHLSDemuxStream *) stream;
-  guint i;
-  GstStreamType uriless_types = 0;
-  GstCaps *variant_caps = NULL;
-
-  GST_DEBUG_OBJECT (stream, "Update tracks of variant stream");
-
-  if (hlsdemux->master->have_codecs) {
-    variant_caps = gst_hls_master_playlist_get_common_caps (hlsdemux->master);
-  }
-
-  /* Use the stream->stream_collection and manifest to create the appropriate tracks */
-  for (i = 0; i < gst_stream_collection_get_size (stream->stream_collection);
-      i++) {
-    GstStream *gst_stream =
-        gst_stream_collection_get_stream (stream->stream_collection, i);
-    GstStreamType stream_type = gst_stream_get_stream_type (gst_stream);
-    GstAdaptiveDemuxTrack *track;
-    GstHLSRenditionStream *embedded_media = NULL;
-    /* tracks from the variant streams should be prefered over those provided by renditions */
-    GstStreamFlags flags =
-        gst_stream_get_stream_flags (gst_stream) | GST_STREAM_FLAG_SELECT;
-    GstCaps *manifest_caps = NULL;
-
-    if (stream_type == GST_STREAM_TYPE_UNKNOWN)
-      continue;
-
-    if (variant_caps)
-      manifest_caps = get_caps_of_stream_type (variant_caps, stream_type);
-    hlsdemux_stream->rendition_type |= stream_type;
-
-    if ((uriless_types & stream_type) == 0) {
-      /* Do we have a uriless media for this stream type */
-      /* Find if there is a rendition without URI, it will be provided by this variant */
-      embedded_media = find_uriless_rendition (hlsdemux, stream_type);
-      /* Remember we used this type for a embedded media */
-      uriless_types |= stream_type;
-    }
-
-    if (embedded_media) {
-      GstTagList *tags = gst_stream_get_tags (gst_stream);
-      GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
-          embedded_media->name);
-      track =
-          new_track_for_rendition (hlsdemux, embedded_media, manifest_caps,
-          flags, tags ? gst_tag_list_make_writable (tags) : tags);
-    } else {
-      gchar *stream_id;
-      stream_id =
-          g_strdup_printf ("main-%s-%d", gst_stream_type_get_name (stream_type),
-          i);
-
-      GST_DEBUG_OBJECT (stream, "Adding track '%s' to main variant stream",
-          stream_id);
-      track =
-          gst_adaptive_demux_track_new (stream->demux, stream_type,
-          flags, stream_id, manifest_caps, NULL);
-      g_free (stream_id);
-    }
-    track->upstream_stream_id =
-        g_strdup (gst_stream_get_stream_id (gst_stream));
-    gst_adaptive_demux2_stream_add_track (stream, track);
-    gst_adaptive_demux_track_unref (track);
-  }
-
-  if (variant_caps)
-    gst_caps_unref (variant_caps);
-
-  /* Update the stream object with rendition types.
-   * FIXME: rendition_type could be removed */
-  stream->stream_type = hlsdemux_stream->rendition_type;
-}
-
-static void
-create_main_variant_stream (GstHLSDemux * demux)
-{
-  GstAdaptiveDemux2Stream *stream;
-  GstHLSDemuxStream *hlsdemux_stream;
-
-  GST_DEBUG_OBJECT (demux, "Creating main variant stream");
-
-  stream = create_common_hls_stream (demux, "hlsstream-variant");
-  demux->main_stream = hlsdemux_stream = (GstHLSDemuxStream *) stream;
-  hlsdemux_stream->is_variant = TRUE;
-  hlsdemux_stream->playlist_fetched = TRUE;
-  /* Due to HLS manifest information being so unreliable/inconsistent, we will
-   * create the actual tracks once we have information about the streams present
-   * in the variant data stream */
-  stream->pending_tracks = TRUE;
-}
-
 static GstHLSDemuxStream *
 create_rendition_stream (GstHLSDemux * demux, GstHLSRenditionStream * media)
 {
@@ -736,7 +513,7 @@ create_rendition_stream (GstHLSDemux * demux, GstHLSRenditionStream * media)
 
   /* We can't reliably provide caps for HLS target tracks since they might
    * change at any point in time */
-  track = new_track_for_rendition (demux, media, NULL, 0, NULL);
+  track = gst_hls_demux_new_track_for_rendition (demux, media, NULL, 0, NULL);
 
   stream_name = g_strdup_printf ("hlsstream-%s", track->stream_id);
   stream = create_common_hls_stream (demux, stream_name);
@@ -833,6 +610,7 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
       if (media_stream->current_rendition)
         gst_hls_rendition_stream_unref (media_stream->current_rendition);
       media_stream->current_rendition = gst_hls_rendition_stream_ref (media);
+      gst_hls_demux_stream_set_playlist_uri (media_stream, media->uri);
     }
 
     if (!previous_media_stream)
@@ -849,12 +627,6 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
   return TRUE;
 }
 
-static const gchar *
-gst_adaptive_demux_get_manifest_ref_uri (GstAdaptiveDemux * d)
-{
-  return d->manifest_base_uri ? d->manifest_base_uri : d->manifest_uri;
-}
-
 static void
 gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
     GstHLSVariantStream * variant)
@@ -866,8 +638,10 @@ gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
     GST_DEBUG_OBJECT (hlsdemux, "Will switch from variant '%s' to '%s'",
         hlsdemux->current_variant->name, variant->name);
     if (hlsdemux->pending_variant) {
-      GST_ERROR_OBJECT (hlsdemux, "Already waiting for pending variant '%s'",
-          hlsdemux->pending_variant->name);
+      if (hlsdemux->pending_variant != variant) {
+        GST_DEBUG_OBJECT (hlsdemux, "Already waiting for pending variant '%s'",
+            hlsdemux->pending_variant->name);
+      }
       gst_hls_variant_stream_unref (hlsdemux->pending_variant);
     }
     hlsdemux->pending_variant = gst_hls_variant_stream_ref (variant);
@@ -875,14 +649,23 @@ gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
     GST_DEBUG_OBJECT (hlsdemux, "Setting variant '%s'", variant->name);
     hlsdemux->current_variant = gst_hls_variant_stream_ref (variant);
   }
+
+  if (hlsdemux->main_stream) {
+    /* The variant stream exists, update the playlist we're loading */
+    gst_hls_demux_stream_set_playlist_uri (hlsdemux->main_stream, variant->uri);
+  }
 }
 
+/* Called to process the initial multi-variant (or simple playlist)
+ * received on the element's sinkpad */
 static gboolean
-gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
+gst_hls_demux_process_initial_manifest (GstAdaptiveDemux * demux,
+    GstBuffer * buf)
 {
   GstHLSVariantStream *variant;
   GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
   gchar *playlist = NULL;
+  guint start_bitrate = hlsdemux->start_bitrate;
   gboolean ret;
   GstHLSMediaPlaylist *simple_media_playlist = NULL;
 
@@ -913,54 +696,85 @@ gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
 
   if (hlsdemux->master->is_simple) {
     simple_media_playlist =
-        gst_hls_media_playlist_parse (playlist,
+        gst_hls_media_playlist_parse (playlist, GST_CLOCK_TIME_NONE,
         gst_adaptive_demux_get_manifest_ref_uri (demux), NULL);
   }
 
+  if (start_bitrate == 0)
+    start_bitrate = demux->connection_speed;
+
   /* select the initial variant stream */
-  if (demux->connection_speed == 0) {
-    variant = hlsdemux->master->default_variant;
-  } else if (hlsdemux->start_bitrate > 0) {
+  if (start_bitrate > 0) {
     variant =
         gst_hls_master_playlist_get_variant_for_bitrate (hlsdemux->master,
-        NULL, hlsdemux->start_bitrate, demux->min_bitrate);
+        FALSE, start_bitrate, demux->min_bitrate, hlsdemux->failed_variants);
   } else {
-    variant =
-        gst_hls_master_playlist_get_variant_for_bitrate (hlsdemux->master,
-        NULL, demux->connection_speed, demux->min_bitrate);
+    variant = hlsdemux->master->default_variant;
   }
 
-  if (variant) {
-    GST_INFO_OBJECT (hlsdemux,
-        "Manifest processed, initial variant selected : `%s`", variant->name);
-    gst_hls_demux_set_current_variant (hlsdemux, variant);      // FIXME: inline?
+  if (variant == NULL) {
+    GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+        (_("Internal data stream error.")),
+        ("Could not find an initial variant to play"));
   }
 
+  GST_INFO_OBJECT (hlsdemux,
+      "Manifest processed, initial variant selected : `%s`", variant->name);
+  gst_hls_demux_set_current_variant (hlsdemux, variant);
+
   GST_DEBUG_OBJECT (hlsdemux, "Manifest handled, now setting up streams");
 
   ret = gst_hls_demux_setup_streams (demux);
+  if (!ret)
+    return FALSE;
 
   if (simple_media_playlist) {
+    GstM3U8SeekResult seek_result;
+    GstM3U8MediaSegment *segment;
+
     hlsdemux->main_stream->playlist = simple_media_playlist;
-    hlsdemux->main_stream->current_segment =
-        gst_hls_media_playlist_get_starting_segment (simple_media_playlist);
-    setup_initial_playlist (hlsdemux, simple_media_playlist);
-    gst_hls_update_time_mappings (hlsdemux, simple_media_playlist);
-    gst_hls_media_playlist_dump (simple_media_playlist);
+    /* This is the initial variant playlist. We will use it to base all our timing
+     * from. */
+    segment = g_ptr_array_index (simple_media_playlist->segments, 0);
+    if (segment) {
+      segment->stream_time = 0;
+      gst_hls_media_playlist_recalculate_stream_time (simple_media_playlist,
+          segment);
+    }
+
+    if (!gst_hls_media_playlist_get_starting_segment (simple_media_playlist,
+            &seek_result)) {
+      GST_DEBUG_OBJECT (hlsdemux->main_stream,
+          "Failed to find a segment to start at");
+      return FALSE;
+    }
+    hlsdemux->main_stream->current_segment = seek_result.segment;
+    hlsdemux->main_stream->in_partial_segments =
+        seek_result.found_partial_segment;
+    hlsdemux->main_stream->part_idx = seek_result.part_idx;
+
+    gst_hls_demux_handle_variant_playlist_update (hlsdemux,
+        simple_media_playlist->uri, simple_media_playlist);
   }
 
-  /* get the selected media playlist (unless the initial list was one already) */
+  /* If this is a multi-variant playlist, wait for the initial variant playlist to load */
   if (!hlsdemux->master->is_simple) {
-    GError *err = NULL;
-
-    if (gst_hls_demux_update_playlist (hlsdemux, FALSE, &err) != GST_FLOW_OK) {
-      GST_ELEMENT_ERROR_FROM_ERROR (demux, "Could not fetch media playlist",
-          err);
+    GstFlowReturn flow_ret = gst_hls_demux_wait_for_variant_playlist (hlsdemux);
+    if (flow_ret == GST_FLOW_FLUSHING)
+      return FALSE;
+    if (flow_ret != GST_FLOW_OK) {
+      GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+          (_("Internal data stream error.")),
+          ("Could not fetch media playlist"));
       return FALSE;
     }
   }
 
-  return ret;
+  /* Make sure the external manifest copy of the main playlist
+   * is available to the baseclass at the start */
+  gst_hls_demux_update_manifest (demux);
+
+  return TRUE;
 }
 
 static GstClockTime
@@ -969,26 +783,26 @@ gst_hls_demux_get_duration (GstAdaptiveDemux * demux)
   GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
   GstClockTime duration = GST_CLOCK_TIME_NONE;
 
-  if (hlsdemux->main_stream)
-    duration =
-        gst_hls_media_playlist_get_duration (hlsdemux->main_stream->playlist);
+  if (hlsdemux->main_playlist)
+    duration = gst_hls_media_playlist_get_duration (hlsdemux->main_playlist);
 
   return duration;
 }
 
+/* Called from base class with the MANIFEST_LOCK held */
 static gboolean
 gst_hls_demux_is_live (GstAdaptiveDemux * demux)
 {
   GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
   gboolean is_live = FALSE;
 
-  if (hlsdemux->main_stream)
-    is_live = gst_hls_media_playlist_is_live (hlsdemux->main_stream->playlist);
+  if (hlsdemux->main_playlist)
+    is_live = gst_hls_media_playlist_is_live (hlsdemux->main_playlist);
 
   return is_live;
 }
 
-static const GstHLSKey *
+const GstHLSKey *
 gst_hls_demux_get_key (GstHLSDemux * demux, const gchar * key_url,
     const gchar * referer, gboolean allow_cache)
 {
@@ -1046,52 +860,7 @@ out:
   return key;
 }
 
-static gboolean
-gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream)
-{
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
-  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
-  const GstHLSKey *key;
-  GstHLSMediaPlaylist *m3u8;
-
-  GST_DEBUG_OBJECT (stream, "Fragment starting");
-
-  gst_hls_demux_stream_clear_pending_data (hls_stream, FALSE);
-
-  /* If no decryption is needed, there's nothing to be done here */
-  if (hls_stream->current_key == NULL)
-    return TRUE;
-
-  m3u8 = hls_stream->playlist;
-
-  key = gst_hls_demux_get_key (hlsdemux, hls_stream->current_key,
-      m3u8->uri, m3u8->allowcache);
-
-  if (key == NULL)
-    goto key_failed;
-
-  if (!gst_hls_demux_stream_decrypt_start (hls_stream, key->data,
-          hls_stream->current_iv))
-    goto decrypt_start_failed;
-
-  return TRUE;
-
-key_failed:
-  {
-    GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT_NOKEY,
-        ("Couldn't retrieve key for decryption"), (NULL));
-    GST_WARNING_OBJECT (hlsdemux, "Failed to decrypt data");
-    return FALSE;
-  }
-decrypt_start_failed:
-  {
-    GST_ELEMENT_ERROR (hlsdemux, STREAM, DECRYPT, ("Failed to start decrypt"),
-        ("Couldn't set key and IV or plugin was built without crypto library"));
-    return FALSE;
-  }
-}
-
-static void
+void
 gst_hls_demux_start_rendition_streams (GstHLSDemux * hlsdemux)
 {
   GstAdaptiveDemux *demux = (GstAdaptiveDemux *) hlsdemux;
@@ -1107,112 +876,6 @@ gst_hls_demux_start_rendition_streams (GstHLSDemux * hlsdemux)
   }
 }
 
-static GstHLSParserType
-caps_to_parser_type (const GstCaps * caps)
-{
-  const GstStructure *s = gst_caps_get_structure (caps, 0);
-
-  if (gst_structure_has_name (s, "video/mpegts"))
-    return GST_HLS_PARSER_MPEGTS;
-  if (gst_structure_has_name (s, "application/x-id3"))
-    return GST_HLS_PARSER_ID3;
-  if (gst_structure_has_name (s, "application/x-subtitle-vtt"))
-    return GST_HLS_PARSER_WEBVTT;
-  if (gst_structure_has_name (s, "video/quicktime"))
-    return GST_HLS_PARSER_ISOBMFF;
-
-  return GST_HLS_PARSER_NONE;
-}
-
-/* Identify the nature of data for this stream
- *
- * Will also setup the appropriate parser (tsreader) if needed
- *
- * Consumes the input buffer when it returns FALSE, but
- * replaces / returns the input buffer in the `buffer` parameter
- * when it returns TRUE.
- *
- * Returns TRUE if we are done with typefinding */
-static gboolean
-gst_hls_demux_typefind_stream (GstHLSDemux * hlsdemux,
-    GstAdaptiveDemux2Stream * stream, GstBuffer ** out_buffer, gboolean at_eos,
-    GstFlowReturn * ret)
-{
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);   // FIXME: pass HlsStream into function
-  GstCaps *caps = NULL;
-  guint buffer_size;
-  GstTypeFindProbability prob = GST_TYPE_FIND_NONE;
-  GstMapInfo info;
-  GstBuffer *buffer = *out_buffer;
-
-  if (hls_stream->pending_typefind_buffer) {
-    /* Append to the existing typefind buffer and create a new one that
-     * we'll return (or consume below) */
-    buffer = *out_buffer =
-        gst_buffer_append (hls_stream->pending_typefind_buffer, buffer);
-    hls_stream->pending_typefind_buffer = NULL;
-  }
-
-  gst_buffer_map (buffer, &info, GST_MAP_READ);
-  buffer_size = info.size;
-
-  /* Typefind could miss if buffer is too small. In this case we
-   * will retry later */
-  if (buffer_size >= (2 * 1024) || at_eos) {
-    caps =
-        gst_type_find_helper_for_data (GST_OBJECT_CAST (hlsdemux), info.data,
-        info.size, &prob);
-  }
-
-  if (G_UNLIKELY (!caps)) {
-    /* Won't need this mapping any more all paths return inside this if() */
-    gst_buffer_unmap (buffer, &info);
-
-    /* Only fail typefinding if we already a good amount of data
-     * and we still don't know the type */
-    if (buffer_size > (2 * 1024 * 1024) || at_eos) {
-      GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
-          ("Could not determine type of stream"), (NULL));
-      gst_buffer_unref (buffer);
-      *ret = GST_FLOW_NOT_NEGOTIATED;
-    } else {
-      GST_LOG_OBJECT (stream, "Not enough data to typefind");
-      hls_stream->pending_typefind_buffer = buffer;     /* Transfer the ref */
-      *ret = GST_FLOW_OK;
-    }
-    *out_buffer = NULL;
-    return FALSE;
-  }
-
-  GST_DEBUG_OBJECT (stream,
-      "Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob);
-
-  if (hls_stream->parser_type == GST_HLS_PARSER_NONE) {
-    hls_stream->parser_type = caps_to_parser_type (caps);
-    if (hls_stream->parser_type == GST_HLS_PARSER_NONE) {
-      GST_WARNING_OBJECT (stream,
-          "Unsupported stream type %" GST_PTR_FORMAT, caps);
-      GST_MEMDUMP_OBJECT (stream, "unknown data", info.data,
-          MIN (info.size, 128));
-      gst_buffer_unref (buffer);
-      *ret = GST_FLOW_ERROR;
-      return FALSE;
-    }
-    if (hls_stream->parser_type == GST_HLS_PARSER_ISOBMFF)
-      hls_stream->presentation_offset = 0;
-  }
-
-  gst_adaptive_demux2_stream_set_caps (stream, caps);
-
-  hls_stream->do_typefind = FALSE;
-
-  gst_buffer_unmap (buffer, &info);
-
-  /* We are done with typefinding. Doesn't consume the input buffer */
-  *ret = GST_FLOW_OK;
-  return TRUE;
-}
-
 static GstHLSTimeMap *
 time_map_in_list (GList * list, gint64 dsn)
 {
@@ -1229,739 +892,59 @@ time_map_in_list (GList * list, gint64 dsn)
 }
 
 GstHLSTimeMap *
-gst_hls_find_time_map (GstHLSDemux * demux, gint64 dsn)
+gst_hls_demux_find_time_map (GstHLSDemux * demux, gint64 dsn)
 {
   return time_map_in_list (demux->mappings, dsn);
 }
 
-/* Compute the stream time for the given internal time, based on the provided
- * time map.
- *
- * Will handle mpeg-ts wraparound. */
-GstClockTimeDiff
-gst_hls_internal_to_stream_time (GstHLSTimeMap * map,
-    GstClockTime internal_time)
+static GstHLSTimeMap *
+gst_hls_time_map_new (void)
 {
-  if (map->internal_time == GST_CLOCK_TIME_NONE)
-    return GST_CLOCK_STIME_NONE;
+  GstHLSTimeMap *map = g_new0 (GstHLSTimeMap, 1);
 
-  /* Handle MPEG-TS Wraparound */
-  if (internal_time < map->internal_time &&
-      map->internal_time - internal_time > (MPEG_TS_MAX_PTS / 2))
-    internal_time += MPEG_TS_MAX_PTS;
+  map->stream_time = GST_CLOCK_TIME_NONE;
+  map->internal_time = GST_CLOCK_TIME_NONE;
 
-  return (map->stream_time + internal_time - map->internal_time);
+  return map;
 }
 
-/* Handle the internal time discovered on a segment.
- *
- * This function is called by the individual buffer parsers once they have
- * extracted that internal time (which is most of the time based on mpegts time,
- * but can also be ISOBMFF pts).
- *
- * This will update the time map when appropriate.
- *
- * If a synchronization issue is detected, the appropriate steps will be taken
- * and the RESYNC return value will be returned
- */
-GstHLSParserResult
-gst_hlsdemux_handle_internal_time (GstHLSDemux * demux,
-    GstHLSDemuxStream * hls_stream, GstClockTime internal_time)
+static void
+gst_hls_time_map_free (GstHLSTimeMap * map)
 {
-  GstM3U8MediaSegment *current_segment = hls_stream->current_segment;
-  GstHLSTimeMap *map;
-  GstClockTimeDiff current_stream_time;
-  GstClockTimeDiff real_stream_time, difference;
-
-  g_return_val_if_fail (current_segment != NULL, GST_HLS_PARSER_RESULT_ERROR);
-
-  current_stream_time = current_segment->stream_time;
-
-  GST_DEBUG_OBJECT (hls_stream,
-      "Got internal time %" GST_TIME_FORMAT " for current segment stream time %"
-      GST_STIME_FORMAT, GST_TIME_ARGS (internal_time),
-      GST_STIME_ARGS (current_stream_time));
+  if (map->pdt)
+    g_date_time_unref (map->pdt);
+  g_free (map);
+}
 
-  map = gst_hls_find_time_map (demux, current_segment->discont_sequence);
+void
+gst_time_map_set_values (GstHLSTimeMap * map, GstClockTimeDiff stream_time,
+    GstClockTime internal_time, GDateTime * pdt)
+{
+  GstClockTime offset = 0;
 
-  /* Time mappings will always be created upon initial parsing and when advancing */
-  g_assert (map);
+  if (stream_time < 0) {
+    offset = -stream_time;
+    stream_time = 0;
+    /* Handle negative stream times. This can happen for example when the server
+     * returns an older playlist.
+     *
+     * Shift the values accordingly to end up with non-negative reference stream
+     * time */
+    GST_DEBUG ("Shifting values before storage (offset : %" GST_TIME_FORMAT ")",
+        GST_TIME_ARGS (offset));
+  }
 
-  /* Handle the first internal time of a discont sequence. We can only store/use
-   * those values for variant streams. */
-  if (!GST_CLOCK_TIME_IS_VALID (map->internal_time)) {
-    if (!hls_stream->is_variant) {
-      GST_WARNING_OBJECT (hls_stream,
-          "Got data from a new discont sequence on a rendition stream, can't validate stream time");
-      return GST_HLS_PARSER_RESULT_DONE;
-    }
-    GST_DEBUG_OBJECT (hls_stream,
-        "Updating time map dsn:%" G_GINT64_FORMAT " stream_time:%"
-        GST_STIME_FORMAT " internal_time:%" GST_TIME_FORMAT, map->dsn,
-        GST_STIME_ARGS (current_stream_time), GST_TIME_ARGS (internal_time));
-    /* The stream time for a mapping should always be positive ! */
-    g_assert (current_stream_time >= 0);
-
-    if (hls_stream->parser_type == GST_HLS_PARSER_ISOBMFF)
-      hls_stream->presentation_offset = internal_time - current_stream_time;
-
-    map->stream_time = current_stream_time;
-    map->internal_time = internal_time;
-
-    gst_hls_demux_start_rendition_streams (demux);
-    return GST_HLS_PARSER_RESULT_DONE;
-  }
-
-  /* The information in a discont is always valid */
-  if (current_segment->discont) {
-    GST_DEBUG_OBJECT (hls_stream,
-        "DISCONT segment, Updating time map to stream_time:%" GST_STIME_FORMAT
-        " internal_time:%" GST_TIME_FORMAT, GST_STIME_ARGS (internal_time),
-        GST_TIME_ARGS (current_stream_time));
-    map->stream_time = current_stream_time;
-    map->internal_time = internal_time;
-    return GST_HLS_PARSER_RESULT_DONE;
-  }
-
-  /* Check if the segment is the expected one */
-  real_stream_time = gst_hls_internal_to_stream_time (map, internal_time);
-  difference = current_stream_time - real_stream_time;
-  GST_DEBUG_OBJECT (hls_stream,
-      "Segment contains stream time %" GST_STIME_FORMAT
-      " difference against expected : %" GST_STIME_FORMAT,
-      GST_STIME_ARGS (real_stream_time), GST_STIME_ARGS (difference));
-
-  if (ABS (difference) > 10 * GST_MSECOND) {
-    /* Update the value */
-    GST_DEBUG_OBJECT (hls_stream,
-        "Updating current stream time to %" GST_STIME_FORMAT,
-        GST_STIME_ARGS (real_stream_time));
-    current_segment->stream_time = real_stream_time;
-
-    gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist,
-        hls_stream->current_segment);
-    gst_hls_media_playlist_dump (hls_stream->playlist);
-
-    if (ABS (difference) > (hls_stream->current_segment->duration / 2)) {
-      GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) hls_stream;
-      GstM3U8MediaSegment *actual_segment;
-
-      /* We are at the wrong segment, try to figure out the *actual* segment */
-      GST_DEBUG_OBJECT (hls_stream,
-          "Trying to seek to the correct segment for %" GST_STIME_FORMAT,
-          GST_STIME_ARGS (current_stream_time));
-      actual_segment =
-          gst_hls_media_playlist_seek (hls_stream->playlist, TRUE,
-          GST_SEEK_FLAG_SNAP_NEAREST, current_stream_time);
-
-      if (actual_segment) {
-        GST_DEBUG_OBJECT (hls_stream, "Synced to position %" GST_STIME_FORMAT,
-            GST_STIME_ARGS (actual_segment->stream_time));
-        gst_m3u8_media_segment_unref (hls_stream->current_segment);
-        hls_stream->current_segment = actual_segment;
-        /* Ask parent class to restart this fragment */
-        return GST_HLS_PARSER_RESULT_RESYNC;
-      }
-
-      GST_WARNING_OBJECT (hls_stream,
-          "Could not find a replacement stream, carrying on with segment");
-      stream->discont = TRUE;
-      stream->fragment.stream_time = real_stream_time;
-    }
-  }
-
-  return GST_HLS_PARSER_RESULT_DONE;
-}
-
-static GstHLSParserResult
-gst_hls_demux_handle_buffer_content (GstHLSDemux * demux,
-    GstHLSDemuxStream * hls_stream, gboolean draining, GstBuffer ** buffer)
-{
-  GstHLSTimeMap *map;
-  GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) hls_stream;
-  GstClockTimeDiff current_stream_time =
-      hls_stream->current_segment->stream_time;
-  GstClockTime current_duration = hls_stream->current_segment->duration;
-  GstHLSParserResult parser_ret;
-
-  GST_LOG_OBJECT (stream,
-      "stream_time:%" GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT
-      " discont:%d draining:%d header:%d index:%d",
-      GST_STIME_ARGS (current_stream_time), GST_TIME_ARGS (current_duration),
-      hls_stream->current_segment->discont, draining,
-      stream->downloading_header, stream->downloading_index);
-
-  /* FIXME : Replace the boolean parser return value (and this function's return
-   *  value) by an enum which clearly specifies whether:
-   *
-   * * The content parsing happened succesfully and it no longer needs to be
-   *   called for the remainder of this fragment
-   * * More data is needed in order to parse the data
-   * * There was a fatal error parsing the contents (ex: invalid/incompatible
-   *   content)
-   * * The computed fragment stream time is out of sync
-   */
-
-  g_assert (demux->mappings);
-  map =
-      gst_hls_find_time_map (demux,
-      hls_stream->current_segment->discont_sequence);
-  if (!map) {
-    /* For rendition streams, we can't do anything without time mapping */
-    if (!hls_stream->is_variant) {
-      GST_DEBUG_OBJECT (stream,
-          "No available time mapping for dsn:%" G_GINT64_FORMAT
-          " using estimated stream time",
-          hls_stream->current_segment->discont_sequence);
-      goto out_done;
-    }
-
-    /* Variants will be able to fill in the the time mapping, so we can carry on without a time mapping */
-  } else {
-    GST_DEBUG_OBJECT (stream,
-        "Using mapping dsn:%" G_GINT64_FORMAT " stream_time:%" GST_TIME_FORMAT
-        " internal_time:%" GST_TIME_FORMAT, map->dsn,
-        GST_TIME_ARGS (map->stream_time), GST_TIME_ARGS (map->internal_time));
-  }
-
-  switch (hls_stream->parser_type) {
-    case GST_HLS_PARSER_MPEGTS:
-      parser_ret =
-          gst_hlsdemux_handle_content_mpegts (demux, hls_stream, draining,
-          buffer);
-      break;
-    case GST_HLS_PARSER_ID3:
-      parser_ret =
-          gst_hlsdemux_handle_content_id3 (demux, hls_stream, draining, buffer);
-      break;
-    case GST_HLS_PARSER_WEBVTT:
-    {
-      /* Furthermore it will handle timeshifting itself */
-      parser_ret =
-          gst_hlsdemux_handle_content_webvtt (demux, hls_stream, draining,
-          buffer);
-      break;
-    }
-    case GST_HLS_PARSER_ISOBMFF:
-      parser_ret =
-          gst_hlsdemux_handle_content_isobmff (demux, hls_stream, draining,
-          buffer);
-      break;
-    case GST_HLS_PARSER_NONE:
-    default:
-    {
-      GST_ERROR_OBJECT (stream, "Unknown stream type");
-      goto out_error;
-    }
-  }
-
-  if (parser_ret == GST_HLS_PARSER_RESULT_NEED_MORE_DATA) {
-    if (stream->downloading_index || stream->downloading_header)
-      goto out_need_more;
-    /* Else if we're draining, it's an error */
-    if (draining)
-      goto out_error;
-    /* Else we just need more data */
-    goto out_need_more;
-  }
-
-  if (parser_ret == GST_HLS_PARSER_RESULT_ERROR)
-    goto out_error;
-
-  if (parser_ret == GST_HLS_PARSER_RESULT_RESYNC)
-    goto out_resync;
-
-out_done:
-  GST_DEBUG_OBJECT (stream, "Done. Finished parsing");
-  return GST_HLS_PARSER_RESULT_DONE;
-
-out_error:
-  GST_DEBUG_OBJECT (stream, "Done. Error while parsing");
-  return GST_HLS_PARSER_RESULT_ERROR;
-
-out_need_more:
-  GST_DEBUG_OBJECT (stream, "Done. Need more data");
-  return GST_HLS_PARSER_RESULT_NEED_MORE_DATA;
-
-out_resync:
-  GST_DEBUG_OBJECT (stream, "Done. Resync required");
-  return GST_HLS_PARSER_RESULT_RESYNC;
-}
-
-static GstFlowReturn
-gst_hls_demux_stream_handle_buffer (GstAdaptiveDemux2Stream * stream,
-    GstBuffer * buffer, gboolean at_eos)
-{
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);   // FIXME: pass HlsStream into function
-  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
-  GstFlowReturn ret = GST_FLOW_OK;
-  GstBuffer *pending_header_data = NULL;
-
-  /* If current segment is not present, this means that a playlist update
-   * happened between the moment ::update_fragment_info() was called and the
-   * moment we received data. And that playlist update couldn't match the
-   * current position. This will happen in live playback when we are downloading
-   * too slowly, therefore we try to "catch up" back to live
-   */
-  if (hls_stream->current_segment == NULL) {
-    GST_WARNING_OBJECT (stream, "Lost sync");
-    /* Drop the buffer */
-    gst_buffer_unref (buffer);
-    return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
-  }
-
-  GST_DEBUG_OBJECT (stream,
-      "buffer:%p at_eos:%d do_typefind:%d uri:%s", buffer, at_eos,
-      hls_stream->do_typefind, hls_stream->current_segment->uri);
-
-  if (buffer == NULL)
-    goto out;
-
-  /* If we need to do typefind and we're not done with it (or we errored), return */
-  if (G_UNLIKELY (hls_stream->do_typefind) &&
-      !gst_hls_demux_typefind_stream (hlsdemux, stream, &buffer, at_eos,
-          &ret)) {
-    goto out;
-  }
-  g_assert (hls_stream->pending_typefind_buffer == NULL);
-
-  if (hls_stream->process_buffer_content) {
-    GstHLSParserResult parse_ret;
-
-    if (hls_stream->pending_segment_data) {
-      if (hls_stream->pending_data_is_header) {
-        /* Keep a copy of the header data in case we need to requeue it
-         * due to GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT below */
-        pending_header_data = gst_buffer_ref (hls_stream->pending_segment_data);
-      }
-      buffer = gst_buffer_append (hls_stream->pending_segment_data, buffer);
-      hls_stream->pending_segment_data = NULL;
-    }
-
-    /* Try to get the timing information */
-    parse_ret =
-        gst_hls_demux_handle_buffer_content (hlsdemux, hls_stream, at_eos,
-        &buffer);
-
-    switch (parse_ret) {
-      case GST_HLS_PARSER_RESULT_NEED_MORE_DATA:
-        /* If we don't have enough, store and return */
-        hls_stream->pending_segment_data = buffer;
-        hls_stream->pending_data_is_header =
-            (stream->downloading_header == TRUE);
-        if (hls_stream->pending_data_is_header)
-          stream->send_segment = TRUE;
-        goto out;
-      case GST_HLS_PARSER_RESULT_ERROR:
-        /* Error, drop buffer and return */
-        gst_buffer_unref (buffer);
-        ret = GST_FLOW_ERROR;
-        goto out;
-      case GST_HLS_PARSER_RESULT_RESYNC:
-        /* Resync, drop buffer and return */
-        gst_buffer_unref (buffer);
-        ret = GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT;
-        /* If we had a pending set of header data, requeue it */
-        if (pending_header_data != NULL) {
-          g_assert (hls_stream->pending_segment_data == NULL);
-
-          GST_DEBUG_OBJECT (hls_stream,
-              "Requeueing header data %" GST_PTR_FORMAT
-              " before returning RESTART_FRAGMENT", pending_header_data);
-          hls_stream->pending_segment_data = pending_header_data;
-          pending_header_data = NULL;
-        }
-        goto out;
-      case GST_HLS_PARSER_RESULT_DONE:
-        /* Done parsing, carry on */
-        hls_stream->process_buffer_content = FALSE;
-        break;
-    }
-  }
-
-  if (!buffer)
-    goto out;
-
-  buffer = gst_buffer_make_writable (buffer);
-
-  GST_BUFFER_OFFSET (buffer) = hls_stream->current_offset;
-  hls_stream->current_offset += gst_buffer_get_size (buffer);
-  GST_BUFFER_OFFSET_END (buffer) = hls_stream->current_offset;
-
-  GST_DEBUG_OBJECT (stream, "We have a buffer, pushing: %" GST_PTR_FORMAT,
-      buffer);
-
-  ret = gst_adaptive_demux2_stream_push_buffer (stream, buffer);
-
-out:
-  if (pending_header_data != NULL) {
-    /* Throw away the pending header data now. If it wasn't consumed above,
-     * we won't need it */
-    gst_buffer_unref (pending_header_data);
-  }
-
-  GST_DEBUG_OBJECT (stream, "Returning %s", gst_flow_get_name (ret));
-  return ret;
-}
-
-static GstFlowReturn
-gst_hls_demux_stream_finish_fragment (GstAdaptiveDemux2Stream * stream)
-{
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);   // FIXME: pass HlsStream into function
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  GST_DEBUG_OBJECT (stream, "Finishing fragment uri:%s",
-      hls_stream->current_segment->uri);
-
-  /* Drain all pending data */
-  if (hls_stream->current_key)
-    gst_hls_demux_stream_decrypt_end (hls_stream);
-
-  if (hls_stream->current_segment && stream->last_ret == GST_FLOW_OK) {
-    if (hls_stream->pending_decrypted_buffer) {
-      if (hls_stream->current_key) {
-        GstMapInfo info;
-        gssize unpadded_size;
-
-        /* Handle pkcs7 unpadding here */
-        gst_buffer_map (hls_stream->pending_decrypted_buffer, &info,
-            GST_MAP_READ);
-        unpadded_size = info.size - info.data[info.size - 1];
-        gst_buffer_unmap (hls_stream->pending_decrypted_buffer, &info);
-
-        gst_buffer_resize (hls_stream->pending_decrypted_buffer, 0,
-            unpadded_size);
-      }
-
-      ret =
-          gst_hls_demux_stream_handle_buffer (stream,
-          hls_stream->pending_decrypted_buffer, TRUE);
-      hls_stream->pending_decrypted_buffer = NULL;
-    }
-
-    if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) {
-      if (G_UNLIKELY (hls_stream->pending_typefind_buffer)) {
-        GstBuffer *buf = hls_stream->pending_typefind_buffer;
-        hls_stream->pending_typefind_buffer = NULL;
-
-        gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
-      }
-
-      if (hls_stream->pending_segment_data) {
-        GstBuffer *buf = hls_stream->pending_segment_data;
-        hls_stream->pending_segment_data = NULL;
-
-        ret = gst_hls_demux_stream_handle_buffer (stream, buf, TRUE);
-      }
-    }
-  }
-
-  gst_hls_demux_stream_clear_pending_data (hls_stream, FALSE);
-
-  if (G_UNLIKELY (stream->downloading_header || stream->downloading_index))
-    return GST_FLOW_OK;
-
-  if (hls_stream->current_segment == NULL) {
-    /* We can't advance, we just return OK for now and let the base class
-     * trigger a new download (or fail and resync itself) */
-    return GST_FLOW_OK;
-  }
-
-  if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) {
-    /* We can update the stream current position with a more accurate value
-     * before advancing. Note that we don't have any period so we can set the
-     * stream_time as-is on the stream current position */
-    stream->current_position = hls_stream->current_segment->stream_time;
-    return gst_adaptive_demux2_stream_advance_fragment (stream,
-        hls_stream->current_segment->duration);
-  }
-  return ret;
-}
-
-static GstFlowReturn
-gst_hls_demux_stream_data_received (GstAdaptiveDemux2Stream * stream,
-    GstBuffer * buffer)
-{
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
-  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
-  GstM3U8MediaSegment *file = hls_stream->current_segment;
-
-  if (file == NULL)
-    return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
-
-  if (hls_stream->current_offset == -1)
-    hls_stream->current_offset = 0;
-
-  /* Is it encrypted? */
-  if (hls_stream->current_key) {
-    GError *err = NULL;
-    gsize size;
-    GstBuffer *decrypted_buffer;
-    GstBuffer *tmp_buffer;
-
-    if (hls_stream->pending_encrypted_data == NULL)
-      hls_stream->pending_encrypted_data = gst_adapter_new ();
-
-    gst_adapter_push (hls_stream->pending_encrypted_data, buffer);
-    size = gst_adapter_available (hls_stream->pending_encrypted_data);
-
-    /* must be a multiple of 16 */
-    size &= (~0xF);
-
-    if (size == 0) {
-      return GST_FLOW_OK;
-    }
-
-    buffer = gst_adapter_take_buffer (hls_stream->pending_encrypted_data, size);
-    decrypted_buffer =
-        gst_hls_demux_decrypt_fragment (hlsdemux, hls_stream, buffer, &err);
-    if (err) {
-      GST_ELEMENT_ERROR (hlsdemux, STREAM, DECODE, ("Failed to decrypt buffer"),
-          ("decryption failed %s", err->message));
-      g_error_free (err);
-      return GST_FLOW_ERROR;
-    }
-
-    tmp_buffer = hls_stream->pending_decrypted_buffer;
-    hls_stream->pending_decrypted_buffer = decrypted_buffer;
-    buffer = tmp_buffer;
-    if (!buffer)
-      return GST_FLOW_OK;
-  }
-
-  if (!hls_stream->pdt_tag_sent && file != NULL && file->datetime != NULL) {
-    gst_adaptive_demux2_stream_set_tags (stream,
-        gst_tag_list_new (GST_TAG_DATE_TIME,
-            gst_date_time_new_from_g_date_time (g_date_time_ref
-                (file->datetime)), NULL));
-    hls_stream->pdt_tag_sent = TRUE;
-  }
-
-  return gst_hls_demux_stream_handle_buffer (stream, buffer, FALSE);
-}
-
-static void
-gst_hls_demux_stream_finalize (GObject * object)
-{
-  GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) object;
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (object);
-  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
-
-  if (hls_stream == hlsdemux->main_stream)
-    hlsdemux->main_stream = NULL;
-
-  g_free (hls_stream->lang);
-  g_free (hls_stream->name);
-
-  if (hls_stream->playlist) {
-    gst_hls_media_playlist_unref (hls_stream->playlist);
-    hls_stream->playlist = NULL;
-  }
-
-  if (hls_stream->init_file) {
-    gst_m3u8_init_file_unref (hls_stream->init_file);
-    hls_stream->init_file = NULL;
-  }
-
-  if (hls_stream->pending_encrypted_data)
-    g_object_unref (hls_stream->pending_encrypted_data);
-
-  gst_buffer_replace (&hls_stream->pending_decrypted_buffer, NULL);
-  gst_buffer_replace (&hls_stream->pending_typefind_buffer, NULL);
-  gst_buffer_replace (&hls_stream->pending_segment_data, NULL);
-
-  if (hls_stream->moov)
-    gst_isoff_moov_box_free (hls_stream->moov);
-
-  if (hls_stream->current_key) {
-    g_free (hls_stream->current_key);
-    hls_stream->current_key = NULL;
-  }
-  if (hls_stream->current_iv) {
-    g_free (hls_stream->current_iv);
-    hls_stream->current_iv = NULL;
-  }
-  if (hls_stream->current_rendition) {
-    gst_hls_rendition_stream_unref (hls_stream->current_rendition);
-    hls_stream->current_rendition = NULL;
-  }
-  if (hls_stream->pending_rendition) {
-    gst_hls_rendition_stream_unref (hls_stream->pending_rendition);
-    hls_stream->pending_rendition = NULL;
-  }
-
-  if (hls_stream->current_segment) {
-    gst_m3u8_media_segment_unref (hls_stream->current_segment);
-    hls_stream->current_segment = NULL;
-  }
-  gst_hls_demux_stream_decrypt_end (hls_stream);
-
-  G_OBJECT_CLASS (stream_parent_class)->finalize (object);
-}
-
-static gboolean
-gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
-{
-  GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
-
-  GST_DEBUG_OBJECT (stream, "has next ?");
-
-  return gst_hls_media_playlist_has_next_fragment (hls_stream->playlist,
-      hls_stream->current_segment, stream->demux->segment.rate > 0);
-}
-
-static GstFlowReturn
-gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream)
-{
-  GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
-  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
-  GstM3U8MediaSegment *new_segment = NULL;
-
-  GST_DEBUG_OBJECT (stream,
-      "Current segment sn:%" G_GINT64_FORMAT " stream_time:%" GST_STIME_FORMAT
-      " uri:%s", hlsdemux_stream->current_segment->sequence,
-      GST_STIME_ARGS (hlsdemux_stream->current_segment->stream_time),
-      hlsdemux_stream->current_segment->uri);
-
-  new_segment =
-      gst_hls_media_playlist_advance_fragment (hlsdemux_stream->playlist,
-      hlsdemux_stream->current_segment, stream->demux->segment.rate > 0);
-  if (new_segment) {
-    hlsdemux_stream->reset_pts = FALSE;
-    if (new_segment->discont_sequence !=
-        hlsdemux_stream->current_segment->discont_sequence)
-      gst_hls_demux_add_time_mapping (hlsdemux, new_segment->discont_sequence,
-          new_segment->stream_time, new_segment->datetime);
-    gst_m3u8_media_segment_unref (hlsdemux_stream->current_segment);
-    hlsdemux_stream->current_segment = new_segment;
-    GST_DEBUG_OBJECT (stream,
-        "Advanced to segment sn:%" G_GINT64_FORMAT " stream_time:%"
-        GST_STIME_FORMAT " uri:%s", hlsdemux_stream->current_segment->sequence,
-        GST_STIME_ARGS (hlsdemux_stream->current_segment->stream_time),
-        hlsdemux_stream->current_segment->uri);
-    return GST_FLOW_OK;
-  }
-
-  GST_LOG_OBJECT (stream, "Could not advance to next fragment");
-  if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (hlsdemux_stream->playlist)) {
-    gst_m3u8_media_segment_unref (hlsdemux_stream->current_segment);
-    hlsdemux_stream->current_segment = NULL;
-    return GST_FLOW_OK;
-  }
-
-  return GST_FLOW_EOS;
-}
-
-static GstHLSMediaPlaylist *
-download_media_playlist (GstHLSDemux * demux, gchar * uri, GError ** err,
-    GstHLSMediaPlaylist * current)
-{
-  GstAdaptiveDemux *adaptive_demux;
-  const gchar *main_uri;
-  DownloadRequest *download;
-  GstBuffer *buf;
-  gchar *playlist_data;
-  GstHLSMediaPlaylist *playlist = NULL;
-  gchar *base_uri;
-  gboolean playlist_uri_change = FALSE;
-
-  adaptive_demux = GST_ADAPTIVE_DEMUX (demux);
-  main_uri = gst_adaptive_demux_get_manifest_ref_uri (adaptive_demux);
-
-  /* If there's no previous playlist, or the URI changed this
-   * is not a refresh/update but a switch to a new playlist */
-  playlist_uri_change = (current == NULL || g_strcmp0 (uri, current->uri) != 0);
-
-  if (!playlist_uri_change) {
-    GST_LOG_OBJECT (demux, "Updating the playlist");
-  }
-
-  download =
-      downloadhelper_fetch_uri (adaptive_demux->download_helper,
-      uri, main_uri, DOWNLOAD_FLAG_COMPRESS | DOWNLOAD_FLAG_FORCE_REFRESH, err);
-
-  if (download == NULL)
-    return NULL;
-
-  /* Set the base URI of the playlist to the redirect target if any */
-  if (download->redirect_permanent && download->redirect_uri) {
-    uri = g_strdup (download->redirect_uri);
-    base_uri = NULL;
-  } else {
-    uri = g_strdup (download->uri);
-    base_uri = g_strdup (download->redirect_uri);
-  }
-
-  if (download->state == DOWNLOAD_REQUEST_STATE_ERROR) {
-    GST_WARNING_OBJECT (demux,
-        "Couldn't get the playlist, got HTTP status code %d",
-        download->status_code);
-    download_request_unref (download);
-    if (err)
-      g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
-          "Couldn't download the playlist");
-    goto out;
-  }
-  buf = download_request_take_buffer (download);
-  download_request_unref (download);
-
-  /* there should be a buf if there wasn't an error (handled above) */
-  g_assert (buf);
-
-  playlist_data = gst_hls_buf_to_utf8_text (buf);
-  gst_buffer_unref (buf);
-
-  if (playlist_data == NULL) {
-    GST_WARNING_OBJECT (demux, "Couldn't validate playlist encoding");
-    if (err)
-      g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
-          "Couldn't validate playlist encoding");
-    goto out;
-  }
-
-  if (!playlist_uri_change && current
-      && gst_hls_media_playlist_has_same_data (current, playlist_data)) {
-    GST_DEBUG_OBJECT (demux, "Same playlist data");
-    playlist = gst_hls_media_playlist_ref (current);
-    playlist->reloaded = TRUE;
-    g_free (playlist_data);
-  } else {
-    playlist = gst_hls_media_playlist_parse (playlist_data, uri, base_uri);
-    if (!playlist) {
-      GST_WARNING_OBJECT (demux, "Couldn't parse playlist");
-      if (err)
-        g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED,
-            "Couldn't parse playlist");
-    }
+  map->stream_time = stream_time;
+  map->internal_time = internal_time;
+  if (pdt) {
+    if (offset)
+      map->pdt = g_date_time_add (pdt, offset / GST_USECOND);
+    else
+      map->pdt = g_date_time_ref (pdt);
   }
-
-out:
-  g_free (uri);
-  g_free (base_uri);
-
-  return playlist;
 }
 
-static GstHLSTimeMap *
-gst_hls_time_map_new (void)
-{
-  GstHLSTimeMap *map = g_new0 (GstHLSTimeMap, 1);
-
-  map->stream_time = GST_CLOCK_TIME_NONE;
-  map->internal_time = GST_CLOCK_TIME_NONE;
-
-  return map;
-}
-
-static void
-gst_hls_time_map_free (GstHLSTimeMap * map)
-{
-  if (map->pdt)
-    g_date_time_unref (map->pdt);
-  g_free (map);
-}
-
-static void
+void
 gst_hls_demux_add_time_mapping (GstHLSDemux * demux, gint64 dsn,
     GstClockTimeDiff stream_time, GDateTime * pdt)
 {
@@ -1970,7 +953,6 @@ gst_hls_demux_add_time_mapping (GstHLSDemux * demux, gint64 dsn,
 #endif
   GstHLSTimeMap *map;
   GList *tmp;
-  GstClockTime offset = 0;
 
   /* Check if we don't already have a mapping for the given dsn */
   for (tmp = demux->mappings; tmp; tmp = tmp->next) {
@@ -2000,28 +982,9 @@ gst_hls_demux_add_time_mapping (GstHLSDemux * demux, gint64 dsn,
   g_free (datestring);
 #endif
 
-  if (stream_time < 0) {
-    offset = -stream_time;
-    stream_time = 0;
-    /* Handle negative stream times. This can happen for example when the server
-     * returns an older playlist.
-     *
-     * Shift the values accordingly to end up with non-negative reference stream
-     * time */
-    GST_DEBUG_OBJECT (demux,
-        "Shifting values before storage (offset : %" GST_TIME_FORMAT ")",
-        GST_TIME_ARGS (offset));
-  }
-
   map = gst_hls_time_map_new ();
   map->dsn = dsn;
-  map->stream_time = stream_time;
-  if (pdt) {
-    if (offset)
-      map->pdt = g_date_time_add (pdt, offset / GST_USECOND);
-    else
-      map->pdt = g_date_time_ref (pdt);
-  }
+  gst_time_map_set_values (map, stream_time, GST_CLOCK_TIME_NONE, pdt);
 
   demux->mappings = g_list_append (demux->mappings, map);
 }
@@ -2051,7 +1014,7 @@ gst_hls_prune_time_mappings (GstHLSDemux * hlsdemux)
       if (dsn == G_MAXINT64 || segment->discont_sequence != dsn) {
         dsn = segment->discont_sequence;
         if (!time_map_in_list (active, dsn)) {
-          GstHLSTimeMap *map = gst_hls_find_time_map (hlsdemux, dsn);
+          GstHLSTimeMap *map = gst_hls_demux_find_time_map (hlsdemux, dsn);
           if (map) {
             GST_DEBUG_OBJECT (demux,
                 "Keeping active time map dsn:%" G_GINT64_FORMAT, map->dsn);
@@ -2061,544 +1024,233 @@ gst_hls_prune_time_mappings (GstHLSDemux * hlsdemux)
           }
         }
       }
-    }
-  }
-
-  g_list_free_full (hlsdemux->mappings, (GDestroyNotify) gst_hls_time_map_free);
-  hlsdemux->mappings = active;
-}
-
-/* Go over the DSN from the playlist and add any missing time mapping */
-static void
-gst_hls_update_time_mappings (GstHLSDemux * demux,
-    GstHLSMediaPlaylist * playlist)
-{
-  guint idx, len = playlist->segments->len;
-  gint64 dsn = G_MAXINT64;
-
-  for (idx = 0; idx < len; idx++) {
-    GstM3U8MediaSegment *segment = g_ptr_array_index (playlist->segments, idx);
-
-    if (dsn == G_MAXINT64 || segment->discont_sequence != dsn) {
-      dsn = segment->discont_sequence;
-      if (!gst_hls_find_time_map (demux, segment->discont_sequence))
-        gst_hls_demux_add_time_mapping (demux, segment->discont_sequence,
-            segment->stream_time, segment->datetime);
-    }
-  }
-}
-
-static void
-setup_initial_playlist (GstHLSDemux * demux, GstHLSMediaPlaylist * playlist)
-{
-  guint idx, len = playlist->segments->len;
-  GstM3U8MediaSegment *segment;
-  GstClockTimeDiff pos = 0;
-
-  GST_DEBUG_OBJECT (demux,
-      "Setting up initial variant segment and time mapping");
-
-  /* This is the initial variant playlist. We will use it to base all our timing
-   * from. */
-
-  for (idx = 0; idx < len; idx++) {
-    segment = g_ptr_array_index (playlist->segments, idx);
-
-    segment->stream_time = pos;
-    pos += segment->duration;
-  }
-}
-
-/* Reset hlsdemux in case of live synchronization loss (i.e. when a media
- * playlist update doesn't match at all with the previous one) */
-static void
-gst_hls_demux_reset_for_lost_sync (GstHLSDemux * hlsdemux)
-{
-  GstAdaptiveDemux *demux = (GstAdaptiveDemux *) hlsdemux;
-  GList *iter;
-
-  GST_DEBUG_OBJECT (hlsdemux, "Resetting for lost sync");
-
-  for (iter = demux->input_period->streams; iter; iter = iter->next) {
-    GstHLSDemuxStream *hls_stream = iter->data;
-    GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) hls_stream;
-
-    if (hls_stream->current_segment)
-      gst_m3u8_media_segment_unref (hls_stream->current_segment);
-    hls_stream->current_segment = NULL;
-
-    if (hls_stream->is_variant) {
-      GstHLSTimeMap *map;
-      /* Resynchronize the variant stream */
-      g_assert (stream->current_position != GST_CLOCK_STIME_NONE);
-      hls_stream->current_segment =
-          gst_hls_media_playlist_get_starting_segment (hls_stream->playlist);
-      hls_stream->current_segment->stream_time = stream->current_position;
-      gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist,
-          hls_stream->current_segment);
-      GST_DEBUG_OBJECT (stream,
-          "Resynced variant playlist to %" GST_STIME_FORMAT,
-          GST_STIME_ARGS (stream->current_position));
-      map =
-          gst_hls_find_time_map (hlsdemux,
-          hls_stream->current_segment->discont_sequence);
-      if (map)
-        map->internal_time = GST_CLOCK_TIME_NONE;
-      gst_hls_update_time_mappings (hlsdemux, hls_stream->playlist);
-      gst_hls_media_playlist_dump (hls_stream->playlist);
-    } else {
-      /* Force playlist update for the rendition streams, it will resync to the
-       * variant stream on the next round */
-      if (hls_stream->playlist)
-        gst_hls_media_playlist_unref (hls_stream->playlist);
-      hls_stream->playlist = NULL;
-      hls_stream->playlist_fetched = FALSE;
-    }
-  }
-}
-
-static GstFlowReturn
-gst_hls_demux_stream_update_media_playlist (GstHLSDemux * demux,
-    GstHLSDemuxStream * stream, gchar ** uri, GError ** err)
-{
-  GstHLSMediaPlaylist *new_playlist;
-
-  GST_DEBUG_OBJECT (stream, "Updating %s", *uri);
-
-  new_playlist = download_media_playlist (demux, *uri, err, stream->playlist);
-  if (new_playlist == NULL) {
-    GST_WARNING_OBJECT (stream, "Could not get playlist '%s'", *uri);
-    return GST_FLOW_ERROR;
-  }
-
-  /* Check if a redirect happened */
-  if (g_strcmp0 (*uri, new_playlist->uri)) {
-    GST_DEBUG_OBJECT (stream, "Playlist URI update : '%s'  =>  '%s'", *uri,
-        new_playlist->uri);
-    g_free (*uri);
-    *uri = g_strdup (new_playlist->uri);
-  }
-
-  /* Synchronize playlist with previous one. If we can't update the playlist
-   * timing and inform the base class that we lost sync */
-  if (stream->playlist
-      && !gst_hls_media_playlist_sync_to_playlist (new_playlist,
-          stream->playlist)) {
-    /* Failure to synchronize with the previous media playlist is only fatal for
-     * variant streams. */
-    if (stream->is_variant) {
-      GST_DEBUG_OBJECT (stream,
-          "Could not synchronize new variant playlist with previous one !");
-      goto lost_sync;
-    }
-
-    /* For rendition streams, we can attempt synchronization against the
-     * variant playlist which is constantly updated */
-    if (demux->main_stream->playlist
-        && !gst_hls_media_playlist_sync_to_playlist (new_playlist,
-            demux->main_stream->playlist)) {
-      GST_DEBUG_OBJECT (stream,
-          "Could not do fallback synchronization of rendition stream to variant stream");
-      goto lost_sync;
-    }
-  } else if (!stream->is_variant && demux->main_stream->playlist) {
-    /* For initial rendition media playlist, attempt to synchronize the playlist
-     * against the variant stream. This is non-fatal if it fails. */
-    GST_DEBUG_OBJECT (stream,
-        "Attempting to synchronize initial rendition stream with variant stream");
-    gst_hls_media_playlist_sync_to_playlist (new_playlist,
-        demux->main_stream->playlist);
-  }
-
-  if (stream->current_segment) {
-    GstM3U8MediaSegment *new_segment;
-    GST_DEBUG_OBJECT (stream,
-        "Current segment sn:%" G_GINT64_FORMAT " stream_time:%" GST_STIME_FORMAT
-        " uri:%s", stream->current_segment->sequence,
-        GST_STIME_ARGS (stream->current_segment->stream_time),
-        stream->current_segment->uri);
-
-    /* Use best-effort techniques to find the correponding current media segment
-     * in the new playlist. This might be off in some cases, but it doesn't matter
-     * since we will be checking the embedded timestamp later */
-    new_segment =
-        gst_hls_media_playlist_sync_to_segment (new_playlist,
-        stream->current_segment);
-    if (new_segment) {
-      if (new_segment->discont_sequence !=
-          stream->current_segment->discont_sequence)
-        gst_hls_demux_add_time_mapping (demux, new_segment->discont_sequence,
-            new_segment->stream_time, new_segment->datetime);
-      /* This can happen in case of misaligned variants/renditions. Only warn about it */
-      if (new_segment->stream_time != stream->current_segment->stream_time)
-        GST_WARNING_OBJECT (stream,
-            "Returned segment stream time %" GST_STIME_FORMAT
-            " differs from current stream time %" GST_STIME_FORMAT,
-            GST_STIME_ARGS (new_segment->stream_time),
-            GST_STIME_ARGS (stream->current_segment->stream_time));
-    } else {
-      /* Not finding a matching segment only happens in live (otherwise we would
-       * have found a match by stream time) when we are at the live edge. This is normal*/
-      GST_DEBUG_OBJECT (stream, "Could not find a matching segment");
-    }
-    gst_m3u8_media_segment_unref (stream->current_segment);
-    stream->current_segment = new_segment;
-  } else {
-    GST_DEBUG_OBJECT (stream, "No current segment");
-  }
-
-  if (stream->playlist) {
-    gst_hls_media_playlist_unref (stream->playlist);
-    stream->playlist = new_playlist;
-  } else {
-    if (stream->is_variant) {
-      GST_DEBUG_OBJECT (stream, "Setting up initial playlist");
-      setup_initial_playlist (demux, new_playlist);
-    }
-    stream->playlist = new_playlist;
-  }
-
-  if (stream->is_variant) {
-    /* Update time mappings. We only use the variant stream for collecting
-     * mappings since it is the reference on which rendition stream timing will
-     * be based. */
-    gst_hls_update_time_mappings (demux, stream->playlist);
-  }
-  gst_hls_media_playlist_dump (stream->playlist);
-
-  if (stream->current_segment) {
-    GST_DEBUG_OBJECT (stream,
-        "After update, current segment now sn:%" G_GINT64_FORMAT
-        " stream_time:%" GST_STIME_FORMAT " uri:%s",
-        stream->current_segment->sequence,
-        GST_STIME_ARGS (stream->current_segment->stream_time),
-        stream->current_segment->uri);
-  } else {
-    GST_DEBUG_OBJECT (stream, "No current segment selected");
-  }
-
-  GST_DEBUG_OBJECT (stream, "done");
-
-  return GST_FLOW_OK;
-
-  /* ERRORS */
-lost_sync:
-  {
-    /* Set new playlist, lost sync handler will know what to do with it */
-    if (stream->playlist)
-      gst_hls_media_playlist_unref (stream->playlist);
-    stream->playlist = new_playlist;
-
-    gst_hls_demux_reset_for_lost_sync (demux);
-
-    return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
-  }
-}
-
-static GstFlowReturn
-gst_hls_demux_stream_update_rendition_playlist (GstHLSDemux * demux,
-    GstHLSDemuxStream * stream)
-{
-  GstFlowReturn ret = GST_FLOW_OK;
-  GstHLSRenditionStream *target_rendition =
-      stream->pending_rendition ? stream->
-      pending_rendition : stream->current_rendition;
-
-  ret = gst_hls_demux_stream_update_media_playlist (demux, stream,
-      &target_rendition->uri, NULL);
-  if (ret != GST_FLOW_OK)
-    return ret;
-
-  if (stream->pending_rendition) {
-    gst_hls_rendition_stream_unref (stream->current_rendition);
-    /* Stealing ref */
-    stream->current_rendition = stream->pending_rendition;
-    stream->pending_rendition = NULL;
+    }
   }
 
-  stream->playlist_fetched = TRUE;
-
-  return ret;
+  g_list_free_full (hlsdemux->mappings, (GDestroyNotify) gst_hls_time_map_free);
+  hlsdemux->mappings = active;
 }
 
-static GstFlowReturn
-gst_hls_demux_stream_update_variant_playlist (GstHLSDemux * demux,
-    GstHLSDemuxStream * stream, GError ** err)
+/* Go over the DSN from the playlist and add any missing time mapping */
+void
+gst_hls_update_time_mappings (GstHLSDemux * demux,
+    GstHLSMediaPlaylist * playlist)
 {
-  GstFlowReturn ret = GST_FLOW_OK;
-  GstHLSVariantStream *target_variant =
-      demux->pending_variant ? demux->pending_variant : demux->current_variant;
+  guint idx, len = playlist->segments->len;
+  gint64 dsn = G_MAXINT64;
 
-  ret = gst_hls_demux_stream_update_media_playlist (demux, stream,
-      &target_variant->uri, err);
-  if (ret != GST_FLOW_OK)
-    return ret;
+  for (idx = 0; idx < len; idx++) {
+    GstM3U8MediaSegment *segment = g_ptr_array_index (playlist->segments, idx);
 
-  if (demux->pending_variant) {
-    gst_hls_variant_stream_unref (demux->current_variant);
-    /* Stealing ref */
-    demux->current_variant = demux->pending_variant;
-    demux->pending_variant = NULL;
+    if (dsn == G_MAXINT64 || segment->discont_sequence != dsn) {
+      dsn = segment->discont_sequence;
+      if (!gst_hls_demux_find_time_map (demux, segment->discont_sequence))
+        gst_hls_demux_add_time_mapping (demux, segment->discont_sequence,
+            segment->stream_time, segment->datetime);
+    }
   }
-
-  stream->playlist_fetched = TRUE;
-
-  return ret;
 }
 
+/* Called by the base class with the manifest lock held */
 static GstFlowReturn
-gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
+gst_hls_demux_update_manifest (GstAdaptiveDemux * demux)
 {
-  GstFlowReturn ret = GST_FLOW_OK;
-  GstHLSDemuxStream *hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
-  GstAdaptiveDemux *demux = stream->demux;
   GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
-  GstM3U8MediaSegment *file;
-  gboolean discont;
-
-  /* If the rendition playlist needs to be updated, do it now */
-  if (!hlsdemux_stream->is_variant && !hlsdemux_stream->playlist_fetched) {
-    ret = gst_hls_demux_stream_update_rendition_playlist (hlsdemux,
-        hlsdemux_stream);
-    if (ret != GST_FLOW_OK)
-      return ret;
-  }
-
-  GST_DEBUG_OBJECT (stream,
-      "Updating fragment information, current_position:%" GST_TIME_FORMAT,
-      GST_TIME_ARGS (stream->current_position));
-
-  /* Find the current segment if we don't already have it */
-  if (hlsdemux_stream->current_segment == NULL) {
-    GST_LOG_OBJECT (stream, "No current segment");
-    if (stream->current_position == GST_CLOCK_TIME_NONE) {
-      GST_DEBUG_OBJECT (stream, "Setting up initial segment");
-      hlsdemux_stream->current_segment =
-          gst_hls_media_playlist_get_starting_segment
-          (hlsdemux_stream->playlist);
-    } else {
-      if (gst_hls_media_playlist_has_lost_sync (hlsdemux_stream->playlist,
-              stream->current_position)) {
-        GST_WARNING_OBJECT (stream, "Lost SYNC !");
-        return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
-      }
-      GST_DEBUG_OBJECT (stream,
-          "Looking up segment for position %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (stream->current_position));
-      hlsdemux_stream->current_segment =
-          gst_hls_media_playlist_seek (hlsdemux_stream->playlist, TRUE,
-          GST_SEEK_FLAG_SNAP_NEAREST, stream->current_position);
-
-      if (hlsdemux_stream->current_segment == NULL) {
-        GST_INFO_OBJECT (stream, "At the end of the current media playlist");
-        return GST_FLOW_EOS;
-      }
 
-      /* Update time mapping. If it already exists it will be ignored */
-      gst_hls_demux_add_time_mapping (hlsdemux,
-          hlsdemux_stream->current_segment->discont_sequence,
-          hlsdemux_stream->current_segment->stream_time,
-          hlsdemux_stream->current_segment->datetime);
-    }
+  /* Take a copy of the main variant playlist for base class
+   * calls that need access from outside the scheduler task,
+   * holding the MANIFEST_LOCK */
+  if (hlsdemux->main_stream && hlsdemux->main_stream->playlist) {
+    if (hlsdemux->main_playlist)
+      gst_hls_media_playlist_unref (hlsdemux->main_playlist);
+    hlsdemux->main_playlist =
+        gst_hls_media_playlist_ref (hlsdemux->main_stream->playlist);
+    return GST_FLOW_OK;
   }
 
-  file = hlsdemux_stream->current_segment;
-
-  GST_DEBUG_OBJECT (stream, "Current segment stream_time %" GST_STIME_FORMAT,
-      GST_STIME_ARGS (file->stream_time));
-
-  discont = file->discont || stream->discont;
-
-  gboolean need_header = GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER (stream);
+  return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
+}
 
-  /* Check if the MAP header file changed and update it */
-  if (file->init_file != NULL
-      && !gst_m3u8_init_file_equal (hlsdemux_stream->init_file,
-          file->init_file)) {
-    GST_DEBUG_OBJECT (stream, "MAP header info changed. Updating");
-    if (hlsdemux_stream->init_file != NULL)
-      gst_m3u8_init_file_unref (hlsdemux_stream->init_file);
-    hlsdemux_stream->init_file = gst_m3u8_init_file_ref (file->init_file);
-    need_header = TRUE;
-  }
+void
+gst_hls_demux_handle_variant_playlist_update (GstHLSDemux * demux,
+    const gchar * playlist_uri, GstHLSMediaPlaylist * playlist)
+{
+  if (demux->main_stream == NULL || !demux->main_stream->playlist_fetched) {
+    GstM3U8MediaSegment *segment;
 
-  if (file->init_file && need_header) {
-    GstM3U8InitFile *header_file = file->init_file;
-    g_free (stream->fragment.header_uri);
-    stream->fragment.header_uri = g_strdup (header_file->uri);
-    stream->fragment.header_range_start = header_file->offset;
-    if (header_file->size != -1) {
-      stream->fragment.header_range_end =
-          header_file->offset + header_file->size - 1;
-    } else {
-      stream->fragment.header_range_end = -1;
+    GST_DEBUG_OBJECT (demux,
+        "Setting up initial variant segment and time mapping");
+
+    /* This is the initial variant playlist. We will use it to base all our timing
+     * from. */
+    segment = g_ptr_array_index (playlist->segments, 0);
+    if (segment) {
+      segment->stream_time = 0;
+      gst_hls_media_playlist_recalculate_stream_time (playlist, segment);
     }
-
-    stream->need_header = TRUE;
-  }
-
-  /* set up our source for download */
-  if (hlsdemux_stream->reset_pts || discont || demux->segment.rate < 0.0) {
-    stream->fragment.stream_time = file->stream_time;
-  } else {
-    stream->fragment.stream_time = GST_CLOCK_STIME_NONE;
   }
 
-  g_free (hlsdemux_stream->current_key);
-  hlsdemux_stream->current_key = g_strdup (file->key);
-  g_free (hlsdemux_stream->current_iv);
-  hlsdemux_stream->current_iv = g_memdup2 (file->iv, sizeof (file->iv));
-
-  g_free (stream->fragment.uri);
-  stream->fragment.uri = g_strdup (file->uri);
-
-  GST_DEBUG_OBJECT (stream, "Stream URI now %s", file->uri);
+  if (demux->pending_variant) {
+    /* The pending variant must always match the one that just got updated:
+     * The loader should only do a callback for the most recently set URI */
+    g_assert (!g_strcmp0 (demux->pending_variant->uri, playlist_uri));
 
-  stream->fragment.range_start = file->offset;
-  if (file->size != -1)
-    stream->fragment.range_end = file->offset + file->size - 1;
-  else
-    stream->fragment.range_end = -1;
+    gboolean changed = (demux->pending_variant != demux->current_variant);
 
-  stream->fragment.duration = file->duration;
+    gst_hls_variant_stream_unref (demux->current_variant);
+    /* Stealing ref */
+    demux->current_variant = demux->pending_variant;
+    demux->pending_variant = NULL;
 
-  stream->recommended_buffering_threshold =
-      gst_hls_media_playlist_recommended_buffering_threshold
-      (hlsdemux_stream->playlist);
+    if (changed) {
+      GstAdaptiveDemux *basedemux = GST_ADAPTIVE_DEMUX (demux);
+      const gchar *main_uri =
+          gst_adaptive_demux_get_manifest_ref_uri (basedemux);
+      gchar *uri = demux->current_variant->uri;
+      gint new_bandwidth = demux->current_variant->bandwidth;
+
+      gst_element_post_message (GST_ELEMENT_CAST (demux),
+          gst_message_new_element (GST_OBJECT_CAST (demux),
+              gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
+                  "manifest-uri", G_TYPE_STRING,
+                  main_uri, "uri", G_TYPE_STRING,
+                  uri, "bitrate", G_TYPE_INT, new_bandwidth, NULL)));
+
+      GST_DEBUG_OBJECT (demux, "Changed variant");
+    }
+  }
 
-  if (discont)
-    stream->discont = TRUE;
+  /* Update time mappings. We only use the variant stream for collecting
+   * mappings since it is the reference on which rendition stream timing will
+   * be based. */
+  gst_hls_update_time_mappings (demux, playlist);
+  gst_hls_media_playlist_dump (playlist);
 
-  return ret;
+  /* Get the base class to call the update_manifest() vfunc with the MANIFEST_LOCK()
+   * held */
+  gst_adaptive_demux2_manual_manifest_update (GST_ADAPTIVE_DEMUX (demux));
 }
 
-static gboolean
-gst_hls_demux_stream_can_start (GstAdaptiveDemux2Stream * stream)
+void
+gst_hls_demux_handle_variant_playlist_update_error (GstHLSDemux * demux,
+    const gchar * playlist_uri)
 {
-  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
-  GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
-  GList *tmp;
+  GST_DEBUG_OBJECT (demux, "Playlist update failure for variant URI %s",
+      playlist_uri);
 
-  GST_DEBUG_OBJECT (stream, "is_variant:%d mappings:%p", hls_stream->is_variant,
-      hlsdemux->mappings);
+  /* Check if this is a new load of the pending variant, or a reload
+   * of the current variant */
+  GstHLSVariantStream *variant = demux->pending_variant;
+  if (variant == NULL)
+    variant = demux->current_variant;
 
-  /* Variant streams can always start straight away */
-  if (hls_stream->is_variant)
-    return TRUE;
+  /* If the variant has an fallback URIs available, we can try one of those */
+  if (variant->fallback != NULL) {
+    gchar *fallback_uri = (gchar *) (variant->fallback->data);
 
-  /* Renditions of the exact same type as the variant are pure alternatives,
-   * they must be started. This can happen for example with audio-only manifests
-   * where the initial stream selected is a rendition and not a variant */
-  if (hls_stream->rendition_type == hlsdemux->main_stream->rendition_type)
-    return TRUE;
+    GST_DEBUG_OBJECT (demux,
+        "Variant playlist update failed. Switching to fallback URI %s",
+        fallback_uri);
 
-  /* Rendition streams only require delaying if we don't have time mappings yet */
-  if (!hlsdemux->mappings)
-    return FALSE;
+    variant->fallback = g_list_remove (variant->fallback, fallback_uri);
+    g_free (variant->uri);
+    variant->uri = fallback_uri;
 
-  /* We can start if we have at least one internal time observation */
-  for (tmp = hlsdemux->mappings; tmp; tmp = tmp->next) {
-    GstHLSTimeMap *map = tmp->data;
-    if (map->internal_time != GST_CLOCK_TIME_NONE)
-      return TRUE;
+    if (demux->main_stream) {
+      /* The variant stream exists, update the playlist we're loading */
+      gst_hls_demux_stream_set_playlist_uri (demux->main_stream, variant->uri);
+    }
+    return;
   }
 
-  /* Otherwise we have to wait */
-  return FALSE;
-}
-
-/* Returns TRUE if the rendition stream switched group-id */
-static gboolean
-gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
-    GstHLSDemuxStream * hls_stream, GError ** err)
-{
-  gchar *current_group_id, *requested_group_id;
-  GstHLSRenditionStream *replacement_media = NULL;
-  GList *tmp;
-
-  /* There always should be a current variant set */
-  g_assert (hlsdemux->current_variant);
-  /* There always is a GstHLSRenditionStream set for rendition streams */
-  g_assert (hls_stream->current_rendition);
+  GST_DEBUG_OBJECT (demux, "Variant playlist update failed. "
+      "Marking variant URL %s as failed and switching over to another variant",
+      playlist_uri);
 
-  requested_group_id =
-      hlsdemux->current_variant->media_groups[hls_stream->
-      current_rendition->mtype];
-  current_group_id = hls_stream->current_rendition->group_id;
+  /* The variant must always match the one that just got updated:
+   * The loader should only do a callback for the most recently set URI */
+  g_assert (!g_strcmp0 (variant->uri, playlist_uri));
 
-  GST_DEBUG_OBJECT (hlsdemux,
-      "Checking playlist change for variant stream %s lang: %s current group-id: %s / requested group-id: %s",
-      gst_stream_type_get_name (hls_stream->rendition_type), hls_stream->lang,
-      current_group_id, requested_group_id);
-
-
-  if (!g_strcmp0 (requested_group_id, current_group_id)) {
-    GST_DEBUG_OBJECT (hlsdemux, "No change needed");
-    return FALSE;
+  /* If we didn't already add this playlist to the failed variants list
+   * do so now. It's possible we get an update error again if we failed
+   * to choose a new variant and posted error but didn't get shut down
+   * yet */
+  if (g_list_find (demux->failed_variants, variant) == NULL) {
+    demux->failed_variants =
+        g_list_prepend (demux->failed_variants,
+        gst_hls_variant_stream_ref (variant));
   }
 
-  GST_DEBUG_OBJECT (hlsdemux,
-      "group-id changed, looking for replacement playlist");
-
-  /* Need to switch/update */
-  for (tmp = hlsdemux->master->renditions; tmp; tmp = tmp->next) {
-    GstHLSRenditionStream *cand = tmp->data;
+  /* Now try to find another variant to play */
+  gdouble play_rate = gst_adaptive_demux_play_rate (GST_ADAPTIVE_DEMUX (demux));
+  guint64 bitrate = gst_hls_demux_get_bitrate (demux);
 
-    if (cand->mtype == hls_stream->current_rendition->mtype
-        && !g_strcmp0 (cand->lang, hls_stream->lang)
-        && !g_strcmp0 (cand->group_id, requested_group_id)) {
-      replacement_media = cand;
-      break;
-    }
-  }
-  if (!replacement_media) {
-    GST_ERROR_OBJECT (hlsdemux,
-        "Could not find a replacement playlist. Staying with previous one");
-    return FALSE;
-  }
+  GST_DEBUG_OBJECT (demux, "Trying to find failover variant playlist");
 
-  GST_DEBUG_OBJECT (hlsdemux, "Use replacement playlist %s",
-      replacement_media->name);
-  hls_stream->playlist_fetched = FALSE;
-  if (hls_stream->pending_rendition) {
-    GST_ERROR_OBJECT (hlsdemux,
-        "Already had a pending rendition switch to '%s'",
-        hls_stream->pending_rendition->name);
-    gst_hls_rendition_stream_unref (hls_stream->pending_rendition);
+  if (!gst_hls_demux_change_variant_playlist (demux,
+          variant->iframe, bitrate / MAX (1.0, ABS (play_rate)), NULL)) {
+    GST_ERROR_OBJECT (demux, "Failed to choose a new variant to play");
+    GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+        (_("Internal data stream error.")),
+        ("Could not update any variant playlist"));
   }
-  hls_stream->pending_rendition =
-      gst_hls_rendition_stream_ref (replacement_media);
-  return TRUE;
 }
 
-static gboolean
-gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
-    guint64 bitrate)
+/* Reset hlsdemux in case of live synchronization loss (i.e. when a media
+ * playlist update doesn't match at all with the previous one) */
+void
+gst_hls_demux_reset_for_lost_sync (GstHLSDemux * hlsdemux)
 {
-  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux);
-  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
-  GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
+  GstAdaptiveDemux *demux = (GstAdaptiveDemux *) hlsdemux;
+  GList *iter;
 
-  /* Fast-Path, no changes possible */
-  if (hlsdemux->master == NULL || hlsdemux->master->is_simple)
-    return FALSE;
+  GST_DEBUG_OBJECT (hlsdemux, "Resetting for lost sync");
+
+  for (iter = demux->input_period->streams; iter; iter = iter->next) {
+    GstHLSDemuxStream *hls_stream = iter->data;
+    GstAdaptiveDemux2Stream *stream = (GstAdaptiveDemux2Stream *) hls_stream;
 
-  if (hls_stream->is_variant) {
-    gdouble play_rate = gst_adaptive_demux_play_rate (demux);
-    gboolean changed = FALSE;
+    if (hls_stream->current_segment)
+      gst_m3u8_media_segment_unref (hls_stream->current_segment);
+    hls_stream->current_segment = NULL;
 
-    /* Handle variant streams */
-    GST_DEBUG_OBJECT (hlsdemux,
-        "Checking playlist change for main variant stream");
-    gst_hls_demux_change_playlist (hlsdemux, bitrate / MAX (1.0,
-            ABS (play_rate)), &changed);
+    if (hls_stream->is_variant) {
+      GstHLSTimeMap *map;
+      GstM3U8SeekResult seek_result;
 
-    GST_DEBUG_OBJECT (hlsdemux, "Returning changed: %d", changed);
-    return changed;
+      /* Resynchronize the variant stream */
+      g_assert (stream->current_position != GST_CLOCK_STIME_NONE);
+      if (gst_hls_media_playlist_get_starting_segment (hls_stream->playlist,
+              &seek_result)) {
+        hls_stream->current_segment = seek_result.segment;
+        hls_stream->in_partial_segments = seek_result.found_partial_segment;
+        hls_stream->part_idx = seek_result.part_idx;
+
+        hls_stream->current_segment->stream_time = stream->current_position;
+        gst_hls_media_playlist_recalculate_stream_time (hls_stream->playlist,
+            hls_stream->current_segment);
+        GST_DEBUG_OBJECT (stream,
+            "Resynced variant playlist to %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (stream->current_position));
+        map =
+            gst_hls_demux_find_time_map (hlsdemux,
+            hls_stream->current_segment->discont_sequence);
+        if (map)
+          map->internal_time = GST_CLOCK_TIME_NONE;
+        gst_hls_update_time_mappings (hlsdemux, hls_stream->playlist);
+        gst_hls_media_playlist_dump (hls_stream->playlist);
+      } else {
+        GST_ERROR_OBJECT (stream, "Failed to locate a segment to restart at!");
+      }
+    } else {
+      /* Force playlist update for the rendition streams, it will resync to the
+       * variant stream on the next round */
+      if (hls_stream->playlist)
+        gst_hls_media_playlist_unref (hls_stream->playlist);
+      hls_stream->playlist = NULL;
+      hls_stream->playlist_fetched = FALSE;
+    }
   }
-
-  /* Handle rendition streams */
-  return gst_hls_demux_update_rendition_stream (hlsdemux, hls_stream, NULL);
 }
 
 static void
@@ -2620,6 +1272,10 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
     gst_hls_master_playlist_unref (demux->master);
     demux->master = NULL;
   }
+  if (demux->main_playlist) {
+    gst_hls_media_playlist_unref (demux->main_playlist);
+    demux->main_playlist = NULL;
+  }
   if (demux->current_variant != NULL) {
     gst_hls_variant_stream_unref (demux->current_variant);
     demux->current_variant = NULL;
@@ -2628,6 +1284,11 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
     gst_hls_variant_stream_unref (demux->pending_variant);
     demux->pending_variant = NULL;
   }
+  if (demux->failed_variants != NULL) {
+    g_list_free_full (demux->failed_variants,
+        (GDestroyNotify) gst_hls_variant_stream_unref);
+    demux->failed_variants = NULL;
+  }
 
   g_list_free_full (demux->mappings, (GDestroyNotify) gst_hls_time_map_free);
   demux->mappings = NULL;
@@ -2635,376 +1296,66 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
   gst_hls_demux_clear_all_pending_data (demux);
 }
 
-/*
- * update: TRUE only when requested from parent class (via
- * ::demux_update_manifest() or ::change_playlist() ).
- */
 static GstFlowReturn
-gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update,
-    GError ** err)
+gst_hls_demux_check_variant_playlist_loaded (GstHLSDemux * demux)
 {
-  GstFlowReturn ret = GST_FLOW_OK;
-  GstAdaptiveDemux *adaptive_demux = GST_ADAPTIVE_DEMUX (demux);
-
-  GST_DEBUG_OBJECT (demux, "update:%d", update);
-
-  /* Download and update the appropriate variant playlist (pending if any, else
-   * current) */
-  ret = gst_hls_demux_stream_update_variant_playlist (demux, demux->main_stream,
-      err);
-  if (ret != GST_FLOW_OK)
-    return ret;
+  GstHLSVariantStream *target_variant =
+      demux->pending_variant ? demux->pending_variant : demux->current_variant;
+  GstHLSDemuxStream *stream = demux->main_stream;
 
-  if (update && gst_hls_demux_is_live (adaptive_demux)) {
-    GList *tmp;
-    GST_DEBUG_OBJECT (demux,
-        "LIVE, Marking rendition streams to be updated next");
-    /* We're live, instruct all rendition medias to be updated next */
-    for (tmp = adaptive_demux->input_period->streams; tmp; tmp = tmp->next) {
-      GstHLSDemuxStream *hls_stream = tmp->data;
-      if (!hls_stream->is_variant)
-        hls_stream->playlist_fetched = FALSE;
-    }
-  }
+  /* The demuxer has been resetted in the meantime */
+  if (target_variant == NULL)
+    return GST_FLOW_FLUSHING;
 
-  return GST_FLOW_OK;
+  return gst_hls_demux_stream_check_current_playlist_uri (stream,
+      target_variant->uri);
 }
 
-static gboolean
-gst_hls_demux_change_playlist (GstHLSDemux * demux, guint max_bitrate,
-    gboolean * changed)
+gboolean
+gst_hls_demux_change_variant_playlist (GstHLSDemux * demux,
+    gboolean iframe_variant, guint max_bitrate, gboolean * changed)
 {
-  GstHLSVariantStream *lowest_variant, *lowest_ivariant;
-  GstHLSVariantStream *previous_variant, *new_variant;
-  gint old_bandwidth, new_bandwidth;
   GstAdaptiveDemux *adaptive_demux = GST_ADAPTIVE_DEMUX_CAST (demux);
-  GstAdaptiveDemux2Stream *stream;
 
-  g_return_val_if_fail (demux->main_stream != NULL, FALSE);
-  stream = (GstAdaptiveDemux2Stream *) demux->main_stream;
+  if (changed)
+    *changed = FALSE;
 
-  /* Make sure we keep a reference in case we need to switch back */
-  previous_variant = gst_hls_variant_stream_ref (demux->current_variant);
-  new_variant =
+  /* Make sure we keep a reference for the debug output below */
+  GstHLSVariantStream *new_variant =
       gst_hls_master_playlist_get_variant_for_bitrate (demux->master,
-      demux->current_variant, max_bitrate, adaptive_demux->min_bitrate);
+      iframe_variant, max_bitrate, adaptive_demux->min_bitrate,
+      demux->failed_variants);
+
+  /* We're out of available variants to use */
+  if (new_variant == NULL) {
+    return FALSE;
+  }
 
-retry_failover_protection:
-  old_bandwidth = previous_variant->bandwidth;
-  new_bandwidth = new_variant->bandwidth;
+  GstHLSVariantStream *previous_variant =
+      gst_hls_variant_stream_ref (demux->current_variant);
 
   /* Don't do anything else if the playlist is the same */
-  if (new_bandwidth == old_bandwidth) {
+  if (new_variant == previous_variant) {
+    GST_TRACE_OBJECT (demux, "Variant didn't change from bandwidth %dbps",
+        new_variant->bandwidth);
     gst_hls_variant_stream_unref (previous_variant);
     return TRUE;
   }
 
   gst_hls_demux_set_current_variant (demux, new_variant);
 
-  GST_INFO_OBJECT (demux, "Client was on %dbps, max allowed is %dbps, switching"
-      " to bitrate %dbps", old_bandwidth, max_bitrate, new_bandwidth);
-
-  if (gst_hls_demux_update_playlist (demux, TRUE, NULL) == GST_FLOW_OK) {
-    const gchar *main_uri;
-    gchar *uri = new_variant->uri;
-
-    main_uri = gst_adaptive_demux_get_manifest_ref_uri (adaptive_demux);
-    gst_element_post_message (GST_ELEMENT_CAST (demux),
-        gst_message_new_element (GST_OBJECT_CAST (demux),
-            gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
-                "manifest-uri", G_TYPE_STRING,
-                main_uri, "uri", G_TYPE_STRING,
-                uri, "bitrate", G_TYPE_INT, new_bandwidth, NULL)));
-    if (changed)
-      *changed = TRUE;
-    stream->discont = TRUE;
-  } else if (gst_adaptive_demux2_is_running (GST_ADAPTIVE_DEMUX_CAST (demux))) {
-    GstHLSVariantStream *failover_variant = NULL;
-    GList *failover;
-
-    GST_INFO_OBJECT (demux, "Unable to update playlist. Switching back");
-
-    /* we find variants by bitrate by going from highest to lowest, so it's
-     * possible that there's another variant with the same bitrate before the
-     * one selected which we can use as failover */
-    failover = g_list_find (demux->master->variants, new_variant);
-    if (failover != NULL)
-      failover = failover->prev;
-    if (failover != NULL)
-      failover_variant = failover->data;
-    if (failover_variant && new_bandwidth == failover_variant->bandwidth) {
-      new_variant = failover_variant;
-      goto retry_failover_protection;
-    }
+  gint new_bandwidth = new_variant->bandwidth;
 
-    gst_hls_demux_set_current_variant (demux, previous_variant);
-
-    /*  Try a lower bitrate (or stop if we just tried the lowest) */
-    if (previous_variant->iframe) {
-      lowest_ivariant = demux->master->iframe_variants->data;
-      if (new_bandwidth == lowest_ivariant->bandwidth) {
-        gst_hls_variant_stream_unref (previous_variant);
-        return FALSE;
-      }
-    } else {
-      lowest_variant = demux->master->variants->data;
-      if (new_bandwidth == lowest_variant->bandwidth) {
-        gst_hls_variant_stream_unref (previous_variant);
-        return FALSE;
-      }
-    }
-    gst_hls_variant_stream_unref (previous_variant);
-    return gst_hls_demux_change_playlist (demux, new_bandwidth - 1, changed);
-  }
+  GST_INFO_OBJECT (demux, "Client was on %dbps, max allowed is %dbps, switching"
+      " to bitrate %dbps", previous_variant->bandwidth, max_bitrate,
+      new_bandwidth);
 
   gst_hls_variant_stream_unref (previous_variant);
+  if (changed)
+    *changed = TRUE;
   return TRUE;
 }
 
-#if defined(HAVE_OPENSSL)
-static gboolean
-gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
-    const guint8 * key_data, const guint8 * iv_data)
-{
-  EVP_CIPHER_CTX *ctx;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-  EVP_CIPHER_CTX_init (&stream->aes_ctx);
-  ctx = &stream->aes_ctx;
-#else
-  stream->aes_ctx = EVP_CIPHER_CTX_new ();
-  ctx = stream->aes_ctx;
-#endif
-  if (!EVP_DecryptInit_ex (ctx, EVP_aes_128_cbc (), NULL, key_data, iv_data))
-    return FALSE;
-  EVP_CIPHER_CTX_set_padding (ctx, 0);
-  return TRUE;
-}
-
-static gboolean
-decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
-    const guint8 * encrypted_data, guint8 * decrypted_data)
-{
-  int len, flen = 0;
-  EVP_CIPHER_CTX *ctx;
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-  ctx = &stream->aes_ctx;
-#else
-  ctx = stream->aes_ctx;
-#endif
-
-  if (G_UNLIKELY (length > G_MAXINT || length % 16 != 0))
-    return FALSE;
-
-  len = (int) length;
-  if (!EVP_DecryptUpdate (ctx, decrypted_data, &len, encrypted_data, len))
-    return FALSE;
-  EVP_DecryptFinal_ex (ctx, decrypted_data + len, &flen);
-  g_return_val_if_fail (len + flen == length, FALSE);
-  return TRUE;
-}
-
-static void
-gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
-{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-  EVP_CIPHER_CTX_cleanup (&stream->aes_ctx);
-#else
-  EVP_CIPHER_CTX_free (stream->aes_ctx);
-  stream->aes_ctx = NULL;
-#endif
-}
-
-#elif defined(HAVE_NETTLE)
-static gboolean
-gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
-    const guint8 * key_data, const guint8 * iv_data)
-{
-  aes128_set_decrypt_key (&stream->aes_ctx.ctx, key_data);
-  CBC_SET_IV (&stream->aes_ctx, iv_data);
-
-  return TRUE;
-}
-
-static gboolean
-decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
-    const guint8 * encrypted_data, guint8 * decrypted_data)
-{
-  if (length % 16 != 0)
-    return FALSE;
-
-  CBC_DECRYPT (&stream->aes_ctx, aes128_decrypt, length, decrypted_data,
-      encrypted_data);
-
-  return TRUE;
-}
-
-static void
-gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
-{
-  /* NOP */
-}
-
-#elif defined(HAVE_LIBGCRYPT)
-static gboolean
-gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
-    const guint8 * key_data, const guint8 * iv_data)
-{
-  gcry_error_t err = 0;
-  gboolean ret = FALSE;
-
-  err =
-      gcry_cipher_open (&stream->aes_ctx, GCRY_CIPHER_AES128,
-      GCRY_CIPHER_MODE_CBC, 0);
-  if (err)
-    goto out;
-  err = gcry_cipher_setkey (stream->aes_ctx, key_data, 16);
-  if (err)
-    goto out;
-  err = gcry_cipher_setiv (stream->aes_ctx, iv_data, 16);
-  if (!err)
-    ret = TRUE;
-
-out:
-  if (!ret)
-    if (stream->aes_ctx)
-      gcry_cipher_close (stream->aes_ctx);
-
-  return ret;
-}
-
-static gboolean
-decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
-    const guint8 * encrypted_data, guint8 * decrypted_data)
-{
-  gcry_error_t err = 0;
-
-  err = gcry_cipher_decrypt (stream->aes_ctx, decrypted_data, length,
-      encrypted_data, length);
-
-  return err == 0;
-}
-
-static void
-gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
-{
-  if (stream->aes_ctx) {
-    gcry_cipher_close (stream->aes_ctx);
-    stream->aes_ctx = NULL;
-  }
-}
-
-#else
-/* NO crypto available */
-static gboolean
-gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
-    const guint8 * key_data, const guint8 * iv_data)
-{
-  GST_ERROR ("No crypto available");
-  return FALSE;
-}
-
-static gboolean
-decrypt_fragment (GstHLSDemuxStream * stream, gsize length,
-    const guint8 * encrypted_data, guint8 * decrypted_data)
-{
-  GST_ERROR ("Cannot decrypt fragment, no crypto available");
-  return FALSE;
-}
-
-static void
-gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream)
-{
-  return;
-}
-#endif
-
-static GstBuffer *
-gst_hls_demux_decrypt_fragment (GstHLSDemux * demux, GstHLSDemuxStream * stream,
-    GstBuffer * encrypted_buffer, GError ** err)
-{
-  GstBuffer *decrypted_buffer = NULL;
-  GstMapInfo encrypted_info, decrypted_info;
-
-  decrypted_buffer =
-      gst_buffer_new_allocate (NULL, gst_buffer_get_size (encrypted_buffer),
-      NULL);
-
-  gst_buffer_map (encrypted_buffer, &encrypted_info, GST_MAP_READ);
-  gst_buffer_map (decrypted_buffer, &decrypted_info, GST_MAP_WRITE);
-
-  if (!decrypt_fragment (stream, encrypted_info.size,
-          encrypted_info.data, decrypted_info.data))
-    goto decrypt_error;
-
-
-  gst_buffer_unmap (decrypted_buffer, &decrypted_info);
-  gst_buffer_unmap (encrypted_buffer, &encrypted_info);
-
-  gst_buffer_unref (encrypted_buffer);
-
-  return decrypted_buffer;
-
-decrypt_error:
-  GST_ERROR_OBJECT (demux, "Failed to decrypt fragment");
-  g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_DECRYPT,
-      "Failed to decrypt fragment");
-
-  gst_buffer_unmap (decrypted_buffer, &decrypted_info);
-  gst_buffer_unmap (encrypted_buffer, &encrypted_info);
-
-  gst_buffer_unref (encrypted_buffer);
-  gst_buffer_unref (decrypted_buffer);
-
-  return NULL;
-}
-
-static gint64
-gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
-{
-  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
-  GstClockTime target_duration = 5 * GST_SECOND;
-
-  if (hlsdemux->main_stream && hlsdemux->main_stream->playlist) {
-    GstHLSMediaPlaylist *playlist = hlsdemux->main_stream->playlist;
-
-    if (playlist->version > 5) {
-      target_duration = hlsdemux->main_stream->playlist->targetduration;
-    } else if (playlist->segments->len) {
-      GstM3U8MediaSegment *last_seg =
-          g_ptr_array_index (playlist->segments, playlist->segments->len - 1);
-      target_duration = last_seg->duration;
-    }
-    if (playlist->reloaded && target_duration > (playlist->targetduration / 2)) {
-      GST_DEBUG_OBJECT (demux,
-          "Playlist didn't change previously, returning lower update interval");
-      target_duration /= 2;
-    }
-  }
-
-  GST_DEBUG_OBJECT (demux, "Returning update interval of %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (target_duration));
-
-  return gst_util_uint64_scale (target_duration, G_USEC_PER_SEC, GST_SECOND);
-}
-
-static GstClockTime
-gst_hls_demux_stream_get_presentation_offset (GstAdaptiveDemux2Stream * stream)
-{
-  GstHLSDemux *hlsdemux = (GstHLSDemux *) stream->demux;
-  GstHLSDemuxStream *hls_stream = (GstHLSDemuxStream *) stream;
-
-  GST_DEBUG_OBJECT (stream, "presentation_offset %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (hls_stream->presentation_offset));
-
-  /* If this stream and the variant stream are ISOBMFF, returns the presentation
-   * offset of the variant stream */
-  if (hls_stream->parser_type == GST_HLS_PARSER_ISOBMFF
-      && hlsdemux->main_stream->parser_type == GST_HLS_PARSER_ISOBMFF)
-    return hlsdemux->main_stream->presentation_offset;
-  return hls_stream->presentation_offset;
-}
-
 static gboolean
 gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
     gint64 * stop)
@@ -3012,10 +1363,11 @@ gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
   GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
   gboolean ret = FALSE;
 
-  if (hlsdemux->main_stream && hlsdemux->main_stream->playlist)
+  if (hlsdemux->main_playlist) {
     ret =
-        gst_hls_media_playlist_get_seek_range (hlsdemux->main_stream->playlist,
-        start, stop);
+        gst_hls_media_playlist_get_seek_range (hlsdemux->main_playlist, start,
+        stop);
+  }
 
   return ret;
 }
diff --git a/ext/adaptivedemux2/hls/gsthlsdemux.h b/ext/adaptivedemux2/hls/gsthlsdemux.h
index d4acf754c31cb0652c5c77a630d15d7eb700f5b2..857577f042754cb794e22d687d08783d62511c4e 100644
--- a/ext/adaptivedemux2/hls/gsthlsdemux.h
+++ b/ext/adaptivedemux2/hls/gsthlsdemux.h
@@ -30,14 +30,6 @@
 #include "m3u8.h"
 #include "gstisoff.h"
 #include "gstadaptivedemux.h"
-#if defined(HAVE_OPENSSL)
-#include <openssl/evp.h>
-#elif defined(HAVE_NETTLE)
-#include <nettle/aes.h>
-#include <nettle/cbc.h>
-#elif defined(HAVE_LIBGCRYPT)
-#include <gcrypt.h>
-#endif
 
 G_BEGIN_DECLS
 
@@ -59,22 +51,7 @@ G_BEGIN_DECLS
 typedef struct _GstHLSDemux2 GstHLSDemux;
 typedef struct _GstHLSDemux2Class GstHLSDemuxClass;
 
-#define GST_TYPE_HLS_DEMUX_STREAM \
-  (gst_hls_demux_stream_get_type())
-#define GST_HLS_DEMUX_STREAM(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HLS_DEMUX_STREAM,GstHLSDemuxStream))
-#define GST_HLS_DEMUX_STREAM_CAST(obj) ((GstHLSDemuxStream *)obj)
-
 typedef struct _GstHLSDemuxStream GstHLSDemuxStream;
-typedef GstAdaptiveDemux2StreamClass GstHLSDemuxStreamClass;
-
-typedef enum {
-  GST_HLS_PARSER_NONE,
-  GST_HLS_PARSER_MPEGTS,
-  GST_HLS_PARSER_ID3,
-  GST_HLS_PARSER_WEBVTT,
-  GST_HLS_PARSER_ISOBMFF
-} GstHLSParserType;
 
 typedef enum {
   /* More data is needed to parse the fragment */
@@ -88,90 +65,6 @@ typedef enum {
   GST_HLS_PARSER_RESULT_RESYNC
 } GstHLSParserResult;
 
-struct _GstHLSDemuxStream
-{
-  GstAdaptiveDemux2Stream adaptive_demux_stream;
-
-  /* A stream either variants or renditions */
-  gboolean is_variant;
-
-  /* Rendition-specific fields */
-  GstStreamType rendition_type;	/* FIXME: Also used by variant streams */
-  gchar *lang;
-  gchar *name;
-  GstHLSRenditionStream *current_rendition;
-  /* rendition to switch to */
-  GstHLSRenditionStream *pending_rendition;
-  /* End of Rendition-specific fields */
-
-  /* Whether the underlying playlist was fetched on creation */
-  gboolean playlist_fetched;
-
-  /* The media playlist currently used */
-  GstHLSMediaPlaylist *playlist;
-
-  /* The current header / init_file data */
-  GstM3U8InitFile *init_file;
-
-  /* The segment (from the above playlist) currently being used */
-  GstM3U8MediaSegment *current_segment;
-
-  /* Whether we need to typefind the next buffer */
-  gboolean do_typefind;
-
-  /* for collecting data until typefind succeeds */
-  GstBuffer *pending_typefind_buffer;
-
-  /* for chunking data into 16 byte multiples for decryption */
-  GstAdapter *pending_encrypted_data;
-
- /* last decrypted buffer for pkcs7 unpadding.  We only know that it is the last
-  * on ::finish_fragment() */
-  GstBuffer *pending_decrypted_buffer;
-
-  /* Current offset (in bytes) in fragment data we pushed downstream. Resets to
-   * -1 at every fragment start */
-  guint64 current_offset;
-
-  gboolean reset_pts;
-
-  /* decryption tooling */
-#if defined(HAVE_OPENSSL)
-# if OPENSSL_VERSION_NUMBER < 0x10100000L
-  EVP_CIPHER_CTX aes_ctx;
-# else
-  EVP_CIPHER_CTX *aes_ctx;
-# endif
-#elif defined(HAVE_NETTLE)
-  struct CBC_CTX (struct aes128_ctx, AES_BLOCK_SIZE) aes_ctx;
-#elif defined(HAVE_LIBGCRYPT)
-  gcry_cipher_hd_t aes_ctx;
-#endif
-
-  gchar     *current_key;
-  guint8    *current_iv;
-
-  /* The type of parser used for data handling */
-  GstHLSParserType parser_type;
-
-  /* Is content processing required ? */
-  gboolean process_buffer_content;
-  /* Data to be analyzed by  */
-  GstBuffer *pending_segment_data;
-  /* TRUE if pending_segment_data contains data from a header/index */
-  gboolean pending_data_is_header;
-
-  /* ISOBMFF */
-  GstMoovBox *moov;
-
-  /* Presentation offset to use and report. This value will be appended to all
-   * "output" stream times. Not enabled (i.e 0) if variant is ISOBMFF
-   */
-  GstClockTime presentation_offset;
-
-  gboolean pdt_tag_sent;
-};
-
 typedef struct {
   guint8 data[16];
 } GstHLSKey;
@@ -192,14 +85,20 @@ struct _GstHLSDemux2
   GHashTable *keys;
   GMutex      keys_lock;
 
-  /* FIXME: check locking, protected automatically by manifest_lock already? */
-  /* The master playlist with the available variant streams */
+  /* The master playlist with the available variant streams,
+   * created at demuxer start based on the input multivariant playlist */
   GstHLSMasterPlaylist *master;
 
+  /* A ref to the main playlist, for access from external threads */
+  GstHLSMediaPlaylist *main_playlist;
+
   GstHLSVariantStream  *current_variant;
-  /* The variant to switch to */
+  /* The variant we're switching to (currently being loaded by the playlist loader) */
   GstHLSVariantStream  *pending_variant;
 
+  /* List of failed variants that should be ignored */
+  GList *failed_variants;
+
   GstHLSDemuxStream *main_stream;
 
   /* Time Mappings (GstHLSTimeMap) */
@@ -211,11 +110,35 @@ struct _GstHLSDemux2Class
   GstAdaptiveDemuxClass parent_class;
 };
 
+GstAdaptiveDemuxTrack *
+gst_hls_demux_new_track_for_rendition (GstHLSDemux * demux,
+		GstHLSRenditionStream * rendition,
+    GstCaps * caps, GstStreamFlags flags, GstTagList * tags);
+
+void gst_hls_demux_start_rendition_streams (GstHLSDemux * hlsdemux);
+void gst_hls_demux_reset_for_lost_sync (GstHLSDemux * hlsdemux);
+const GstHLSKey *gst_hls_demux_get_key (GstHLSDemux * demux,
+    const gchar * key_url, const gchar * referer, gboolean allow_cache);
+
+void gst_hls_demux_handle_variant_playlist_update (GstHLSDemux * demux,
+    const gchar *playlist_uri, GstHLSMediaPlaylist * playlist);
+void gst_hls_demux_handle_variant_playlist_update_error (GstHLSDemux * demux,
+    const gchar *playlist_uri);
+gboolean gst_hls_demux_change_variant_playlist (GstHLSDemux * demux,
+    gboolean iframe_variant, guint max_bitrate, gboolean * changed);
+GstFlowReturn gst_hls_demux_update_variant_playlist (GstHLSDemux * demux,
+    GError ** err);
+
+void gst_time_map_set_values (GstHLSTimeMap *map, GstClockTimeDiff stream_time,
+			      GstClockTime internal_time, GDateTime *pdt);
+void gst_hls_demux_add_time_mapping (GstHLSDemux * demux, gint64 dsn,
+    GstClockTimeDiff stream_time, GDateTime * pdt);
+void gst_hls_update_time_mappings (GstHLSDemux * demux,
+    GstHLSMediaPlaylist * playlist);
 
 gchar *gst_hls_buf_to_utf8_text (GstBuffer * buf);
 
 /* Private */
-
 GstHLSParserResult gst_hlsdemux_handle_content_mpegts (GstHLSDemux       *demux,
 						       GstHLSDemuxStream *hls_stream,
 						       gboolean           draining,
@@ -236,14 +159,10 @@ GstHLSParserResult gst_hlsdemux_handle_content_webvtt (GstHLSDemux        *demux
 						       gboolean           draining,
 						       GstBuffer        **buffer);
 
-GstHLSParserResult gst_hlsdemux_handle_internal_time (GstHLSDemux       *demux,
-						      GstHLSDemuxStream *hls_stream,
-						      GstClockTime       internal_time);
-
 GstClockTimeDiff gst_hls_internal_to_stream_time (GstHLSTimeMap *map,
 						  GstClockTime   internal_time);
 
-GstHLSTimeMap *gst_hls_find_time_map (GstHLSDemux * demux, gint64 dsn);
+GstHLSTimeMap *gst_hls_demux_find_time_map (GstHLSDemux * demux, gint64 dsn);
 
 GType gst_hls_demux2_get_type (void);
 GType gst_hls_demux_stream_get_type (void);
diff --git a/ext/adaptivedemux2/hls/m3u8.c b/ext/adaptivedemux2/hls/m3u8.c
index 89558a9e21995f92018af90ec24c199419eca0c6..c4c7248cb82f955e7df25ac2478b878c7e015502 100644
--- a/ext/adaptivedemux2/hls/m3u8.c
+++ b/ext/adaptivedemux2/hls/m3u8.c
@@ -39,6 +39,9 @@
 #define GST_CAT_DEFAULT hls2_debug
 
 static gchar *uri_join (const gchar * uri, const gchar * path);
+static void
+gst_m3u8_media_segment_fill_partial_stream_times (GstM3U8MediaSegment *
+    segment);
 
 GstHLSMediaPlaylist *
 gst_hls_media_playlist_ref (GstHLSMediaPlaylist * m3u8)
@@ -60,6 +63,12 @@ gst_hls_media_playlist_unref (GstHLSMediaPlaylist * self)
 
     g_ptr_array_free (self->segments, TRUE);
 
+    if (self->preload_hints != NULL)
+      g_ptr_array_free (self->preload_hints, TRUE);
+
+    if (self->removed_date_ranges != NULL)
+      g_strfreev (self->removed_date_ranges);
+
     g_free (self->last_data);
     g_mutex_clear (&self->lock);
     g_free (self);
@@ -112,10 +121,71 @@ gst_m3u8_media_segment_unref (GstM3U8MediaSegment * self)
     g_free (self->key);
     if (self->datetime)
       g_date_time_unref (self->datetime);
+    if (self->partial_segments)
+      g_ptr_array_free (self->partial_segments, TRUE);
     g_free (self);
   }
 }
 
+GstM3U8PartialSegment *
+gst_m3u8_partial_segment_ref (GstM3U8PartialSegment * part)
+{
+  g_assert (part != NULL && part->ref_count > 0);
+
+  g_atomic_int_add (&part->ref_count, 1);
+  return part;
+}
+
+void
+gst_m3u8_partial_segment_unref (GstM3U8PartialSegment * part)
+{
+  g_return_if_fail (part != NULL && part->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&part->ref_count)) {
+    g_free (part->uri);
+    g_free (part);
+  }
+}
+
+GstM3U8PreloadHint *
+gst_m3u8_preload_hint_ref (GstM3U8PreloadHint * hint)
+{
+  g_assert (hint != NULL && hint->ref_count > 0);
+
+  g_atomic_int_add (&hint->ref_count, 1);
+  return hint;
+}
+
+void
+gst_m3u8_preload_hint_unref (GstM3U8PreloadHint * hint)
+{
+  g_return_if_fail (hint != NULL && hint->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&hint->ref_count)) {
+    g_free (hint->uri);
+    g_free (hint);
+  }
+}
+
+gboolean
+gst_m3u8_preload_hint_equal (GstM3U8PreloadHint * hint1,
+    GstM3U8PreloadHint * hint2)
+{
+  if (hint1->hint_type != hint2->hint_type)
+    return FALSE;
+
+  if (g_strcmp0 (hint1->uri, hint2->uri))
+    return FALSE;
+
+  if (hint1->offset != hint2->offset)
+    return FALSE;
+
+  if (hint1->size != hint2->size)
+    return FALSE;
+
+  return TRUE;
+}
+
 static GstM3U8InitFile *
 gst_m3u8_init_file_new (gchar * uri, gint64 size, gint64 offset)
 {
@@ -165,7 +235,7 @@ gst_m3u8_init_file_equal (const GstM3U8InitFile * ifile1,
   if (ifile1 != NULL && ifile2 == NULL)
     return FALSE;
 
-  if (!g_str_equal (ifile1->uri, ifile2->uri))
+  if (g_strcmp0 (ifile1->uri, ifile2->uri))
     return FALSE;
   if (ifile1->offset != ifile2->offset)
     return FALSE;
@@ -260,6 +330,17 @@ double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
   return end != ptr;
 }
 
+static gboolean
+time_from_double_in_string (gchar * ptr, gchar ** endptr, GstClockTime * val)
+{
+  double fval;
+  if (!double_from_string (ptr, endptr, &fval)) {
+    return FALSE;
+  }
+  *val = fval * (gdouble) GST_SECOND;
+  return TRUE;
+}
+
 static gboolean
 parse_attributes (gchar ** ptr, gchar ** a, gchar ** v)
 {
@@ -326,14 +407,17 @@ gst_hls_media_playlist_new (const gchar * uri, const gchar * base_uri)
 
   m3u8 = g_new0 (GstHLSMediaPlaylist, 1);
 
+  m3u8->playlist_ts = GST_CLOCK_TIME_NONE;
   m3u8->uri = g_strdup (uri);
   m3u8->base_uri = g_strdup (base_uri);
+  m3u8->request_time = GST_CLOCK_TIME_NONE;
 
   m3u8->version = 1;
   m3u8->type = GST_HLS_PLAYLIST_TYPE_UNDEFINED;
   m3u8->targetduration = GST_CLOCK_TIME_NONE;
+  m3u8->partial_targetduration = GST_CLOCK_TIME_NONE;
   m3u8->media_sequence = 0;
-  m3u8->discont_sequence = 0;
+  m3u8->discont_sequence = -1;
   m3u8->endlist = FALSE;
   m3u8->i_frame = FALSE;
   m3u8->allowcache = TRUE;
@@ -346,6 +430,10 @@ gst_hls_media_playlist_new (const gchar * uri, const gchar * base_uri)
 
   m3u8->duration = 0;
 
+  m3u8->skip_boundary = GST_CLOCK_TIME_NONE;
+  m3u8->hold_back = GST_CLOCK_TIME_NONE;
+  m3u8->part_hold_back = GST_CLOCK_TIME_NONE;
+
   g_mutex_init (&m3u8->lock);
   m3u8->ref_count = 1;
 
@@ -366,6 +454,8 @@ gst_hls_media_playlist_dump (GstHLSMediaPlaylist * self)
 
   GST_DEBUG ("targetduration   : %" GST_TIME_FORMAT,
       GST_TIME_ARGS (self->targetduration));
+  GST_DEBUG ("partial segment targetduration   : %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (self->partial_targetduration));
   GST_DEBUG ("media_sequence   : %" G_GINT64_FORMAT, self->media_sequence);
   GST_DEBUG ("discont_sequence : %" G_GINT64_FORMAT, self->discont_sequence);
 
@@ -381,12 +471,36 @@ gst_hls_media_playlist_dump (GstHLSMediaPlaylist * self)
   GST_DEBUG ("duration         : %" GST_TIME_FORMAT,
       GST_TIME_ARGS (self->duration));
 
+  GST_DEBUG ("skip boundary    : %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (self->skip_boundary));
+
+  GST_DEBUG ("skip dateranges  : %s", self->can_skip_dateranges ? "YES" : "NO");
+
+  GST_DEBUG ("hold back        : %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (self->hold_back));
+  GST_DEBUG ("part hold back   : %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (self->part_hold_back));
+
+  GST_DEBUG ("can block reloads: %s", self->can_block_reload ? "YES" : "NO");
+
+  GST_DEBUG ("skipped segments: %d", self->skipped_segments);
+
+  if (self->num_removed_date_ranges && self->removed_date_ranges) {
+    GST_DEBUG ("Removed date ranges: %u", self->num_removed_date_ranges);
+    gchar **cur = self->removed_date_ranges;
+    while (*cur != NULL) {
+      GST_DEBUG ("  ID: %s", *cur);
+      cur++;
+    }
+  }
+
   GST_DEBUG ("Segments : %d", self->segments->len);
   for (idx = 0; idx < self->segments->len; idx++) {
     GstM3U8MediaSegment *segment = g_ptr_array_index (self->segments, idx);
 
     GST_DEBUG ("  sequence:%" G_GINT64_FORMAT " discont_sequence:%"
         G_GINT64_FORMAT, segment->sequence, segment->discont_sequence);
+    GST_DEBUG ("    partial only: %s", segment->partial_only ? "YES" : "NO");
     GST_DEBUG ("    stream_time : %" GST_STIME_FORMAT,
         GST_STIME_ARGS (segment->stream_time));
     GST_DEBUG ("    duration    :  %" GST_TIME_FORMAT,
@@ -399,8 +513,55 @@ gst_hls_media_playlist_dump (GstHLSMediaPlaylist * self)
       GST_DEBUG ("    date/time    : %s", datestring);
       g_free (datestring);
     }
+    if (segment->init_file) {
+      GST_DEBUG ("    init file : %s %" G_GUINT64_FORMAT " %" G_GINT64_FORMAT,
+          segment->init_file->uri, segment->init_file->offset,
+          segment->init_file->size);
+    }
+
     GST_DEBUG ("    uri         : %s %" G_GUINT64_FORMAT " %" G_GINT64_FORMAT,
         segment->uri, segment->offset, segment->size);
+
+    GST_DEBUG ("    is gap      : %s", segment->is_gap ? "YES" : "NO");
+
+    if (segment->partial_segments != NULL) {
+      guint part_idx;
+      for (part_idx = 0; part_idx < segment->partial_segments->len; part_idx++) {
+        GstM3U8PartialSegment *part =
+            g_ptr_array_index (segment->partial_segments, part_idx);
+        GST_DEBUG ("    partial segment %u:", part_idx);
+        GST_DEBUG ("      uri         : %s %" G_GUINT64_FORMAT " %"
+            G_GINT64_FORMAT, part->uri, part->offset, part->size);
+        GST_DEBUG ("      stream_time : %" GST_STIME_FORMAT,
+            GST_STIME_ARGS (part->stream_time));
+        GST_DEBUG ("      duration    : %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (part->duration));
+        GST_DEBUG ("      is gap      : %s", part->is_gap ? "YES" : "NO");
+        GST_DEBUG ("      independent : %s", part->independent ? "YES" : "NO");
+      }
+    }
+  }
+
+  if (self->preload_hints) {
+    GST_DEBUG ("Preload Hints: %d", self->preload_hints->len);
+    for (idx = 0; idx < self->preload_hints->len; idx++) {
+      GstM3U8PreloadHint *hint = g_ptr_array_index (self->preload_hints, idx);
+      const gchar *hint_type_str;
+      switch (hint->hint_type) {
+        case M3U8_PRELOAD_HINT_MAP:
+          hint_type_str = "MAP";
+          break;
+        case M3U8_PRELOAD_HINT_PART:
+          hint_type_str = "PART";
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+
+      GST_DEBUG ("    preload hint %u: type %s", idx, hint_type_str);
+      GST_DEBUG ("      uri         : %s %" G_GUINT64_FORMAT " %"
+          G_GINT64_FORMAT, hint->uri, hint->offset, hint->size);
+    }
   }
 #endif
 }
@@ -468,15 +629,194 @@ gst_hls_media_playlist_postprocess_pdt (GstHLSMediaPlaylist * self)
   }
 }
 
+static GstM3U8PartialSegment *
+gst_m3u8_parse_partial_segment (gchar * data, const gchar * base_uri)
+{
+  gchar *v, *a;
+  gboolean have_duration = FALSE;
+  GstM3U8PartialSegment *part = g_new0 (GstM3U8PartialSegment, 1);
+
+  part->ref_count = 1;
+  part->stream_time = GST_CLOCK_STIME_NONE;
+  part->size = -1;
+
+  while (data != NULL && parse_attributes (&data, &a, &v)) {
+    if (strcmp (a, "URI") == 0) {
+      g_free (part->uri);
+      part->uri = uri_join (base_uri, v);
+    } else if (strcmp (a, "DURATION") == 0) {
+      if (!time_from_double_in_string (v, NULL, &part->duration)) {
+        GST_WARNING ("Can't read EXT-X-PART duration");
+        goto malformed_line;
+      }
+      have_duration = TRUE;
+    } else if (strcmp (a, "INDEPENDENT") == 0) {
+      part->independent = g_ascii_strcasecmp (v, "yes") == 0;
+    } else if (strcmp (a, "GAP") == 0) {
+      part->is_gap = g_ascii_strcasecmp (v, "yes") == 0;
+    } else if (strcmp (a, "BYTERANGE") == 0) {
+      if (int64_from_string (v, &v, &part->size)) {
+        goto malformed_line;
+      }
+      if (*v == '@' && !int64_from_string (v + 1, &v, &part->offset)) {
+        goto malformed_line;
+      }
+    }
+  }
+
+  if (part->uri == NULL || !have_duration) {
+    goto required_attributes_missing;
+  }
+
+  return part;
+
+required_attributes_missing:
+  {
+    GST_WARNING
+        ("EXT-X-PART description is missing required URI or DURATION attributes");
+    gst_m3u8_partial_segment_unref (part);
+    return NULL;
+  }
+malformed_line:
+  {
+    GST_WARNING ("Invalid EXT-X-PART entry in playlist");
+    gst_m3u8_partial_segment_unref (part);
+    return NULL;
+  }
+}
+
+static GstM3U8PreloadHint *
+gst_m3u8_parse_preload_hint (gchar * data, const gchar * base_uri)
+{
+  gchar *v, *a;
+  GstM3U8PreloadHint *hint = g_new0 (GstM3U8PreloadHint, 1);
+  gboolean have_hint_type = FALSE;
+
+  hint->ref_count = 1;
+  hint->size = -1;
+
+  while (data != NULL && parse_attributes (&data, &a, &v)) {
+    if (strcmp (a, "TYPE") == 0) {
+      if (g_ascii_strcasecmp (v, "MAP") == 0) {
+        hint->hint_type = M3U8_PRELOAD_HINT_MAP;
+      } else if (g_ascii_strcasecmp (v, "PART") == 0) {
+        hint->hint_type = M3U8_PRELOAD_HINT_PART;
+      } else {
+        GST_WARNING ("Unknown Preload Hint type %s", v);
+        goto malformed_line;
+      }
+      have_hint_type = TRUE;
+    } else if (strcmp (a, "URI") == 0) {
+      g_free (hint->uri);
+      hint->uri = uri_join (base_uri, v);
+    } else if (strcmp (a, "BYTERANGE-START") == 0) {
+      if (int64_from_string (v, NULL, &hint->offset)) {
+        goto malformed_line;
+      }
+    } else if (strcmp (a, "BYTERANGE-LENGTH") == 0) {
+      if (int64_from_string (v, NULL, &hint->size)) {
+        goto malformed_line;
+      }
+    }
+  }
+
+  if (hint->uri == NULL || !have_hint_type) {
+    goto required_attributes_missing;
+  }
+
+  return hint;
+
+required_attributes_missing:
+  {
+    GST_WARNING
+        ("EXT-X-PRELOAD-HINT is missing required URI or TYPE attributes");
+    gst_m3u8_preload_hint_unref (hint);
+    return NULL;
+  }
+malformed_line:
+  {
+    GST_WARNING ("Invalid EXT-X-PRELOAD-HINT entry in playlist");
+    gst_m3u8_preload_hint_unref (hint);
+    return NULL;
+  }
+}
+
+static void
+parse_server_control (GstHLSMediaPlaylist * self, gchar * data)
+{
+  gchar *v, *a;
+
+  while (data != NULL && parse_attributes (&data, &a, &v)) {
+    if (strcmp (a, "CAN-SKIP-UNTIL") == 0) {
+      if (!time_from_double_in_string (v, NULL, &self->skip_boundary)) {
+        GST_WARNING ("Can't read Skip Boundary value");
+        goto malformed_line;
+      }
+    } else if (strcmp (a, "CAN-SKIP-DATERANGES") == 0) {
+      self->can_skip_dateranges = g_ascii_strcasecmp (v, "YES") == 0;
+    } else if (strcmp (a, "HOLD-BACK") == 0) {
+      if (!time_from_double_in_string (v, NULL, &self->hold_back)) {
+        GST_WARNING ("Can't read Hold-Back value");
+        goto malformed_line;
+      }
+    } else if (strcmp (a, "PART-HOLD-BACK") == 0) {
+      if (!time_from_double_in_string (v, NULL, &self->part_hold_back)) {
+        GST_WARNING ("Can't read Part-Hold-Back value");
+        goto malformed_line;
+      }
+    } else if (strcmp (a, "CAN-BLOCK-RELOAD") == 0) {
+      self->can_block_reload = g_ascii_strcasecmp (v, "YES") == 0;
+    }
+  }
+
+  return;
+malformed_line:
+  {
+    GST_WARNING ("Invalid EXT-X-SERVER-CONTROL entry in playlist");
+    return;
+  }
+}
+
+/* Parse EXT-X-SKIP */
+static void
+parse_skip_tag (GstHLSMediaPlaylist * self, gchar * data)
+{
+  gchar *v, *a;
+
+  while (data != NULL && parse_attributes (&data, &a, &v)) {
+    if (strcmp (a, "SKIPPED-SEGMENTS") == 0) {
+      if (!int_from_string (v, NULL, &self->skipped_segments)
+          || self->skipped_segments < 0) {
+        GST_WARNING ("Can't read skipped segments from EXT-X-SKIP value");
+        self->skipped_segments = 0;
+        goto malformed_line;
+      }
+    } else if (strcmp (a, "RECENTLY-REMOVED-DATERANGES") == 0) {
+      gchar **removed_date_ranges = g_strsplit (v, "\t", -1);
+
+      g_strfreev (self->removed_date_ranges);
+      self->removed_date_ranges = removed_date_ranges;
+      self->num_removed_date_ranges = g_strv_length (removed_date_ranges);
+    }
+  }
+
+  return;
+malformed_line:
+  {
+    GST_WARNING ("Invalid EXT-X-SKIP entry in playlist");
+    return;
+  }
+}
+
 /* Parse and create a new GstHLSMediaPlaylist */
 GstHLSMediaPlaylist *
-gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
-    const gchar * base_uri)
+gst_hls_media_playlist_parse (gchar * data,
+    GstClockTime playlist_ts, const gchar * uri, const gchar * base_uri)
 {
   gchar *input_data = data;
   GstHLSMediaPlaylist *self;
   gint val;
-  GstClockTime duration;
+  GstClockTime duration, partial_duration;
   gchar *title, *end;
   gboolean discontinuity = FALSE;
   gchar *current_key = NULL;
@@ -488,8 +828,10 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
   GDateTime *date_time = NULL;
   GstM3U8InitFile *last_init_file = NULL;
   GstM3U8MediaSegment *previous = NULL;
+  GPtrArray *partial_segments = NULL;
   gboolean is_gap = FALSE;
 
+  GST_LOG ("playlist ts: %" GST_TIMEP_FORMAT, &playlist_ts);
   GST_LOG ("uri: %s", uri);
   GST_LOG ("base_uri: %s", base_uri);
   GST_TRACE ("data:\n%s", data);
@@ -507,11 +849,13 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
   }
 
   self = gst_hls_media_playlist_new (uri, base_uri);
+  self->playlist_ts = playlist_ts;
 
   /* Store a copy of the data */
   self->last_data = g_strdup (data);
 
   duration = 0;
+  partial_duration = 0;
   title = NULL;
   data += 7;
   while (TRUE) {
@@ -541,17 +885,22 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
         data = NULL;
         date_time = NULL;
         duration = 0;
+        partial_duration = 0;
         g_free (title);
         title = NULL;
         discontinuity = FALSE;
         size = offset = -1;
         is_gap = FALSE;
+        if (partial_segments != NULL) {
+          g_ptr_array_free (partial_segments, TRUE);
+          partial_segments = NULL;
+        }
         goto next_line;
       }
       if (data != NULL) {
         GstM3U8MediaSegment *file;
         /* We can finally create the segment */
-        /* The disconinuity sequence number is only stored if the header has
+        /* The discontinuity sequence number is only stored if the header has
          * EXT-X-DISCONTINUITY-SEQUENCE present.  */
         file =
             gst_m3u8_media_segment_new (data, title, duration, mediasequence++,
@@ -582,8 +931,12 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
         if (last_init_file)
           file->init_file = gst_m3u8_init_file_ref (last_init_file);
 
+        file->partial_segments = partial_segments;
+        partial_segments = NULL;
+
         date_time = NULL;       /* Ownership was passed to the segment */
         duration = 0;
+        partial_duration = 0;
         title = NULL;           /* Ownership was passed to the segment */
         discontinuity = FALSE;
         size = offset = -1;
@@ -592,16 +945,18 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
       }
 
     } else if (g_str_has_prefix (data, "#EXTINF:")) {
-      gdouble fval;
-      if (!double_from_string (data + 8, &data, &fval)) {
+      if (!time_from_double_in_string (data + 8, &data, &duration)) {
         GST_WARNING ("Can't read EXTINF duration");
         goto next_line;
       }
-      duration = fval * (gdouble) GST_SECOND;
-      if (self->targetduration > 0 && duration > self->targetduration) {
-        GST_DEBUG ("EXTINF duration (%" GST_TIME_FORMAT
-            ") > TARGETDURATION (%" GST_TIME_FORMAT ")",
-            GST_TIME_ARGS (duration), GST_TIME_ARGS (self->targetduration));
+
+      /* As of protocol version 6, targetduration is maximum segment duration
+       * rounded to nearest integer seconds,so can be up to 0.5 seconds too low */
+      if (self->targetduration > 0
+          && duration > (self->targetduration + GST_SECOND / 2)) {
+        GST_DEBUG ("EXTINF duration (%" GST_TIME_FORMAT ") > TARGETDURATION (%"
+            GST_TIME_FORMAT ")", GST_TIME_ARGS (duration),
+            GST_TIME_ARGS (self->targetduration));
       }
       if (!data || *data != ',')
         goto next_line;
@@ -750,6 +1105,59 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
         }
       } else if (g_str_has_prefix (data_ext_x, "GAP:")) {
         is_gap = TRUE;
+      } else if (g_str_has_prefix (data_ext_x, "PART:")) {
+        GstM3U8PartialSegment *part = NULL;
+
+        part =
+            gst_m3u8_parse_partial_segment (data + strlen ("#EXT-X-PART:"),
+            self->base_uri ? self->base_uri : self->uri);
+        if (part == NULL)
+          goto next_line;
+
+        if (partial_segments == NULL) {
+          partial_segments = g_ptr_array_new_full (2,
+              (GDestroyNotify) gst_m3u8_partial_segment_unref);
+        }
+        g_ptr_array_add (partial_segments, part);
+        partial_duration += part->duration;
+
+      } else if (g_str_has_prefix (data_ext_x, "PART-INF:")) {
+        gchar *v, *a;
+
+        data += strlen ("#EXT-X-PART-INF:");
+
+        while (data != NULL && parse_attributes (&data, &a, &v)) {
+          if (strcmp (a, "PART-TARGET") == 0) {
+            if (!time_from_double_in_string (v, NULL,
+                    &self->partial_targetduration)) {
+              GST_WARNING ("Invalid PART-TARGET");
+              goto next_line;
+            }
+          }
+        }
+      } else if (g_str_has_prefix (data_ext_x, "SERVER-CONTROL:")) {
+        data += strlen ("#EXT-X-SERVER-CONTROL:");
+        parse_server_control (self, data);
+      } else if (g_str_has_prefix (data_ext_x, "PRELOAD-HINT:")) {
+        GstM3U8PreloadHint *hint = NULL;
+
+        hint =
+            gst_m3u8_parse_preload_hint (data + strlen ("#EXT-X-PRELOAD-HINT:"),
+            self->base_uri ? self->base_uri : self->uri);
+        if (hint == NULL)
+          goto next_line;
+
+        if (self->preload_hints == NULL) {
+          self->preload_hints = g_ptr_array_new_full (1,
+              (GDestroyNotify) gst_m3u8_preload_hint_unref);
+        }
+        g_ptr_array_add (self->preload_hints, hint);
+      } else if (g_str_has_prefix (data_ext_x, "SKIP:")) {
+        data += strlen ("#EXT-X-SKIP:");
+        parse_skip_tag (self, data);
+        /* Increment the current MSN by the number
+         * of segments that were removed */
+        mediasequence += self->skipped_segments;
       } else {
         GST_LOG ("Ignored line: %s", data);
       }
@@ -764,6 +1172,59 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
     data = g_utf8_next_char (end);      /* skip \n */
   }
 
+  /* If there are trailing partial segments at the end,
+   * create a dummy segment to hold them */
+  if (partial_segments != NULL) {
+    GstM3U8MediaSegment *file;
+    GST_DEBUG ("Creating dummy segment for trailing partial segments");
+
+    /* The discontinuity sequence number is only stored if the header has
+     * EXT-X-DISCONTINUITY-SEQUENCE present.  */
+    file =
+        gst_m3u8_media_segment_new (NULL, title, partial_duration,
+        mediasequence++, dsn, size, offset);
+
+    file->partial_only = TRUE;
+
+    self->duration += partial_duration;
+
+    file->is_gap = is_gap;
+
+    /* set encryption params */
+    if (current_key != NULL) {
+      file->key = g_strdup (current_key);
+      if (have_iv) {
+        memcpy (file->iv, iv, sizeof (iv));
+      } else {
+        /* An EXT-X-KEY tag with a KEYFORMAT of "identity" that does
+         * not have an IV attribute indicates that the Media Sequence
+         * Number is to be used as the IV when decrypting a Media
+         * Segment, by putting its big-endian binary representation
+         * into a 16-octet (128-bit) buffer and padding (on the left)
+         * with zeros. */
+        guint8 *iv = file->iv + 12;
+        GST_WRITE_UINT32_BE (iv, file->sequence);
+      }
+    }
+
+    file->datetime = date_time;
+    file->discont = discontinuity;
+    if (last_init_file)
+      file->init_file = gst_m3u8_init_file_ref (last_init_file);
+
+    file->partial_segments = partial_segments;
+    partial_segments = NULL;
+
+    date_time = NULL;           /* Ownership was passed to the partial segment */
+    duration = 0;
+    partial_duration = 0;
+    title = NULL;               /* Ownership was passed to the partial segment */
+    discontinuity = FALSE;
+    size = offset = -1;
+    g_ptr_array_add (self->segments, file);
+    previous = file;
+  }
+
   /* Clean up date that wasn't freed / handed to a segment */
   g_free (current_key);
   current_key = NULL;
@@ -782,6 +1243,9 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
     return NULL;
   }
 
+  if (!self->has_ext_x_dsn)
+    self->discont_sequence = 0;
+
   /* Now go over the parsed data to ensure MSN and/or PDT are set */
   if (self->ext_x_pdt_present)
     gst_hls_media_playlist_postprocess_pdt (self);
@@ -794,6 +1258,8 @@ gst_hls_media_playlist_parse (gchar * data, const gchar * uri,
     for (iter = 0; iter < len; iter++) {
       GstM3U8MediaSegment *segment = g_ptr_array_index (self->segments, iter);
       segment->stream_time = stream_time;
+      gst_m3u8_media_segment_fill_partial_stream_times (segment);
+
       stream_time += segment->duration;
     }
   }
@@ -818,23 +1284,124 @@ gst_hls_media_playlist_has_same_data (GstHLSMediaPlaylist * self,
   return ret;
 }
 
-GstM3U8MediaSegment *
+/* gst_hls_media_playlist_seek() is used when performing
+ * an actual seek. It finds a suitable segment (or partial segment
+ * for LL-HLS) at which to resume playback. Only partial segments
+ * in the last 2 target durations of the live edge are considered
+ * when playing live, otherwise we might start playing a partial
+ * segment group that disappears before we're done with it.
+ * We want a segment or partial that contains a keyframe if possible
+ */
+gboolean
 gst_hls_media_playlist_seek (GstHLSMediaPlaylist * playlist, gboolean forward,
-    GstSeekFlags flags, GstClockTimeDiff ts)
+    GstSeekFlags flags, GstClockTimeDiff ts, GstM3U8SeekResult * seek_result)
 {
   gboolean snap_nearest =
       (flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST;
   gboolean snap_after =
       (flags & GST_SEEK_FLAG_SNAP_AFTER) == GST_SEEK_FLAG_SNAP_AFTER;
+  gboolean want_keyunit = (flags & GST_SEEK_FLAG_KEY_UNIT);
   guint idx;
   GstM3U8MediaSegment *res = NULL;
+  guint res_part_idx = 0;
+  GstClockTime partial_window_start = GST_CLOCK_TIME_NONE;
 
-  GST_DEBUG ("ts:%" GST_STIME_FORMAT " forward:%d playlist uri: %s",
+  GST_DEBUG ("target ts:%" GST_STIME_FORMAT " forward:%d playlist uri: %s",
       GST_STIME_ARGS (ts), forward, playlist->uri);
 
+  /* Can't seek if there's no segments */
+  if (playlist->segments->len < 1)
+    return FALSE;
+
+  /* Calculate the threshold at which we might start inspecting partial segments */
+  if (flags & GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL) {
+    GstM3U8MediaSegment *last_seg =
+        g_ptr_array_index (playlist->segments, playlist->segments->len - 1);
+    GstClockTime playlist_end = last_seg->stream_time + last_seg->duration;
+
+    if (playlist_end >= 2 * playlist->targetduration)
+      partial_window_start = playlist_end - 2 * playlist->targetduration;
+    else
+      partial_window_start = last_seg->stream_time;
+
+    GST_DEBUG ("Partial segment threshold %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (partial_window_start));
+  }
+
   for (idx = 0; idx < playlist->segments->len; idx++) {
     GstM3U8MediaSegment *cand = g_ptr_array_index (playlist->segments, idx);
 
+    if (flags & GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL &&
+        GST_CLOCK_TIME_IS_VALID (partial_window_start) &&
+        cand->stream_time + cand->duration > partial_window_start) {
+      /* Permitted to land at a partial segment, but only do so if
+       * they are in the last 2 target durations of the playlist, so we can
+       * be fairly sure we'll have to time download them all before
+       * they get removed.
+       *
+       * 6.2.2: EXT-X-PART tags SHOULD be removed from the Playlist after they are
+       * greater than three Target Durations from the end of the Playlist.
+       * Clients MUST be able to download the Partial Segment for at least
+       * three Target Durations after the EXT-X-PART tag is removed from the
+       * Playlist.
+       */
+      if (cand->partial_segments != NULL) {
+        guint part_idx;
+        guint last_independent_idx = 0;
+
+        for (part_idx = 0; part_idx < cand->partial_segments->len; part_idx++) {
+          GstM3U8PartialSegment *part =
+              g_ptr_array_index (cand->partial_segments, part_idx);
+
+          GST_LOG ("Inspecting partial segment sn:%" G_GINT64_FORMAT
+              " idx %u stream_time:%" GST_STIME_FORMAT " duration:%"
+              GST_TIME_FORMAT, cand->sequence, part_idx,
+              GST_STIME_ARGS (part->stream_time),
+              GST_TIME_ARGS (part->duration));
+
+          if ((forward & snap_after) || snap_nearest) {
+            if (!want_keyunit || part->independent) {
+              if (part->stream_time >= ts ||
+                  (snap_nearest
+                      && (ts - part->stream_time < part->duration / 2))) {
+                res = cand;
+                res_part_idx = part_idx;
+                goto partial_seg_out;
+              }
+            }
+          } else if (!forward && snap_after) {
+            GstClockTime next_pos = cand->stream_time + cand->duration;
+
+            if (!want_keyunit || part->independent) {
+              if (next_pos <= ts && ts < next_pos + cand->duration) {
+                res = cand;
+                res_part_idx = part_idx;
+                goto partial_seg_out;
+              }
+            }
+          } else if (part->stream_time <= ts
+              && ts < (GstClockTimeDiff) (part->stream_time + part->duration)) {
+            res = cand;
+            if (!want_keyunit || part->independent)
+              res_part_idx = part_idx;
+            else
+              res_part_idx = last_independent_idx;
+            goto partial_seg_out;
+          }
+
+          if (part->independent)
+            last_independent_idx = part_idx;
+        }
+      }
+    } else if (cand->partial_only) {
+      /* If only full segments were requested or we're still outside the partial segment
+       * window, skip the last segment if it only has EXT-X-PARTs attached */
+      continue;
+    }
+
+    /* For full segment alignment, we ignore the KEY_UNIT flag and assume
+     * all segments have a keyframe, since HLS doesn't give us reliable info
+     * about that */
     if ((forward & snap_after) || snap_nearest) {
       if (cand->stream_time >= ts ||
           (snap_nearest && (ts - cand->stream_time < cand->duration / 2))) {
@@ -849,7 +1416,7 @@ gst_hls_media_playlist_seek (GstHLSMediaPlaylist * playlist, gboolean forward,
         goto out;
       }
     } else if ((cand->stream_time <= ts || idx == 0)
-        && ts < cand->stream_time + cand->duration) {
+        && ts < (GstClockTimeDiff) (cand->stream_time + cand->duration)) {
       res = cand;
       goto out;
     }
@@ -860,12 +1427,229 @@ out:
     GST_DEBUG ("Returning segment sn:%" G_GINT64_FORMAT " stream_time:%"
         GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, res->sequence,
         GST_STIME_ARGS (res->stream_time), GST_TIME_ARGS (res->duration));
-    gst_m3u8_media_segment_ref (res);
-  } else {
-    GST_DEBUG ("Couldn't find a match");
+
+    seek_result->stream_time = res->stream_time;
+    seek_result->segment = gst_m3u8_media_segment_ref (res);
+    seek_result->found_partial_segment = res->partial_only;
+    seek_result->part_idx = 0;
+    return TRUE;
   }
 
-  return res;
+  GST_DEBUG ("Couldn't find a match");
+  return FALSE;
+
+partial_seg_out:
+  if (res && res->partial_segments != NULL
+      && res_part_idx < res->partial_segments->len) {
+    GstM3U8PartialSegment *part =
+        g_ptr_array_index (res->partial_segments, res_part_idx);
+
+    GST_DEBUG ("Returning partial segment sn:%" G_GINT64_FORMAT
+        " part_idx %u stream_time:%" GST_STIME_FORMAT " duration:%"
+        GST_TIME_FORMAT, res->sequence, res_part_idx,
+        GST_STIME_ARGS (part->stream_time), GST_TIME_ARGS (part->duration));
+
+    seek_result->stream_time = part->stream_time;
+    seek_result->segment = gst_m3u8_media_segment_ref (res);
+    seek_result->found_partial_segment = TRUE;
+    seek_result->part_idx = res_part_idx;
+    return TRUE;
+  }
+
+  GST_DEBUG ("Couldn't find a match");
+  return FALSE;
+}
+
+static gboolean
+gst_hls_media_playlist_find_partial_position (GstHLSMediaPlaylist * playlist,
+    GstM3U8MediaSegment * seg, GstClockTimeDiff ts,
+    GstM3U8SeekResult * seek_result)
+{
+  guint i;
+
+  /* As with full segment search below, we more often want to find our position
+   * near the end of a live playlist, so iterate segments backward */
+  for (i = seg->partial_segments->len; i > 0; i--) {
+    guint part_idx = i - 1;
+    GstM3U8PartialSegment *cand =
+        g_ptr_array_index (seg->partial_segments, part_idx);
+
+    GST_DEBUG ("partial segment %d ts:%" GST_STIME_FORMAT " end:%"
+        GST_STIME_FORMAT, part_idx, GST_STIME_ARGS (cand->stream_time),
+        GST_STIME_ARGS ((GstClockTimeDiff) (cand->stream_time +
+                cand->duration)));
+
+    /* If the target timestamp is before this partial segment, or in the first half, this
+     * is the partial segment to land in */
+    if ((GstClockTimeDiff) (cand->stream_time + (cand->duration / 2)) >= ts &&
+        cand->stream_time <= (GstClockTimeDiff) (ts + (cand->duration / 2))) {
+      GST_DEBUG ("choosing partial segment %d", part_idx);
+      seek_result->segment = gst_m3u8_media_segment_ref (seg);
+      seek_result->found_partial_segment = TRUE;
+      seek_result->part_idx = part_idx;
+      seek_result->stream_time = cand->stream_time;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* gst_hls_media_playlist_find_position() is used when finding the segment or
+ * partial segment that corresponds to our current playback position.
+ *
+ * If we're "playing partial segments", we want to find the partial segment
+ * whose stream_time matches the target position most closely (or fail if
+ * there's no partial segment, since the target partial segment was removed from
+ * the playlist and we lost sync.
+ *
+ * If not currently playing partial segment, find the segment with a stream_time
+ * that matches, or the partial segment exactly at the start of the
+ * 'partial_only' segment.
+ */
+gboolean
+gst_hls_media_playlist_find_position (GstHLSMediaPlaylist * playlist,
+    GstClockTimeDiff ts, gboolean in_partial_segments,
+    GstM3U8SeekResult * seek_result)
+{
+  guint i;
+  GstM3U8MediaSegment *seg = NULL, *following = NULL;
+
+  GST_DEBUG ("ts:%" GST_STIME_FORMAT
+      " in_partial_segments %d (live %d) playlist uri: %s", GST_STIME_ARGS (ts),
+      in_partial_segments, GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist),
+      playlist->uri);
+
+  /* The *common* case is that we want to find our position in a live playback
+   * scenario, when we're playing close to the live edge, so start at the end
+   * of the segments and go backward */
+  for (i = playlist->segments->len; i != 0; i--) {
+    guint seg_idx = i - 1;
+    GstM3U8MediaSegment *cand = g_ptr_array_index (playlist->segments, seg_idx);
+
+    GST_DEBUG ("segment %d ts:%" GST_STIME_FORMAT " end:%" GST_STIME_FORMAT
+        " partial only: %d",
+        seg_idx, GST_STIME_ARGS (cand->stream_time),
+        GST_STIME_ARGS ((GstClockTimeDiff) (cand->stream_time +
+                cand->duration)), cand->partial_only);
+
+    /* Ignore any (disallowed by the spec) partial_only segment if
+     * the playlist is no longer live */
+    if (cand->partial_only && !GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist))
+      continue;
+
+    /* If the target stream time is definitely past the end
+     * of this segment, no earlier segment (with lower stream time)
+     * could match, so we fail */
+    if (ts >= (GstClockTimeDiff) (cand->stream_time + (3 * cand->duration / 2))) {
+      break;
+    }
+
+    if (in_partial_segments || cand->partial_only) {
+      if (cand->partial_segments == NULL) {
+        GstClockTime partial_targetduration = playlist->partial_targetduration;
+
+        /* Default, if the playlist fails to give us a part duration (REQUIRED attribute, but
+         * maybe it got removed) */
+        if (!GST_CLOCK_TIME_IS_VALID (partial_targetduration)) {
+          partial_targetduration = 200 * GST_MSECOND;
+        }
+
+        /* If we want to match a partial segment but this segment doesn't have
+         * any, then the partial segment we want got removed from the playlist,
+         * so we need to fail, except in the specific case that our target
+         * timestamp is within half a part duration of the segment start
+         * itself (ie, we wanted the *first* partial segment
+         */
+        if (cand->stream_time + (partial_targetduration / 2) >= ts &&
+            cand->stream_time <= ts + (partial_targetduration / 2)) {
+          GST_DEBUG ("choosing full segment %d", seg_idx);
+          seek_result->stream_time = seg->stream_time;
+          seek_result->segment = gst_m3u8_media_segment_ref (seg);
+          seek_result->found_partial_segment = FALSE;
+          return TRUE;
+        }
+
+        GST_DEBUG ("Couldn't find a matching partial segment");
+        return FALSE;
+      }
+
+      /* If our partial segment target ts is within half a partial duration
+       * of this segment start/finish, check the partial segments for a match */
+      if (gst_hls_media_playlist_find_partial_position (playlist, cand, ts,
+              seek_result)) {
+        GST_DEBUG ("Returning partial segment sn:%" G_GINT64_FORMAT
+            " part %u stream_time:%" GST_STIME_FORMAT, cand->sequence,
+            seek_result->part_idx, GST_STIME_ARGS (seek_result->stream_time));
+        return TRUE;
+      }
+    }
+
+    /* Otherwise, we're doing a full segment match so check that the timestamp
+     * is within half a segment duration of this segment stream_time.
+     *
+     * If the final segment has partial fragments, the target duration is used
+     * instead, because the partial-only last segment might be quite small (as
+     * it's still being created), which can cause a missed match otherwise */
+    GstClockTimeDiff match_threshold =
+        (cand->partial_only ? playlist->targetduration : cand->duration) / 2;
+
+    /* If the requesting position is beyond the halfway point of this segment,
+     * we should return the following segment, which is closer to the requested
+     * position. Note that it can be NULL */
+    if (ts > cand->stream_time + match_threshold) {
+      if (following)
+        GST_DEBUG ("choosing following segment %d", seg_idx + 1);
+      else
+        GST_DEBUG ("After last segment");
+      seg = following;
+      break;
+    }
+
+    /* Else if the requested position is within the first half we are sure it's
+     * this segment */
+    if (ts >= cand->stream_time) {
+      GST_DEBUG ("choosing segment %d", seg_idx);
+      seg = cand;
+      break;
+    }
+
+    /* Last check for the very first segment in the playlist */
+    if (i == 1 && cand->stream_time <= ts + match_threshold) {
+      GST_DEBUG ("choosing first segment");
+      seg = cand;
+      break;
+    }
+
+    /* We are scanning backwards, remember this as the following segment */
+    following = cand;
+  }
+
+  if (seg == NULL) {
+    GST_DEBUG ("Couldn't find a matching segment");
+    return FALSE;
+  }
+
+  /* The partial_only segment case should have been handled above
+   * by gst_hls_media_playlist_find_partial_position(). If it
+   * wasn't, it implies the segment we're looking for was not
+   * present in the available partial segments at all,
+   * so we need to return FALSE */
+  if (seg->partial_only) {
+    GST_DEBUG
+        ("Couldn't find a matching partial segment in the partial_only segment");
+    return FALSE;
+  }
+
+  seek_result->stream_time = seg->stream_time;
+  seek_result->segment = gst_m3u8_media_segment_ref (seg);
+  seek_result->found_partial_segment = FALSE;
+
+  GST_DEBUG ("Returning segment sn:%" G_GINT64_FORMAT " stream_time:%"
+      GST_STIME_FORMAT " duration:%" GST_TIME_FORMAT, seg->sequence,
+      GST_STIME_ARGS (seg->stream_time), GST_TIME_ARGS (seg->duration));
+
+  return TRUE;
 }
 
 /* Recalculate all segment DSN based on the DSN of the provided anchor segment
@@ -911,6 +1695,24 @@ gst_hls_media_playlist_recalculate_dsn (GstHLSMediaPlaylist * playlist,
 }
 
 
+static void
+gst_m3u8_media_segment_fill_partial_stream_times (GstM3U8MediaSegment * segment)
+{
+  guint idx;
+  GstClockTimeDiff stream_time = segment->stream_time;
+
+  if (segment->partial_segments == NULL)
+    return;
+
+  for (idx = 0; idx < segment->partial_segments->len; idx++) {
+    GstM3U8PartialSegment *part =
+        g_ptr_array_index (segment->partial_segments, idx);
+
+    part->stream_time = stream_time;
+    stream_time += part->duration;
+  }
+}
+
 /* Recalculate all segment stream time based on the stream time of the provided
  * anchor segment (which must belong to the playlist) */
 void
@@ -930,6 +1732,7 @@ gst_hls_media_playlist_recalculate_stream_time (GstHLSMediaPlaylist * playlist,
 
   GST_DEBUG ("Re-calculating stream times from segment #%d %" GST_TIME_FORMAT,
       idx, GST_TIME_ARGS (anchor->stream_time));
+  gst_m3u8_media_segment_fill_partial_stream_times (anchor);
 
   /* Forward */
   prev = anchor;
@@ -938,6 +1741,7 @@ gst_hls_media_playlist_recalculate_stream_time (GstHLSMediaPlaylist * playlist,
     cand->stream_time = prev->stream_time + prev->duration;
     GST_DEBUG ("Forward iter %d %" GST_STIME_FORMAT, iter,
         GST_STIME_ARGS (cand->stream_time));
+    gst_m3u8_media_segment_fill_partial_stream_times (cand);
     prev = cand;
   }
 
@@ -948,8 +1752,38 @@ gst_hls_media_playlist_recalculate_stream_time (GstHLSMediaPlaylist * playlist,
     cand->stream_time = prev->stream_time - cand->duration;
     GST_DEBUG ("Backward iter %d %" GST_STIME_FORMAT, iter,
         GST_STIME_ARGS (cand->stream_time));
+    gst_m3u8_media_segment_fill_partial_stream_times (cand);
+    prev = cand;
+  }
+}
+
+void
+gst_hls_media_playlist_recalculate_stream_time_from_part (GstHLSMediaPlaylist *
+    playlist, GstM3U8MediaSegment * anchor, guint part_idx)
+{
+  g_assert (anchor->partial_segments != NULL
+      && part_idx < anchor->partial_segments->len);
+
+  GstClockTimeDiff last_stream_time;
+  GstM3U8PartialSegment *part =
+      g_ptr_array_index (anchor->partial_segments, part_idx);
+  GstM3U8PartialSegment *cand, *prev;
+  gint iter;
+
+  /* Work backward from the target partial segment, assigning stream times until
+   * we update the segment time itself, then recalculate all stream times */
+  prev = part;
+  last_stream_time = part->stream_time;
+  for (iter = part_idx - 1; iter >= 0; iter--) {
+    cand = g_ptr_array_index (anchor->partial_segments, iter);
+    last_stream_time = cand->stream_time = prev->stream_time - cand->duration;
+    GST_DEBUG ("Backward partial segment iter %d %" GST_STIME_FORMAT, iter,
+        GST_STIME_ARGS (cand->stream_time));
     prev = cand;
   }
+  anchor->stream_time = last_stream_time;
+
+  gst_hls_media_playlist_recalculate_stream_time (playlist, anchor);
 }
 
 /* If a segment with the same URI, size, offset, SN and DSN is present in the
@@ -1043,11 +1877,24 @@ find_segment_in_playlist (GstHLSMediaPlaylist * playlist,
         }
       }
 
-      if (cand->datetime
-          && g_date_time_difference (cand->datetime, segment->datetime) >= 0) {
-        GST_DEBUG ("Picking by date time");
-        *matched_pdt = TRUE;
-        return cand;
+      /* The reported PDT might not be 100% identical for matching segments
+       * across playlists, we therefore need to take into account a certain
+       * tolerance otherwise we would fail to match candidates with a PDT which
+       * is slightly before. We therefore check whether the segment starts
+       * within the first third of the candidate segment.
+       */
+      if (cand->datetime) {
+        GstClockTimeDiff pdtdiff = g_date_time_difference (cand->datetime,
+            segment->datetime) * GST_USECOND + cand->duration / 3;
+        if (pdtdiff >= 0) {
+#ifndef GST_DISABLE_GST_DEBUG
+          gchar *pdtstring = g_date_time_format_iso8601 (cand->datetime);
+          GST_DEBUG ("Picking segment with datetime %s", pdtstring);
+          g_free (pdtstring);
+#endif
+          *matched_pdt = TRUE;
+          return cand;
+        }
       }
     }
   }
@@ -1102,6 +1949,64 @@ find_segment_in_playlist (GstHLSMediaPlaylist * playlist,
   return NULL;
 }
 
+/* Match up the first segment in a delta playlist against the reference and transfer
+ * over preceding segments if we can */
+gboolean
+gst_hls_media_playlist_sync_skipped_segments (GstHLSMediaPlaylist * m3u8,
+    GstHLSMediaPlaylist * reference)
+{
+  /* Trivially there might be nothing to do (not a delta playlist) */
+  if (m3u8->skipped_segments < 1 || m3u8->segments->len < 1)
+    return TRUE;
+
+  /* find the first non-skipped segment from this playlist
+   * in the reference playlist, then transfer over as many
+   * skipped segments as we can */
+  GstM3U8MediaSegment *first = g_ptr_array_index (m3u8->segments, 0);
+
+  guint ref_idx;
+  gboolean found_ref_seg = FALSE;
+  for (ref_idx = 0; ref_idx < reference->segments->len; ref_idx++) {
+    GstM3U8MediaSegment *cand =
+        g_ptr_array_index (reference->segments, ref_idx);
+
+    if (cand->sequence == first->sequence &&
+        cand->discont_sequence == first->discont_sequence &&
+        cand->offset == first->offset && cand->size == first->size &&
+        !g_strcmp0 (cand->uri, first->uri)) {
+      found_ref_seg = TRUE;
+      break;
+    }
+  }
+
+  if (!found_ref_seg)
+    return FALSE;               /* Couldn't match the segment */
+
+  /* Found the first segment of this playlist in the reference. Transfer over
+   * as many skipped segments as we can */
+  guint segs_avail = MIN (ref_idx, m3u8->skipped_segments);
+  if (segs_avail < 1)
+    return FALSE;
+
+  GST_DEBUG
+      ("Transferring %u skipped segments from reference playlist starting at index %u",
+      segs_avail, ref_idx - segs_avail);
+
+  /* Reduce the skipped_segments count by the number we will transfer */
+  m3u8->skipped_segments -= segs_avail;
+
+  /* And copy over the segments */
+  guint cur_idx;
+  for (cur_idx = ref_idx - 1; segs_avail > 0; segs_avail--, cur_idx--) {
+    GstM3U8MediaSegment *segment =
+        g_ptr_array_index (reference->segments, cur_idx);
+    g_ptr_array_insert (m3u8->segments, 0,
+        gst_m3u8_media_segment_ref (segment));
+  }
+
+  return TRUE;
+}
+
 /* Given a media segment (potentially from another media playlist), find the
  * equivalent media segment in this playlist.
  *
@@ -1158,6 +2063,7 @@ gst_hls_media_playlist_sync_to_segment (GstHLSMediaPlaylist * playlist,
             &segment->stream_time, &stream_time_offset);
       }
       res->stream_time = segment->stream_time + stream_time_offset;
+      gst_m3u8_media_segment_fill_partial_stream_times (res);
     }
     if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist))
       gst_hls_media_playlist_recalculate_stream_time (playlist, res);
@@ -1192,10 +2098,11 @@ gst_hls_media_playlist_sync_to_segment (GstHLSMediaPlaylist * playlist,
   return res;
 }
 
-GstM3U8MediaSegment *
-gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self)
+gboolean
+gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self,
+    GstM3U8SeekResult * seek_result)
 {
-  GstM3U8MediaSegment *res;
+  GstM3U8MediaSegment *res = NULL;
 
   GST_DEBUG ("playlist %s", self->uri);
 
@@ -1203,20 +2110,91 @@ gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self)
     /* For non-live, we just grab the first one */
     res = g_ptr_array_index (self->segments, 0);
   } else {
-    /* Live playlist */
-    res =
-        g_ptr_array_index (self->segments,
-        MAX ((gint) self->segments->len - GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE -
-            1, 0));
+    GstClockTime hold_back = GST_CLOCK_TIME_NONE;
+    GstM3U8MediaSegment *last_seg;
+    g_assert (self->segments->len);
+    last_seg = g_ptr_array_index (self->segments, self->segments->len - 1);
+
+    /* Live playlist. If low-latency, use the PART-HOLD-BACK specified distance
+     * from the end, otherwise HOLD-BACK distance */
+    if (GST_CLOCK_TIME_IS_VALID (self->part_hold_back))
+      hold_back = self->part_hold_back;
+    else if (GST_CLOCK_TIME_IS_VALID (self->partial_targetduration))
+      hold_back = 3 * self->partial_targetduration;
+    else if (GST_CLOCK_TIME_IS_VALID (self->hold_back))
+      hold_back = self->hold_back;
+
+    if (hold_back == GST_CLOCK_TIME_NONE) {
+      /* If low-latency is not enabled, or none of the above were present,
+       * fallback to the standard behaviour:
+       *
+       * RFC 8216 6.3.3. Playing the Media Playlist File:
+       *
+       *   The client SHALL choose which Media Segment to play first from the
+       *   Media Playlist when playback starts.  If the EXT-X-ENDLIST tag is not
+       *   present and the client intends to play the media normally, the client
+       *   SHOULD NOT choose a segment that starts less than three target
+       *   durations from the end of the Playlist file.  Doing so can trigger
+       *   playback stalls.
+       */
+      hold_back = GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE * self->targetduration;
+    }
+
+    if (GST_CLOCK_TIME_IS_VALID (hold_back)
+        && GST_CLOCK_STIME_IS_VALID (last_seg->stream_time)) {
+      GstSeekFlags flags =
+          GST_SEEK_FLAG_SNAP_BEFORE | GST_SEEK_FLAG_KEY_UNIT |
+          GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL;
+      GstClockTime playlist_duration =
+          last_seg->stream_time + last_seg->duration;
+      GstClockTime target_ts;
+
+      /* Clamp the hold back so we don't go below zero */
+      if (hold_back > playlist_duration)
+        hold_back = playlist_duration;
+
+      target_ts = playlist_duration - hold_back;
+
+      GST_DEBUG ("Hold back is %" GST_TIME_FORMAT
+          " Looking for a segment before %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (hold_back), GST_TIME_ARGS (target_ts));
+
+      if (gst_hls_media_playlist_seek (self, TRUE, flags, target_ts,
+              seek_result)) {
+#ifndef GST_DISABLE_GST_DEBUG
+        GstClockTime distance_from_edge =
+            playlist_duration - seek_result->stream_time;
+
+        GST_DEBUG ("Found starting position %" GST_TIME_FORMAT " which is %"
+            GST_TIME_FORMAT " from the live edge",
+            GST_TIME_ARGS (seek_result->stream_time),
+            GST_TIME_ARGS (distance_from_edge));
+#endif
+        return TRUE;
+      }
+    }
+
+    /* Worst case fallback, start 3 fragments from the end */
+    if (res == NULL) {
+      res =
+          g_ptr_array_index (self->segments,
+          MAX ((gint) self->segments->len -
+              GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE - 1, 0));
+    }
   }
 
   if (res) {
     GST_DEBUG ("Using segment sn:%" G_GINT64_FORMAT " dsn:%" G_GINT64_FORMAT,
         res->sequence, res->discont_sequence);
-    gst_m3u8_media_segment_ref (res);
+
+    seek_result->stream_time = res->stream_time;
+    seek_result->segment = gst_m3u8_media_segment_ref (res);
+    seek_result->found_partial_segment = FALSE;
+    seek_result->part_idx = 0;
+    return TRUE;
   }
 
-  return res;
+  return FALSE;
 }
 
 /* Calls this to carry over stream time, DSN, ... from one playlist to another.
@@ -1224,10 +2202,15 @@ gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist * self)
  * This should be used when a reference media segment couldn't be matched in the
  * playlist, but we still want to carry over the information from a reference
  * playlist to an updated one. This can happen with live playlists where the
- * reference media segment is no longer present but the playlists intersect */
+ * reference media segment is no longer present but the playlists intersect
+ *
+ * If the sync is sucessfull, discont will be set to TRUE if it was a perfect
+ * URI fragment match, else it will be FALSE (ex: match was done on PDT or
+ * SN/DSN).
+ **/
 gboolean
 gst_hls_media_playlist_sync_to_playlist (GstHLSMediaPlaylist * playlist,
-    GstHLSMediaPlaylist * reference)
+    GstHLSMediaPlaylist * reference, gboolean * discont)
 {
   GstM3U8MediaSegment *res = NULL;
   GstM3U8MediaSegment *cand = NULL;
@@ -1235,14 +2218,18 @@ gst_hls_media_playlist_sync_to_playlist (GstHLSMediaPlaylist * playlist,
   gboolean is_before;
   gboolean matched_pdt = FALSE;
 
+  if (discont)
+    *discont = FALSE;
+
   g_return_val_if_fail (playlist && reference, FALSE);
 
 retry_without_dsn:
   /* The new playlist is supposed to be an update of the reference playlist,
-   * therefore we will try from the last segment of the reference playlist and
-   * go backwards */
-  for (idx = reference->segments->len - 1; idx; idx--) {
-    cand = g_ptr_array_index (reference->segments, idx);
+   * or a more recently fetched playlist from another rendition. In either case,
+   * it's best to start from the last segment of the (older) reference playlist and
+   * go backwards to find an overlap */
+  for (idx = reference->segments->len; idx; idx--) {
+    cand = g_ptr_array_index (reference->segments, idx - 1);
     res = find_segment_in_playlist (playlist, cand, &is_before, &matched_pdt);
     if (res)
       break;
@@ -1263,6 +2250,13 @@ retry_without_dsn:
     return FALSE;
   }
 
+  if (discont) {
+    /* If not a perfect match, mark as such */
+    GST_DEBUG ("Checking match uri cand: %s", cand->uri);
+    GST_DEBUG ("Checking match uri res : %s", res->uri);
+    *discont = g_strcmp0 (res->uri, cand->uri) != 0;
+  }
+
   /* Carry over reference stream time */
   if (res->stream_time == GST_CLOCK_STIME_NONE) {
     GstClockTimeDiff stream_time_offset = 0;
@@ -1282,6 +2276,7 @@ retry_without_dsn:
 
     }
     res->stream_time = cand->stream_time + stream_time_offset;
+    gst_m3u8_media_segment_fill_partial_stream_times (res);
   }
 
   if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist))
@@ -1375,6 +2370,18 @@ out:
   return file;
 }
 
+GstClockTime
+gst_hls_media_playlist_get_end_stream_time (GstHLSMediaPlaylist * m3u8)
+{
+  if (m3u8->segments->len == 0)
+    return GST_CLOCK_TIME_NONE;
+
+  GstM3U8MediaSegment *last =
+      g_ptr_array_index (m3u8->segments, m3u8->segments->len - 1);
+
+  return last->stream_time + last->duration;
+}
+
 GstClockTime
 gst_hls_media_playlist_get_duration (GstHLSMediaPlaylist * m3u8)
 {
@@ -1406,6 +2413,32 @@ gst_hls_media_playlist_get_duration (GstHLSMediaPlaylist * m3u8)
   return duration;
 }
 
+void
+gst_hls_media_playlist_get_next_msn_and_part (GstHLSMediaPlaylist * m3u8,
+    gint64 * next_msn, gint64 * next_part)
+{
+  /* Return the MSN and part number that are 1 past the end of the current playlist */
+  if (m3u8->segments->len == 0) {
+    *next_msn = -1;
+    *next_part = -1;
+    return;
+  }
+
+  GstM3U8MediaSegment *last =
+      g_ptr_array_index (m3u8->segments, m3u8->segments->len - 1);
+
+  /* If low_latency mode and the last segment contains partial segments, the next playlist update is
+   * when one extra partial segment gets added */
+  if (last->partial_segments != NULL) {
+    *next_msn = last->sequence;
+    *next_part = last->partial_segments->len;
+    return;
+  }
+
+  *next_msn = last->sequence + 1;
+  *next_part = -1;
+}
+
 gchar *
 gst_hls_media_playlist_get_uri (GstHLSMediaPlaylist * m3u8)
 {
@@ -1432,7 +2465,7 @@ gst_hls_media_playlist_is_live (GstHLSMediaPlaylist * m3u8)
   return is_live;
 }
 
-gchar *
+static gchar *
 uri_join (const gchar * uri1, const gchar * uri2)
 {
   gchar *uri_copy, *tmp, *ret = NULL;
@@ -1499,8 +2532,8 @@ gst_hls_media_playlist_has_lost_sync (GstHLSMediaPlaylist * m3u8,
   first = g_ptr_array_index (m3u8->segments, 0);
 
   GST_DEBUG ("position %" GST_TIME_FORMAT " first %" GST_STIME_FORMAT
-      " duration %" GST_STIME_FORMAT, GST_TIME_ARGS (position),
-      GST_STIME_ARGS (first->stream_time), GST_STIME_ARGS (first->duration));
+      " duration %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
+      GST_STIME_ARGS (first->stream_time), GST_TIME_ARGS (first->duration));
 
   if (first->stream_time <= 0)
     return FALSE;
@@ -1516,7 +2549,6 @@ gst_hls_media_playlist_get_seek_range (GstHLSMediaPlaylist * m3u8,
     gint64 * start, gint64 * stop)
 {
   GstM3U8MediaSegment *first, *last;
-  guint min_distance = 1;
 
   g_return_val_if_fail (m3u8 != NULL, FALSE);
 
@@ -1526,17 +2558,30 @@ gst_hls_media_playlist_get_seek_range (GstHLSMediaPlaylist * m3u8,
   first = g_ptr_array_index (m3u8->segments, 0);
   *start = first->stream_time;
 
-  if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (m3u8) && m3u8->segments->len > 1) {
-    /* min_distance is used to make sure the seek range is never closer than
-       GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE fragments from the end of a live
-       playlist - see 6.3.3. "Playing the Playlist file" of the HLS draft */
-    min_distance =
-        MIN (GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE, m3u8->segments->len - 1);
-  }
-
-  last = g_ptr_array_index (m3u8->segments, m3u8->segments->len - min_distance);
+  /* Default is the end of the playlist */
+  last = g_ptr_array_index (m3u8->segments, m3u8->segments->len - 1);
   *stop = last->stream_time + last->duration;
 
+  /* For live playlists, take the minimum hold back into account
+   * for the end of the seek range */
+  if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (m3u8)) {
+    GstM3U8SeekResult seek_result;
+
+    if (gst_hls_media_playlist_get_starting_segment (m3u8, &seek_result)) {
+      if (seek_result.found_partial_segment) {
+        GstM3U8PartialSegment *part =
+            g_ptr_array_index (seek_result.segment->partial_segments,
+            seek_result.part_idx);
+        *stop = part->stream_time + part->duration;
+      } else {
+        *stop =
+            seek_result.segment->stream_time + seek_result.segment->duration;
+      }
+
+      gst_m3u8_media_segment_unref (seek_result.segment);
+    }
+  }
+
   return TRUE;
 }
 
@@ -1549,7 +2594,29 @@ gst_hls_media_playlist_recommended_buffering_threshold (GstHLSMediaPlaylist *
     return GST_CLOCK_TIME_NONE;
 
   /* The recommended buffering threshold is 1.5 average segment duration */
-  return 3 * (playlist->duration / playlist->segments->len) / 2;
+  GstClockTime threshold =
+      3 * (playlist->duration / playlist->segments->len) / 2;
+
+  if (GST_HLS_MEDIA_PLAYLIST_IS_LIVE (playlist)) {
+    /* For live playlists, reduce the recommended buffering threshold 
+     * to match the starting hold back distance if needed, otherwise
+     * we'll hit the live edge and have to wait before we hit 100% */
+    if (GST_CLOCK_TIME_IS_VALID (playlist->hold_back)
+        && threshold > playlist->hold_back)
+      threshold = playlist->hold_back;
+    else if (GST_CLOCK_TIME_IS_VALID (playlist->targetduration)
+        && threshold > 3 * playlist->targetduration)
+      threshold = 3 * playlist->targetduration;
+
+    if (GST_CLOCK_TIME_IS_VALID (playlist->part_hold_back)
+        && threshold > playlist->part_hold_back)
+      threshold = playlist->part_hold_back;
+    else if (GST_CLOCK_TIME_IS_VALID (playlist->partial_targetduration)
+        && threshold > 3 * playlist->partial_targetduration)
+      threshold = 3 * playlist->partial_targetduration;
+  }
+
+  return threshold;
 }
 
 GstHLSRenditionStream *
@@ -1584,7 +2651,7 @@ gst_m3u8_get_hls_media_type_from_string (const gchar * type_name)
     return GST_HLS_RENDITION_STREAM_TYPE_VIDEO;
   if (strcmp (type_name, "SUBTITLES") == 0)
     return GST_HLS_RENDITION_STREAM_TYPE_SUBTITLES;
-  if (strcmp (type_name, "CLOSED_CAPTIONS") == 0)
+  if (strcmp (type_name, "CLOSED-CAPTIONS") == 0)
     return GST_HLS_RENDITION_STREAM_TYPE_CLOSED_CAPTIONS;
 
   return GST_HLS_RENDITION_STREAM_TYPE_INVALID;
@@ -1604,7 +2671,7 @@ gst_hls_rendition_stream_type_get_name (GstHLSRenditionStreamType mtype)
   return nicks[mtype];
 }
 
-/* returns unquoted copy of string */
+/* returns copy of string with surrounding quotation marks removed */
 static gchar *
 gst_m3u8_unquote (const gchar * str)
 {
@@ -1658,7 +2725,8 @@ gst_m3u8_parse_media (gchar * desc, const gchar * base_uri)
     } else if (strcmp (a, "AUTOSELECT") == 0) {
       media->autoselect = g_ascii_strcasecmp (v, "yes") == 0;
     } else {
-      /* unhandled: ASSOC-LANGUAGE, INSTREAM-ID, CHARACTERISTICS */
+      /* unhandled: ASSOC-LANGUAGE, INSTREAM-ID, CHARACTERISTICS,
+       * STABLE-RENDITION-ID, CHANNELS */
       GST_FIXME ("EXT-X-MEDIA: unhandled attribute: %s = %s", a, v);
     }
   }
@@ -1669,7 +2737,8 @@ gst_m3u8_parse_media (gchar * desc, const gchar * base_uri)
   if (media->group_id == NULL || media->name == NULL)
     goto required_attributes_missing;
 
-  if (media->mtype == GST_HLS_RENDITION_STREAM_TYPE_CLOSED_CAPTIONS)
+  if (media->mtype == GST_HLS_RENDITION_STREAM_TYPE_CLOSED_CAPTIONS
+      && media->uri != NULL)
     goto uri_with_cc;
 
   GST_DEBUG ("media: %s, group '%s', name '%s', uri '%s', %s %s %s, lang=%s",
@@ -2264,20 +3333,26 @@ hls_master_playlist_new_from_data (gchar * data, const gchar * base_uri)
 
 GstHLSVariantStream *
 hls_master_playlist_get_variant_for_bitrate (GstHLSMasterPlaylist *
-    playlist, GstHLSVariantStream * current_variant, guint bitrate,
-    guint min_bitrate)
+    playlist, gboolean iframe_variant, guint bitrate,
+    guint min_bitrate, GList * failed_variants)
 {
-  GstHLSVariantStream *variant = current_variant;
-  GstHLSVariantStream *variant_by_min = current_variant;
+  GstHLSVariantStream *variant = NULL;
+  GstHLSVariantStream *variant_by_min = NULL;
   GList *l;
 
   /* variant lists are sorted low to high, so iterate from highest to lowest */
-  if (current_variant == NULL || !current_variant->iframe)
-    l = g_list_last (playlist->variants);
-  else
+  if (iframe_variant && playlist->iframe_variants != NULL)
     l = g_list_last (playlist->iframe_variants);
+  else
+    l = g_list_last (playlist->variants);
 
   while (l != NULL) {
+    if (g_list_find (failed_variants, l->data) != NULL) {
+      /* Ignore all variants from the failed list */
+      l = l->prev;
+      continue;
+    }
+
     variant = l->data;
     if (variant->bandwidth >= min_bitrate)
       variant_by_min = variant;
diff --git a/ext/adaptivedemux2/hls/m3u8.h b/ext/adaptivedemux2/hls/m3u8.h
index bbe1c1cda24b7ff155b904e1b25bfbc484a1e18b..1817a8fe9b7a8531a2b3a8e9649260de94357bc7 100644
--- a/ext/adaptivedemux2/hls/m3u8.h
+++ b/ext/adaptivedemux2/hls/m3u8.h
@@ -34,8 +34,12 @@ G_BEGIN_DECLS
 
 typedef struct _GstHLSMediaPlaylist GstHLSMediaPlaylist;
 typedef struct _GstHLSTimeMap GstHLSTimeMap;
+typedef struct _GstM3U8SeekResult GstM3U8SeekResult;
 typedef struct _GstM3U8MediaSegment GstM3U8MediaSegment;
+typedef struct _GstM3U8PartialSegment GstM3U8PartialSegment;
 typedef struct _GstM3U8InitFile GstM3U8InitFile;
+typedef enum _GstM3U8PreloadHintType GstM3U8PreloadHintType;
+typedef struct _GstM3U8PreloadHint GstM3U8PreloadHint;
 typedef struct _GstHLSRenditionStream GstHLSRenditionStream;
 typedef struct _GstM3U8Client GstM3U8Client;
 typedef struct _GstHLSVariantStream GstHLSVariantStream;
@@ -43,6 +47,7 @@ typedef struct _GstHLSMasterPlaylist GstHLSMasterPlaylist;
 
 #define GST_HLS_MEDIA_PLAYLIST(m) ((GstHLSMediaPlaylist*)m)
 #define GST_M3U8_MEDIA_SEGMENT(f) ((GstM3U8MediaSegment*)f)
+#define GST_M3U8_PARTIAL_SEGMENT(p) ((GstM3U8PartialSegment*)p)
 
 #define GST_HLS_MEDIA_PLAYLIST_LOCK(m) g_mutex_lock (&m->lock);
 #define GST_HLS_MEDIA_PLAYLIST_UNLOCK(m) g_mutex_unlock (&m->lock);
@@ -61,6 +66,21 @@ typedef enum {
   GST_HLS_PLAYLIST_TYPE_VOD,
 } GstHLSPlaylistType;
 
+/* Extra seek flag extensions for partial segment handling
+ * Values are chosen to avoid collision with the core GST_SEEK_FLAG_*
+ * flags */
+#define GST_HLS_M3U8_SEEK_FLAG_ALLOW_PARTIAL (1 << 16)  /* Allow seeking to a partial segment */
+
+struct _GstM3U8SeekResult {
+  /* stream time of the segment or partial segment */
+  GstClockTimeDiff stream_time;
+
+  GstM3U8MediaSegment	*segment;
+
+  gboolean found_partial_segment;
+  guint part_idx;
+};
+
 /**
  * GstHLSMediaPlaylist:
  *
@@ -77,11 +97,16 @@ struct _GstHLSMediaPlaylist
   gchar *uri;                   /* actually downloaded URI */
   gchar *base_uri;              /* URI to use as base for resolving relative URIs.
                                  * This will be different to uri in case of redirects */
+  GstClockTime playlist_ts;     /* Monotonic clock time estimate for this playlist's validity from download time and cached Age */
+  GstClockTime request_time;	/* Time at which this playlist was requested in monotonic clock time. */
+
   /* Base Tag */
   gint version;                 /* EXT-X-VERSION (default 1) */
 
   /* Media Playlist Tags */
   GstClockTime targetduration;  /* EXT-X-TARGETDURATION, default GST_CLOCK_TIME_NONE */
+  GstClockTime partial_targetduration;  /* EXT-X-PART-INF, default GST_CLOCK_TIME_NONE */
+
   gint64 media_sequence;	/* EXT-X-MEDIA-SEQUENCE, MSN of the first Media
 				   Segment in the playlist. */
   gint64 discont_sequence;	/* EXT-X-DISCONTINUITY-SEQUENCE. Default : 0 */
@@ -101,6 +126,8 @@ struct _GstHLSMediaPlaylist
 
   GPtrArray *segments;		/* Array of GstM3U8MediaSegment */
 
+  GPtrArray *preload_hints;		/* Array of GstM3U8PreloadHint */
+
   /* Generated information */
   GstClockTime duration;	/* The estimated total duration of all segments
 				   contained in this playlist */
@@ -108,6 +135,19 @@ struct _GstHLSMediaPlaylist
   gboolean reloaded;		/* If TRUE, this indicates that this playlist
 				 * was reloaded but had identical content */
 
+  /* Server-Control directive values */
+  GstClockTime skip_boundary;   /* Skip Boundary from CAN-SKIP-UNTIL */
+  gboolean can_skip_dateranges; /* TRUE if CAN-SKIP-DATERANGES was YES */
+
+  GstClockTime hold_back;       /* Hold-Back value, if provided (or CLOCK_TIME_NONE) */
+  GstClockTime part_hold_back;  /* Part-Hold-Back value, if provided (or CLOCK_TIME_NONE */
+  gboolean can_block_reload;    /* TRUE if CAN-BLOCK-RELOAD was YES */
+
+  /* Delta playlist info from EXT-X-SKIP tag */
+  gint skipped_segments;
+  gint num_removed_date_ranges;
+  gchar **removed_date_ranges;
+
   /*< private > */
   GMutex lock;
 
@@ -127,6 +167,66 @@ GstHLSMediaPlaylist * gst_hls_media_playlist_ref (GstHLSMediaPlaylist * m3u8);
 
 void                  gst_hls_media_playlist_unref (GstHLSMediaPlaylist * m3u8);
 
+/**
+ * GstM3U8PartialSegment:
+ *
+ * Official term in RFC : "Partial Segment"
+ *
+ */
+struct _GstM3U8PartialSegment
+{
+  gboolean is_gap; /* TRUE if this part is a gap */
+  gboolean independent; /* TRUE if there is an I-frame in the partial segment */
+  gchar *uri;
+  gint64 offset, size;
+
+  GstClockTimeDiff stream_time;	/* Computed stream time */
+  GstClockTime duration;
+
+  gint ref_count;               /* ATOMIC */
+};
+
+GstM3U8PartialSegment *
+gst_m3u8_partial_segment_ref   (GstM3U8PartialSegment *part);
+
+void
+gst_m3u8_partial_segment_unref (GstM3U8PartialSegment *part);
+
+/* Set up as flags, so we can form a bitmask
+ * of seen hint types */
+enum _GstM3U8PreloadHintType {
+  M3U8_PRELOAD_HINT_NONE = (0 << 0),
+  M3U8_PRELOAD_HINT_MAP = (1 << 0),
+  M3U8_PRELOAD_HINT_PART = (1 << 1),
+};
+
+#define M3U8_PRELOAD_HINT_ALL (M3U8_PRELOAD_HINT_PART | M3U8_PRELOAD_HINT_MAP)
+
+/**
+ * GstM3U8PreloadHint:
+ *
+ * Official term in RFC : "Preload Hint"
+ *
+ */
+struct _GstM3U8PreloadHint
+{
+  GstM3U8PreloadHintType hint_type;
+
+  gchar *uri;
+  gint64 offset, size;
+
+  gint ref_count;               /* ATOMIC */
+};
+
+GstM3U8PreloadHint *
+gst_m3u8_preload_hint_ref  (GstM3U8PreloadHint *hint);
+
+void
+gst_m3u8_preload_hint_unref (GstM3U8PreloadHint *hint);
+
+gboolean
+gst_m3u8_preload_hint_equal (GstM3U8PreloadHint *hint1, GstM3U8PreloadHint *hint2);
+
 /**
  * GstM3U8MediaSegment:
  *
@@ -137,6 +237,7 @@ void                  gst_hls_media_playlist_unref (GstHLSMediaPlaylist * m3u8);
 struct _GstM3U8MediaSegment
 {
   gboolean is_gap; /* TRUE if EXT-X-GAP was present for this segment */
+  gboolean partial_only; /* TRUE if this is the last segment in a playlist consisting of only EXT-X-PART and no full URL */
 
   gchar *title;
   GstClockTimeDiff stream_time;	/* Computed stream time */
@@ -148,9 +249,12 @@ struct _GstM3U8MediaSegment
   gchar *key;
   guint8 iv[16];
   gint64 offset, size;
-  gint ref_count;               /* ATOMIC */
   GstM3U8InitFile *init_file;   /* Media Initialization (hold ref) */
   GDateTime *datetime;		/* EXT-X-PROGRAM-DATE-TIME */
+
+  GPtrArray *partial_segments; /* If there are Partial Segments for this Media Segment */
+
+  gint ref_count;               /* ATOMIC */
 };
 
 struct _GstM3U8InitFile
@@ -170,27 +274,36 @@ gst_m3u8_media_segment_ref   (GstM3U8MediaSegment * mfile);
 void
 gst_m3u8_media_segment_unref (GstM3U8MediaSegment * mfile);
 
-
 gboolean
 gst_hls_media_playlist_has_same_data (GstHLSMediaPlaylist * m3u8,
 				      gchar   * playlist_data);
 
 GstHLSMediaPlaylist *
 gst_hls_media_playlist_parse (gchar        * data,
+			      GstClockTime playlist_ts,
 			      const gchar  * uri,
 			      const gchar  * base_uri);
 
+gboolean
+gst_hls_media_playlist_sync_skipped_segments (GstHLSMediaPlaylist * m3u8,
+					   GstHLSMediaPlaylist * reference);
+
 void
 gst_hls_media_playlist_recalculate_stream_time (GstHLSMediaPlaylist *playlist,
 						GstM3U8MediaSegment *anchor);
 
+void
+gst_hls_media_playlist_recalculate_stream_time_from_part (GstHLSMediaPlaylist *playlist,
+						GstM3U8MediaSegment *anchor, guint part_idx);
+
 GstM3U8MediaSegment *
 gst_hls_media_playlist_sync_to_segment      (GstHLSMediaPlaylist * m3u8,
 					     GstM3U8MediaSegment * segment);
 
 gboolean
 gst_hls_media_playlist_sync_to_playlist     (GstHLSMediaPlaylist * m3u8,
-					     GstHLSMediaPlaylist * reference);
+					     GstHLSMediaPlaylist * reference,
+					     gboolean *discont);
 
 gboolean
 gst_hls_media_playlist_has_next_fragment    (GstHLSMediaPlaylist * m3u8,
@@ -202,12 +315,21 @@ gst_hls_media_playlist_advance_fragment     (GstHLSMediaPlaylist * m3u8,
 					     GstM3U8MediaSegment * current,
 					     gboolean  forward);
 
-GstM3U8MediaSegment *
-gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist *self);
+gboolean
+gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist *self, 
+					     GstM3U8SeekResult *seek_result);
+
+GstClockTime
+gst_hls_media_playlist_get_end_stream_time  (GstHLSMediaPlaylist * m3u8);
 
 GstClockTime
 gst_hls_media_playlist_get_duration         (GstHLSMediaPlaylist * m3u8);
 
+void
+gst_hls_media_playlist_get_next_msn_and_part (GstHLSMediaPlaylist * m3u8,
+					      gint64 *next_msn,
+					      gint64 *next_part);
+
 gchar *
 gst_hls_media_playlist_get_uri              (GstHLSMediaPlaylist * m3u8);
 
@@ -223,16 +345,23 @@ gboolean
 gst_hls_media_playlist_has_lost_sync        (GstHLSMediaPlaylist * m3u8,
 					     GstClockTime position);
 
-GstM3U8MediaSegment *
-gst_hls_media_playlist_seek                 (GstHLSMediaPlaylist *playlist,
+gboolean
+gst_hls_media_playlist_seek (GstHLSMediaPlaylist *playlist,
 					     gboolean forward,
 					     GstSeekFlags flags,
-					     GstClockTimeDiff ts);
+					     GstClockTimeDiff ts,
+					     GstM3U8SeekResult *seek_result);
+
+gboolean
+gst_hls_media_playlist_find_position (GstHLSMediaPlaylist *playlist,
+               GstClockTimeDiff ts, gboolean in_partial_segments,
+               GstM3U8SeekResult *seek_result);
+
 void
 gst_hls_media_playlist_dump                 (GstHLSMediaPlaylist* self);
 
 GstClockTime
-gst_hls_media_playlist_recommended_buffering_threshold (GstHLSMediaPlaylist *playlist);
+gst_hls_media_playlist_recommended_buffering_threshold (GstHLSMediaPlaylist * playlist);
 
 typedef enum
 {
@@ -367,9 +496,10 @@ GstHLSMasterPlaylist * hls_master_playlist_new_from_data (gchar       * data,
 
 #define gst_hls_master_playlist_get_variant_for_bitrate hls_master_playlist_get_variant_for_bitrate
 GstHLSVariantStream *  hls_master_playlist_get_variant_for_bitrate (GstHLSMasterPlaylist * playlist,
-								    GstHLSVariantStream  * current_variant,
-								    guint                  bitrate,
-								    guint                  min_bitrate);
+								    gboolean  iframe_variant,
+								    guint     bitrate,
+								    guint     min_bitrate,
+                    GList   * failed_variants);
 
 #define gst_hls_master_playlist_get_common_caps hls_master_playlist_get_common_caps
 GstCaps *              hls_master_playlist_get_common_caps (GstHLSMasterPlaylist *playlist);
diff --git a/ext/adaptivedemux2/hls/meson.build b/ext/adaptivedemux2/hls/meson.build
index c4cc3ad4355756fdbe184a364f3d48e23facc280..a9ce7ae6ae0777ea58badb7a4bf46a428173f33f 100644
--- a/ext/adaptivedemux2/hls/meson.build
+++ b/ext/adaptivedemux2/hls/meson.build
@@ -1,5 +1,8 @@
 hls_sources = [
   'hls/gsthlsdemux.c',
+  'hls/gsthlsdemux-stream.c',
+  'hls/gsthlsdemux-playlist-loader.c',
+  'hls/gsthlsdemux-preloader.c',
   'hls/gsthlsdemux-util.c',
   'hls/gsthlselement.c',
   'hls/m3u8.c',
diff --git a/ext/adaptivedemux2/meson.build b/ext/adaptivedemux2/meson.build
index 6f4564deb0e12fe06c20e5732bbc39f9194299a7..711b38a2a90c3e04f9a3992449e081f75a8c0c41 100644
--- a/ext/adaptivedemux2/meson.build
+++ b/ext/adaptivedemux2/meson.build
@@ -56,6 +56,8 @@ hls_dep = dependency('', required : false)
 adaptivedemux2_dep = dependency('', required : false)
 
 adaptivedemux2_opt = get_option('adaptivedemux2')
+soup_opt = get_option('soup')
+soup_ver_opt = get_option('soup-version')
 if adaptivedemux2_opt.disabled()
   message('Not building adaptivedemux2 plugin because it was disabled')
   subdir_done()
@@ -74,60 +76,71 @@ plugin_sources += dash_sources
 plugin_sources += smoothstreaming_sources
 plugin_sources += hls_sources
 
+libdl = cc.find_library('dl', required: false)
 soup_loader_args = ['-DBUILDING_ADAPTIVEDEMUX2']
+soup_link_args = []
+soup_link_deps = []
 
 default_library = get_option('default_library')
-if default_library in ['static', 'both']
-  libsoup2_dep = dependency('libsoup-2.4', version : '>=2.48',
-                            required : false, fallback : ['libsoup', 'libsoup_dep'],
-                            default_options: ['sysprof=disabled'])
-  libsoup3_dep = dependency('libsoup-3.0', required : false,
-                            fallback : ['libsoup3', 'libsoup_dep'])
+if host_system != 'linux' or default_library in ['static', 'both']
+  if soup_ver_opt in ['auto', '3']
+    libsoup3_dep = dependency('libsoup-3.0', allow_fallback: true,
+                              required: soup_ver_opt == '3' and soup_opt.enabled())
+  endif
+  if soup_ver_opt in ['auto', '2']
+    libsoup2_dep = dependency('libsoup-2.4', version : '>=2.48', allow_fallback: true,
+                              default_options: ['sysprof=disabled'],
+                              required: soup_ver_opt == '2' and soup_opt.enabled())
+  endif
 
   if libsoup3_dep.found()
-    soup_dep = libsoup3_dep
-    static_soup_loader_args = ['-DSTATIC_SOUP=3']
+    soup_link_deps += [libsoup3_dep]
+    soup_link_args = ['-DLINK_SOUP=3']
   elif libsoup2_dep.found()
-    soup_dep = libsoup2_dep
-    static_soup_loader_args = ['-DSTATIC_SOUP=2']
+    soup_link_deps += [libsoup2_dep]
+    soup_link_args = ['-DLINK_SOUP=2']
   else
     if adaptivedemux2_opt.enabled()
-      error(f'adaptivedemux2: Either libsoup2 or libsoup3 is needed for build with default_library=@default_library@')
+      error(f'adaptivedemux2: Either libsoup2 or libsoup3 is needed')
     endif
-
-    message(f'Not building adaptivedemux2 plugin: either libsoup2 or libsoup3 is needed for build with default_library=@default_library@')
+    message(f'Not building adaptivedemux2 plugin: either libsoup2 or libsoup3 is needed')
     subdir_done()
   endif
-
-  # Static plugin links to libsoup directly at build time
-  adaptivedemux2_static = static_library('gstadaptivedemux2',
-    plugin_sources,
-    include_directories: [configinc, libsinc],
-    c_args: [gst_plugins_good_args, soup_loader_args, soup_loader_args, hls_cargs,
-             '-DGST_ISOFF_API=G_GNUC_INTERNAL'],
-    dependencies: [gst_dep, gsttag_dep, gstnet_dep, gstbase_dep,
-                   gstpbutils_dep, gstapp_dep, soup_dep,
-                   gio_dep, adaptive_xml2_dep,
-                   hls_crypto_dep, libm],
-    install: true,
-    install_dir: plugins_install_dir)
 endif
 
-if default_library in ['shared', 'both']
-  # Shared plugin doesn't link to libsoup but dlopen()s it at runtime
-  libdl = cc.find_library('dl', required: false)
-
-  adaptivedemux2_shared = shared_library('gstadaptivedemux2',
-    plugin_sources,
-    include_directories: [configinc, libsinc],
-    c_args: [gst_plugins_good_args, soup_loader_args, hls_cargs,
-             '-DGST_ISOFF_API=G_GNUC_INTERNAL'],
-    dependencies: [gsttag_dep, gstnet_dep, gstbase_dep,
-                   gstpbutils_dep, gstapp_dep, gio_dep,
-                   gmodule_dep, adaptive_xml2_dep,
-                   hls_crypto_dep, libm, libdl],
-    install: true,
-    install_dir: plugins_install_dir)
+# Shared plugin doesn't link to libsoup but dlopen()s it at runtime
+adaptive_kwargs = {
+  'sources': plugin_sources,
+  'include_directories': [configinc, libsinc],
+  'install': true,
+  'install_dir': plugins_install_dir,
+}
+adaptive_deps = [gmodule_dep, gst_dep, gsttag_dep, gstnet_dep, gstbase_dep, gstpbutils_dep,
+    gstapp_dep, gio_dep, adaptive_xml2_dep, hls_crypto_dep, libdl, libm]
+adaptive_args = [gst_plugins_good_args, soup_loader_args, hls_cargs,
+    '-DGST_ISOFF_API=G_GNUC_INTERNAL']
+
+if host_system != 'linux'
+    adaptivedemux2 = library('gstadaptivedemux2',
+      c_args: [adaptive_args, soup_link_args],
+      dependencies: [adaptive_deps, soup_link_deps],
+      kwargs: adaptive_kwargs)
+    adaptivedemux2_static = adaptivedemux2
+    adaptivedemux2_shared = adaptivedemux2
+else
+  if default_library in ['static', 'both']
+    # Static plugin links to libsoup directly at build time
+    adaptivedemux2_static = static_library('gstadaptivedemux2',
+      c_args: [adaptive_args, soup_link_args],
+      dependencies: [adaptive_deps, soup_link_deps],
+      kwargs: adaptive_kwargs)
+  endif
+  if default_library in ['shared', 'both']
+    adaptivedemux2_shared = shared_library('gstadaptivedemux2',
+      c_args: adaptive_args,
+      dependencies: adaptive_deps,
+      kwargs: adaptive_kwargs)
+  endif
 endif
 
 # Use the static library to generate the .pc file if it's available. The shared
diff --git a/ext/adaptivedemux2/mss/gstmssdemux.c b/ext/adaptivedemux2/mss/gstmssdemux.c
index 186e02985003c5ee37867514a8fe1361e7c674c9..846d6ae7bd5fd10f2f70c8c0f87180d0a8155adc 100644
--- a/ext/adaptivedemux2/mss/gstmssdemux.c
+++ b/ext/adaptivedemux2/mss/gstmssdemux.c
@@ -431,6 +431,9 @@ gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
     if (lang != NULL)
       tags = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, lang, NULL);
 
+    if (tags)
+      gst_adaptive_demux2_stream_set_tags (stream, gst_tag_list_ref (tags));
+
     track = gst_adaptive_demux_track_new (demux, stream_type,
         GST_STREAM_FLAG_NONE, (gchar *) stream_id, create_mss_caps (mss_stream,
             caps), tags);
@@ -443,9 +446,6 @@ gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
     GST_DEBUG_OBJECT (stream, "Current quality bitrate %" G_GUINT64_FORMAT,
         gst_mss_stream_get_current_bitrate (manifeststream));
 
-    if (tags)
-      gst_adaptive_demux2_stream_set_tags (stream, tags);
-
     active_streams = g_slist_prepend (active_streams, mss_stream);
   }
 
diff --git a/ext/adaptivedemux2/mss/gstmssmanifest.c b/ext/adaptivedemux2/mss/gstmssmanifest.c
index 110aa681c91c46e11720082f861b5d8895300492..c9c4fa608fe982ead09909a2924ad80a97361e4e 100644
--- a/ext/adaptivedemux2/mss/gstmssmanifest.c
+++ b/ext/adaptivedemux2/mss/gstmssmanifest.c
@@ -211,7 +211,7 @@ node_has_type (xmlNodePtr node, const gchar * name)
 static GstMssStreamQuality *
 gst_mss_stream_quality_new (xmlNodePtr node)
 {
-  GstMssStreamQuality *q = g_slice_new (GstMssStreamQuality);
+  GstMssStreamQuality *q = g_new (GstMssStreamQuality, 1);
 
   q->xmlnode = node;
   q->bitrate_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_BITRATE);
@@ -230,7 +230,7 @@ gst_mss_stream_quality_free (GstMssStreamQuality * quality)
   g_return_if_fail (quality != NULL);
 
   xmlFree (quality->bitrate_str);
-  g_slice_free (GstMssStreamQuality, quality);
+  g_free (quality);
 }
 
 static gint
diff --git a/ext/amrnb/GstAmrnbEnc.prs b/ext/amrnb/GstAmrnbEnc.prs
new file mode 100644
index 0000000000000000000000000000000000000000..69e3719dd104f7d54e36b07f04b55f107d72d383
--- /dev/null
+++ b/ext/amrnb/GstAmrnbEnc.prs
@@ -0,0 +1,11 @@
+[_presets_]
+version=0.10
+element-name=GstAmrnbEnc
+
+[enhance-size]
+_meta/comment=Maximize compression, lowest bitrate
+band-mode=0
+
+[enhance-quality]
+_meta/comment=Maximize quality, highest bitrate
+band-mode=7
diff --git a/ext/amrnb/README b/ext/amrnb/README
new file mode 100644
index 0000000000000000000000000000000000000000..879dbe74e96eba33e00432b280e6a495586bb91a
--- /dev/null
+++ b/ext/amrnb/README
@@ -0,0 +1,6 @@
+Compiling AMRNB:
+================
+
+To compile the amrnb plugin, you need the opencore-amrnb development package.
+If your distribution does not provide this package, you can download the 
+source code from "http://sourceforge.net/projects/opencore-amr".
diff --git a/ext/amrnb/amrnb.c b/ext/amrnb/amrnb.c
new file mode 100644
index 0000000000000000000000000000000000000000..3fe3a00f873bb5cc90035858cf9636f4fa113735
--- /dev/null
+++ b/ext/amrnb/amrnb.c
@@ -0,0 +1,44 @@
+/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amrnbdec.h"
+#include "amrnbenc.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  gboolean ret = FALSE;
+
+  ret |= GST_ELEMENT_REGISTER (amrnbdec, plugin);
+  ret |= GST_ELEMENT_REGISTER (amrnbenc, plugin);
+
+  return ret;
+}
+
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    amrnb,
+    "Adaptive Multi-Rate Narrow-Band",
+    plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME,
+    GST_PACKAGE_ORIGIN);
diff --git a/ext/amrnb/amrnbdec.c b/ext/amrnb/amrnbdec.c
new file mode 100644
index 0000000000000000000000000000000000000000..e984fb854bb83d22b6b8b9d02a3d3cc659325b2f
--- /dev/null
+++ b/ext/amrnb/amrnbdec.c
@@ -0,0 +1,309 @@
+/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-amrnbdec
+ * @title: amrnbdec
+ * @see_also: #GstAmrnbEnc, #GstAmrParse
+ *
+ * AMR narrowband decoder based on the
+ * [opencore codec implementation](http://sourceforge.net/projects/opencore-amr).
+ *
+ * ## Example launch line
+ * |[
+ * gst-launch-1.0 filesrc location=abc.amr ! amrparse ! amrnbdec ! audioconvert ! audioresample ! autoaudiosink
+ * ]|
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amrnbdec.h"
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/AMR, " "rate = (int) 8000, " "channels = (int) 1")
+    );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw, format = (string) " GST_AUDIO_NE (S16) ", "
+        "layout = (string) interleaved, "
+        "rate = (int) 8000," "channels = (int) 1")
+    );
+
+GST_DEBUG_CATEGORY_STATIC (gst_amrnbdec_debug);
+#define GST_CAT_DEFAULT gst_amrnbdec_debug
+
+static const gint block_size_if1[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5,
+  0, 0, 0, 0, 0, 0, 0
+};
+
+static const gint block_size_if2[16] = { 12, 13, 15, 17, 18, 20, 25, 30, 5,
+  0, 0, 0, 0, 0, 0, 0
+};
+
+static GType
+gst_amrnb_variant_get_type (void)
+{
+  static GType gst_amrnb_variant_type = 0;
+  static const GEnumValue gst_amrnb_variant[] = {
+    {GST_AMRNB_VARIANT_IF1, "IF1", "IF1"},
+    {GST_AMRNB_VARIANT_IF2, "IF2", "IF2"},
+    {0, NULL, NULL},
+  };
+  if (!gst_amrnb_variant_type) {
+    gst_amrnb_variant_type =
+        g_enum_register_static ("GstAmrnbVariant", gst_amrnb_variant);
+  }
+  return gst_amrnb_variant_type;
+}
+
+#define GST_AMRNB_VARIANT_TYPE (gst_amrnb_variant_get_type())
+
+#define VARIANT_DEFAULT GST_AMRNB_VARIANT_IF1
+enum
+{
+  PROP_0,
+  PROP_VARIANT
+};
+
+static void gst_amrnbdec_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_amrnbdec_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static gboolean gst_amrnbdec_start (GstAudioDecoder * dec);
+static gboolean gst_amrnbdec_stop (GstAudioDecoder * dec);
+static gboolean gst_amrnbdec_set_format (GstAudioDecoder * dec, GstCaps * caps);
+static GstFlowReturn gst_amrnbdec_parse (GstAudioDecoder * dec,
+    GstAdapter * adapter, gint * offset, gint * length);
+static GstFlowReturn gst_amrnbdec_handle_frame (GstAudioDecoder * dec,
+    GstBuffer * buffer);
+
+#define gst_amrnbdec_parent_class parent_class
+G_DEFINE_TYPE (GstAmrnbDec, gst_amrnbdec, GST_TYPE_AUDIO_DECODER);
+GST_ELEMENT_REGISTER_DEFINE (amrnbdec, "amrnbdec", GST_RANK_PRIMARY,
+    GST_TYPE_AMRNBDEC);
+
+static void
+gst_amrnbdec_class_init (GstAmrnbDecClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
+
+  object_class->set_property = gst_amrnbdec_set_property;
+  object_class->get_property = gst_amrnbdec_get_property;
+
+  gst_element_class_add_static_pad_template (element_class, &sink_template);
+  gst_element_class_add_static_pad_template (element_class, &src_template);
+
+  gst_element_class_set_static_metadata (element_class, "AMR-NB audio decoder",
+      "Codec/Decoder/Audio",
+      "Adaptive Multi-Rate Narrow-Band audio decoder",
+      "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
+
+  base_class->start = GST_DEBUG_FUNCPTR (gst_amrnbdec_start);
+  base_class->stop = GST_DEBUG_FUNCPTR (gst_amrnbdec_stop);
+  base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrnbdec_set_format);
+  base_class->parse = GST_DEBUG_FUNCPTR (gst_amrnbdec_parse);
+  base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrnbdec_handle_frame);
+
+  g_object_class_install_property (object_class, PROP_VARIANT,
+      g_param_spec_enum ("variant", "Variant",
+          "The decoder variant", GST_AMRNB_VARIANT_TYPE,
+          VARIANT_DEFAULT,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+  GST_DEBUG_CATEGORY_INIT (gst_amrnbdec_debug, "amrnbdec", 0,
+      "AMR-NB audio decoder");
+
+  gst_type_mark_as_plugin_api (GST_AMRNB_VARIANT_TYPE, 0);
+}
+
+static void
+gst_amrnbdec_init (GstAmrnbDec * amrnbdec)
+{
+  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (amrnbdec), TRUE);
+  gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
+      (amrnbdec), TRUE);
+  GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (amrnbdec));
+}
+
+static gboolean
+gst_amrnbdec_start (GstAudioDecoder * dec)
+{
+  GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
+
+  GST_DEBUG_OBJECT (dec, "start");
+  if (!(amrnbdec->handle = Decoder_Interface_init ()))
+    return FALSE;
+
+  amrnbdec->rate = 0;
+  amrnbdec->channels = 0;
+
+  return TRUE;
+}
+
+static gboolean
+gst_amrnbdec_stop (GstAudioDecoder * dec)
+{
+  GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
+
+  GST_DEBUG_OBJECT (dec, "stop");
+  Decoder_Interface_exit (amrnbdec->handle);
+
+  return TRUE;
+}
+
+static void
+gst_amrnbdec_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstAmrnbDec *self = GST_AMRNBDEC (object);
+
+  switch (prop_id) {
+    case PROP_VARIANT:
+      self->variant = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  return;
+}
+
+static void
+gst_amrnbdec_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstAmrnbDec *self = GST_AMRNBDEC (object);
+
+  switch (prop_id) {
+    case PROP_VARIANT:
+      g_value_set_enum (value, self->variant);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  return;
+}
+
+static gboolean
+gst_amrnbdec_set_format (GstAudioDecoder * dec, GstCaps * caps)
+{
+  GstStructure *structure;
+  GstAmrnbDec *amrnbdec;
+  GstAudioInfo info;
+
+  amrnbdec = GST_AMRNBDEC (dec);
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  /* get channel count */
+  gst_structure_get_int (structure, "channels", &amrnbdec->channels);
+  gst_structure_get_int (structure, "rate", &amrnbdec->rate);
+
+  /* create reverse caps */
+  gst_audio_info_init (&info);
+  gst_audio_info_set_format (&info,
+      GST_AUDIO_FORMAT_S16, amrnbdec->rate, amrnbdec->channels, NULL);
+
+  return gst_audio_decoder_set_output_format (dec, &info);
+}
+
+static GstFlowReturn
+gst_amrnbdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
+    gint * offset, gint * length)
+{
+  GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
+  guint8 head[1];
+  guint size;
+  gboolean sync, eos;
+  gint block, mode;
+
+  size = gst_adapter_available (adapter);
+  if (size < 1)
+    return GST_FLOW_ERROR;
+
+  gst_audio_decoder_get_parse_state (dec, &sync, &eos);
+
+  /* need to peek data to get the size */
+  gst_adapter_copy (adapter, head, 0, 1);
+
+  /* get size */
+  switch (amrnbdec->variant) {
+    case GST_AMRNB_VARIANT_IF1:
+      mode = (head[0] >> 3) & 0x0F;
+      block = block_size_if1[mode] + 1;
+      break;
+    case GST_AMRNB_VARIANT_IF2:
+      mode = head[0] & 0x0F;
+      block = block_size_if2[mode] + 1;
+      break;
+    default:
+      g_assert_not_reached ();
+      return GST_FLOW_ERROR;
+      break;
+  }
+
+  GST_DEBUG_OBJECT (amrnbdec, "mode %d, block %d", mode, block);
+
+  if (block > size)
+    return GST_FLOW_EOS;
+
+  *offset = 0;
+  *length = block;
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_amrnbdec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
+{
+  GstAmrnbDec *amrnbdec;
+  GstMapInfo inmap, outmap;
+  GstBuffer *out;
+
+  amrnbdec = GST_AMRNBDEC (dec);
+
+  /* no fancy flushing */
+  if (!buffer || !gst_buffer_get_size (buffer))
+    return GST_FLOW_OK;
+
+  gst_buffer_map (buffer, &inmap, GST_MAP_READ);
+
+  /* get output */
+  out = gst_buffer_new_and_alloc (160 * 2);
+  /* decode */
+  gst_buffer_map (out, &outmap, GST_MAP_WRITE);
+  Decoder_Interface_Decode (amrnbdec->handle, inmap.data,
+      (gint16 *) outmap.data, 0);
+  gst_buffer_unmap (out, &outmap);
+
+  gst_buffer_unmap (buffer, &inmap);
+
+  return gst_audio_decoder_finish_frame (dec, out, 1);
+}
diff --git a/ext/amrnb/amrnbdec.h b/ext/amrnb/amrnbdec.h
new file mode 100644
index 0000000000000000000000000000000000000000..25b3db2990abde9459a9c13e9d1f39566fe3bf28
--- /dev/null
+++ b/ext/amrnb/amrnbdec.h
@@ -0,0 +1,71 @@
+/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_AMRNBDEC_H__
+#define __GST_AMRNBDEC_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiodecoder.h>
+
+#include <opencore-amrnb/interf_dec.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AMRNBDEC \
+  (gst_amrnbdec_get_type())
+#define GST_AMRNBDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRNBDEC, GstAmrnbDec))
+#define GST_AMRNBDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRNBDEC, GstAmrnbDecClass))
+#define GST_IS_AMRNBDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRNBDEC))
+#define GST_IS_AMRNBDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRNBDEC))
+
+typedef struct _GstAmrnbDec GstAmrnbDec;
+typedef struct _GstAmrnbDecClass GstAmrnbDecClass;
+
+typedef enum
+{
+  GST_AMRNB_VARIANT_IF1,
+  GST_AMRNB_VARIANT_IF2
+} GstAmrnbVariant;
+
+struct _GstAmrnbDec {
+  GstAudioDecoder element;
+
+  GstAmrnbVariant variant;
+
+  /* library handle */
+  void *handle;
+
+  /* output settings */
+  gint channels, rate;
+};
+
+struct _GstAmrnbDecClass {
+  GstAudioDecoderClass parent_class;
+};
+
+GType gst_amrnbdec_get_type (void);
+GST_ELEMENT_REGISTER_DECLARE (amrnbdec);
+
+G_END_DECLS
+
+#endif /* __GST_AMRNBDEC_H__ */
diff --git a/ext/amrnb/amrnbenc.c b/ext/amrnb/amrnbenc.c
new file mode 100644
index 0000000000000000000000000000000000000000..8417326f358b9acfe7bf476ff822f9578ed619ef
--- /dev/null
+++ b/ext/amrnb/amrnbenc.c
@@ -0,0 +1,295 @@
+/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-amrnbenc
+ * @title: amrnbenc
+ * @see_also: #GstAmrnbDec, #GstAmrnbParse
+ *
+ * AMR narrowband encoder based on the
+ * [opencore codec implementation](http://sourceforge.net/projects/opencore-amr).
+ *
+ * ## Example launch line
+ * |[
+ * gst-launch-1.0 filesrc location=abc.wav ! wavparse ! audioconvert ! audioresample ! amrnbenc ! filesink location=abc.amr
+ * ]|
+ * Please note that the above stream misses the header, that is needed to play
+ * the stream.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amrnbenc.h"
+
+static GType
+gst_amrnbenc_bandmode_get_type (void)
+{
+  static GType gst_amrnbenc_bandmode_type = 0;
+  static const GEnumValue gst_amrnbenc_bandmode[] = {
+    {MR475, "MR475", "MR475"},
+    {MR515, "MR515", "MR515"},
+    {MR59, "MR59", "MR59"},
+    {MR67, "MR67", "MR67"},
+    {MR74, "MR74", "MR74"},
+    {MR795, "MR795", "MR795"},
+    {MR102, "MR102", "MR102"},
+    {MR122, "MR122", "MR122"},
+    {MRDTX, "MRDTX", "MRDTX"},
+    {0, NULL, NULL},
+  };
+  if (!gst_amrnbenc_bandmode_type) {
+    gst_amrnbenc_bandmode_type =
+        g_enum_register_static ("GstAmrnbEncBandMode", gst_amrnbenc_bandmode);
+  }
+  return gst_amrnbenc_bandmode_type;
+}
+
+#define GST_AMRNBENC_BANDMODE_TYPE (gst_amrnbenc_bandmode_get_type())
+
+#define BANDMODE_DEFAULT MR122
+enum
+{
+  PROP_0,
+  PROP_BANDMODE
+};
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw, format = (string) " GST_AUDIO_NE (S16) ", "
+        "layout = (string) interleaved, "
+        "rate = (int) 8000," "channels = (int) 1")
+    );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/AMR, " "rate = (int) 8000, " "channels = (int) 1")
+    );
+
+GST_DEBUG_CATEGORY_STATIC (gst_amrnbenc_debug);
+#define GST_CAT_DEFAULT gst_amrnbenc_debug
+
+static gboolean gst_amrnbenc_start (GstAudioEncoder * enc);
+static gboolean gst_amrnbenc_stop (GstAudioEncoder * enc);
+static gboolean gst_amrnbenc_set_format (GstAudioEncoder * enc,
+    GstAudioInfo * info);
+static GstFlowReturn gst_amrnbenc_handle_frame (GstAudioEncoder * enc,
+    GstBuffer * in_buf);
+
+#define gst_amrnbenc_parent_class parent_class
+G_DEFINE_TYPE (GstAmrnbEnc, gst_amrnbenc, GST_TYPE_AUDIO_ENCODER);
+GST_ELEMENT_REGISTER_DEFINE (amrnbenc, "amrnbenc", GST_RANK_SECONDARY,
+    GST_TYPE_AMRNBENC);
+
+static void
+gst_amrnbenc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstAmrnbEnc *self = GST_AMRNBENC (object);
+
+  switch (prop_id) {
+    case PROP_BANDMODE:
+      self->bandmode = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  return;
+}
+
+static void
+gst_amrnbenc_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstAmrnbEnc *self = GST_AMRNBENC (object);
+
+  switch (prop_id) {
+    case PROP_BANDMODE:
+      g_value_set_enum (value, self->bandmode);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  return;
+}
+
+static void
+gst_amrnbenc_class_init (GstAmrnbEncClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass);
+
+  object_class->set_property = gst_amrnbenc_set_property;
+  object_class->get_property = gst_amrnbenc_get_property;
+
+  base_class->start = GST_DEBUG_FUNCPTR (gst_amrnbenc_start);
+  base_class->stop = GST_DEBUG_FUNCPTR (gst_amrnbenc_stop);
+  base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrnbenc_set_format);
+  base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrnbenc_handle_frame);
+
+  g_object_class_install_property (object_class, PROP_BANDMODE,
+      g_param_spec_enum ("band-mode", "Band Mode",
+          "Encoding Band Mode (Kbps)", GST_AMRNBENC_BANDMODE_TYPE,
+          BANDMODE_DEFAULT,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+  gst_element_class_add_static_pad_template (element_class, &sink_template);
+  gst_element_class_add_static_pad_template (element_class, &src_template);
+
+  gst_element_class_set_static_metadata (element_class, "AMR-NB audio encoder",
+      "Codec/Encoder/Audio",
+      "Adaptive Multi-Rate Narrow-Band audio encoder",
+      "Wim Taymans <wim.taymans@gmail.com>");
+
+  GST_DEBUG_CATEGORY_INIT (gst_amrnbenc_debug, "amrnbenc", 0,
+      "AMR-NB audio encoder");
+
+  gst_type_mark_as_plugin_api (GST_AMRNBENC_BANDMODE_TYPE, 0);
+}
+
+static void
+gst_amrnbenc_init (GstAmrnbEnc * amrnbenc)
+{
+  GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (amrnbenc));
+}
+
+static gboolean
+gst_amrnbenc_start (GstAudioEncoder * enc)
+{
+  GstAmrnbEnc *amrnbenc = GST_AMRNBENC (enc);
+
+  GST_DEBUG_OBJECT (amrnbenc, "start");
+
+  if (!(amrnbenc->handle = Encoder_Interface_init (0)))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+gst_amrnbenc_stop (GstAudioEncoder * enc)
+{
+  GstAmrnbEnc *amrnbenc = GST_AMRNBENC (enc);
+
+  GST_DEBUG_OBJECT (amrnbenc, "stop");
+
+  Encoder_Interface_exit (amrnbenc->handle);
+
+  return TRUE;
+}
+
+static gboolean
+gst_amrnbenc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
+{
+  GstAmrnbEnc *amrnbenc;
+  GstCaps *copy;
+
+  amrnbenc = GST_AMRNBENC (enc);
+
+  /* parameters already parsed for us */
+  amrnbenc->rate = GST_AUDIO_INFO_RATE (info);
+  amrnbenc->channels = GST_AUDIO_INFO_CHANNELS (info);
+
+  /* we do not really accept other input, but anyway ... */
+  /* this is not wrong but will sound bad */
+  if (amrnbenc->channels != 1) {
+    g_warning ("amrnbdec is only optimized for mono channels");
+  }
+  if (amrnbenc->rate != 8000) {
+    g_warning ("amrnbdec is only optimized for 8000 Hz samplerate");
+  }
+
+  /* create reverse caps */
+  copy = gst_caps_new_simple ("audio/AMR",
+      "channels", G_TYPE_INT, amrnbenc->channels,
+      "rate", G_TYPE_INT, amrnbenc->rate, NULL);
+
+  gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (amrnbenc), copy);
+  gst_caps_unref (copy);
+
+  /* report needs to base class: hand one frame at a time */
+  gst_audio_encoder_set_frame_samples_min (enc, 160);
+  gst_audio_encoder_set_frame_samples_max (enc, 160);
+  gst_audio_encoder_set_frame_max (enc, 1);
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_amrnbenc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer)
+{
+  GstAmrnbEnc *amrnbenc;
+  GstFlowReturn ret;
+  GstBuffer *out;
+  GstMapInfo in_map, out_map;
+  gsize out_size;
+
+  amrnbenc = GST_AMRNBENC (enc);
+
+  g_return_val_if_fail (amrnbenc->handle, GST_FLOW_FLUSHING);
+
+  /* we don't deal with squeezing remnants, so simply discard those */
+  if (G_UNLIKELY (buffer == NULL)) {
+    GST_DEBUG_OBJECT (amrnbenc, "no data");
+    return GST_FLOW_OK;
+  }
+
+  gst_buffer_map (buffer, &in_map, GST_MAP_READ);
+
+  if (G_UNLIKELY (in_map.size < 320)) {
+    gst_buffer_unmap (buffer, &in_map);
+    GST_DEBUG_OBJECT (amrnbenc, "discarding trailing data of %" G_GSIZE_FORMAT
+        " bytes", in_map.size);
+    return gst_audio_encoder_finish_frame (enc, NULL, -1);
+  }
+
+  /* get output, max size is 32 */
+  out = gst_buffer_new_and_alloc (32);
+  /* AMR encoder actually writes into the source data buffers it gets */
+  /* should be able to handle that with what we are given */
+
+  gst_buffer_map (out, &out_map, GST_MAP_WRITE);
+  /* encode */
+  out_size =
+      Encoder_Interface_Encode (amrnbenc->handle, amrnbenc->bandmode,
+      (short *) in_map.data, out_map.data, 0);
+  gst_buffer_unmap (out, &out_map);
+  gst_buffer_resize (out, 0, out_size);
+  gst_buffer_unmap (buffer, &in_map);
+
+  GST_LOG_OBJECT (amrnbenc, "output data size %" G_GSIZE_FORMAT, out_size);
+
+  if (out_size) {
+    ret = gst_audio_encoder_finish_frame (enc, out, 160);
+  } else {
+    /* should not happen (without dtx or so at least) */
+    GST_WARNING_OBJECT (amrnbenc, "no encoded data; discarding input");
+    gst_buffer_unref (out);
+    ret = gst_audio_encoder_finish_frame (enc, NULL, -1);
+  }
+
+  return ret;
+}
diff --git a/ext/amrnb/amrnbenc.h b/ext/amrnb/amrnbenc.h
new file mode 100644
index 0000000000000000000000000000000000000000..83262e474b83e8bc3453df2d16d0d4d36dd249e2
--- /dev/null
+++ b/ext/amrnb/amrnbenc.h
@@ -0,0 +1,67 @@
+/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_AMRNBENC_H__
+#define __GST_AMRNBENC_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudioencoder.h>
+
+#include <opencore-amrnb/interf_enc.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AMRNBENC \
+  (gst_amrnbenc_get_type())
+#define GST_AMRNBENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRNBENC, GstAmrnbEnc))
+#define GST_AMRNBENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRNBENC, GstAmrnbEncClass))
+#define GST_IS_AMRNBENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRNBENC))
+#define GST_IS_AMRNBENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRNBENC))
+
+typedef struct _GstAmrnbEnc GstAmrnbEnc;
+typedef struct _GstAmrnbEncClass GstAmrnbEncClass;
+
+struct _GstAmrnbEnc {
+  GstAudioEncoder element;
+
+  /* library handle */
+  void *handle;
+
+  /* input settings */
+  gint channels, rate;
+  gint duration;
+
+  /* property */
+  enum Mode bandmode;
+};
+
+struct _GstAmrnbEncClass {
+  GstAudioEncoderClass parent_class;
+};
+
+GType gst_amrnbenc_get_type (void);
+GST_ELEMENT_REGISTER_DECLARE (amrnbenc);
+
+G_END_DECLS
+
+#endif /* __GST_AMRNBENC_H__ */
diff --git a/ext/amrnb/meson.build b/ext/amrnb/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..6fbd8faeee1419e5d3e227d30df69f2f9f62233e
--- /dev/null
+++ b/ext/amrnb/meson.build
@@ -0,0 +1,18 @@
+amrnb_dep = dependency('opencore-amrnb', version : '>= 0.1.3', required : get_option('amrnb'))
+
+if amrnb_dep.found()
+  amrnb = library('gstamrnb',
+    ['amrnb.c', 'amrnbdec.c', 'amrnbenc.c'],
+    c_args : gst_plugins_good_args,
+    include_directories : [configinc],
+    dependencies : [gstaudio_dep, amrnb_dep],
+    install : true,
+    install_dir : plugins_install_dir,
+  )
+  plugins += [amrnb]
+  install_data(sources: 'GstAmrnbEnc.prs', install_dir: presetdir)
+
+  env = environment()
+  env.prepend('GST_PRESET_PATH', meson.current_source_dir())
+  meson.add_devenv(env)
+endif
diff --git a/ext/amrwbdec/README b/ext/amrwbdec/README
new file mode 100644
index 0000000000000000000000000000000000000000..835adef1cf3093c1b9ced00ad681a61645964b03
--- /dev/null
+++ b/ext/amrwbdec/README
@@ -0,0 +1,6 @@
+Compiling AMRWB decoder:
+========================
+
+To compile the amrwbdec plugin, you need the opencore-amrwb development
+package. If your distribution does not provide this package, you can
+download the source code from "http://sourceforge.net/projects/opencore-amr".
diff --git a/ext/amrwbdec/amrwb.c b/ext/amrwbdec/amrwb.c
new file mode 100644
index 0000000000000000000000000000000000000000..e3a028e27660fc947729c30d1cb5f1572efb12db
--- /dev/null
+++ b/ext/amrwbdec/amrwb.c
@@ -0,0 +1,38 @@
+/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) Decoder plugin
+ * Copyright (C) 2006 Edgard Lima <edgard.lima@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amrwbdec.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  return GST_ELEMENT_REGISTER (amrwbdec, plugin);
+}
+
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    amrwbdec,
+    "Adaptive Multi-Rate Wide-Band Decoder",
+    plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME,
+    GST_PACKAGE_ORIGIN);
diff --git a/ext/amrwbdec/amrwbdec.c b/ext/amrwbdec/amrwbdec.c
new file mode 100644
index 0000000000000000000000000000000000000000..3d84f647feecc20cb0fe788a24e9c0f6f5c90c3d
--- /dev/null
+++ b/ext/amrwbdec/amrwbdec.c
@@ -0,0 +1,234 @@
+/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-amrwbdec
+ * @title: amrwbdec
+ * @see_also: #GstAmrwbEnc
+ *
+ * AMR wideband decoder based on the
+ * [opencore codec implementation](http://sourceforge.net/projects/opencore-amr).
+ *
+ * ## Example launch line
+ * |[
+ * gst-launch-1.0 filesrc location=abc.amr ! amrparse ! amrwbdec ! audioconvert ! audioresample ! autoaudiosink
+ * ]|
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/audio/audio.h>
+
+#include "amrwbdec.h"
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/AMR-WB, "
+        "rate = (int) 16000, " "channels = (int) 1")
+    );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw, "
+        "format = (string) " GST_AUDIO_NE (S16) ", "
+        "layout = (string) interleaved, "
+        "rate = (int) 16000, " "channels = (int) 1")
+    );
+
+GST_DEBUG_CATEGORY_STATIC (gst_amrwbdec_debug);
+#define GST_CAT_DEFAULT gst_amrwbdec_debug
+
+#define L_FRAME16k      320     /* Frame size at 16kHz  */
+
+static const unsigned char block_size[16] =
+    { 18, 24, 33, 37, 41, 47, 51, 59, 61,
+  6, 0, 0, 0, 0, 1, 1
+};
+
+static gboolean gst_amrwbdec_start (GstAudioDecoder * dec);
+static gboolean gst_amrwbdec_stop (GstAudioDecoder * dec);
+static gboolean gst_amrwbdec_set_format (GstAudioDecoder * dec, GstCaps * caps);
+static GstFlowReturn gst_amrwbdec_parse (GstAudioDecoder * dec,
+    GstAdapter * adapter, gint * offset, gint * length);
+static GstFlowReturn gst_amrwbdec_handle_frame (GstAudioDecoder * dec,
+    GstBuffer * buffer);
+
+#define gst_amrwbdec_parent_class parent_class
+G_DEFINE_TYPE (GstAmrwbDec, gst_amrwbdec, GST_TYPE_AUDIO_DECODER);
+GST_ELEMENT_REGISTER_DEFINE (amrwbdec, "amrwbdec",
+    GST_RANK_PRIMARY, GST_TYPE_AMRWBDEC);
+
+static void
+gst_amrwbdec_class_init (GstAmrwbDecClass * klass)
+{
+  GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_static_pad_template (element_class, &sink_template);
+  gst_element_class_add_static_pad_template (element_class, &src_template);
+
+  gst_element_class_set_static_metadata (element_class, "AMR-WB audio decoder",
+      "Codec/Decoder/Audio",
+      "Adaptive Multi-Rate Wideband audio decoder",
+      "Renato Araujo <renato.filho@indt.org.br>");
+
+  base_class->start = GST_DEBUG_FUNCPTR (gst_amrwbdec_start);
+  base_class->stop = GST_DEBUG_FUNCPTR (gst_amrwbdec_stop);
+  base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrwbdec_set_format);
+  base_class->parse = GST_DEBUG_FUNCPTR (gst_amrwbdec_parse);
+  base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrwbdec_handle_frame);
+
+  GST_DEBUG_CATEGORY_INIT (gst_amrwbdec_debug, "amrwbdec", 0,
+      "AMR-WB audio decoder");
+}
+
+static void
+gst_amrwbdec_init (GstAmrwbDec * amrwbdec)
+{
+  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (amrwbdec), TRUE);
+  gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
+      (amrwbdec), TRUE);
+  GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (amrwbdec));
+}
+
+static gboolean
+gst_amrwbdec_start (GstAudioDecoder * dec)
+{
+  GstAmrwbDec *amrwbdec = GST_AMRWBDEC (dec);
+
+  GST_DEBUG_OBJECT (dec, "start");
+  if (!(amrwbdec->handle = D_IF_init ()))
+    return FALSE;
+
+  amrwbdec->rate = 0;
+  amrwbdec->channels = 0;
+
+  return TRUE;
+}
+
+static gboolean
+gst_amrwbdec_stop (GstAudioDecoder * dec)
+{
+  GstAmrwbDec *amrwbdec = GST_AMRWBDEC (dec);
+
+  GST_DEBUG_OBJECT (dec, "stop");
+  D_IF_exit (amrwbdec->handle);
+
+  return TRUE;
+}
+
+static gboolean
+gst_amrwbdec_set_format (GstAudioDecoder * dec, GstCaps * caps)
+{
+  GstStructure *structure;
+  GstAmrwbDec *amrwbdec;
+  GstAudioInfo info;
+
+  amrwbdec = GST_AMRWBDEC (dec);
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  /* get channel count */
+  gst_structure_get_int (structure, "channels", &amrwbdec->channels);
+  gst_structure_get_int (structure, "rate", &amrwbdec->rate);
+
+  /* create reverse caps */
+  gst_audio_info_init (&info);
+  gst_audio_info_set_format (&info,
+      GST_AUDIO_FORMAT_S16, amrwbdec->rate, amrwbdec->channels, NULL);
+
+  gst_audio_decoder_set_output_format (dec, &info);
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_amrwbdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
+    gint * offset, gint * length)
+{
+  GstAmrwbDec *amrwbdec = GST_AMRWBDEC (dec);
+  guint8 header[1];
+  guint size;
+  gboolean sync, eos;
+  gint block, mode;
+
+  size = gst_adapter_available (adapter);
+  if (size < 1)
+    return GST_FLOW_ERROR;
+
+  gst_audio_decoder_get_parse_state (dec, &sync, &eos);
+
+  /* need to peek data to get the size */
+  gst_adapter_copy (adapter, header, 0, 1);
+  mode = (header[0] >> 3) & 0x0F;
+  block = block_size[mode];
+
+  GST_DEBUG_OBJECT (amrwbdec, "mode %d, block %d", mode, block);
+
+  if (block) {
+    if (block > size)
+      return GST_FLOW_EOS;
+    *offset = 0;
+    *length = block;
+  } else {
+    /* no frame yet, skip one byte */
+    GST_LOG_OBJECT (amrwbdec, "skipping byte");
+    *offset = 1;
+    return GST_FLOW_EOS;
+  }
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_amrwbdec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
+{
+  GstAmrwbDec *amrwbdec;
+  GstBuffer *out;
+  GstMapInfo inmap, outmap;
+
+  amrwbdec = GST_AMRWBDEC (dec);
+
+  /* no fancy flushing */
+  if (!buffer || !gst_buffer_get_size (buffer))
+    return GST_FLOW_OK;
+
+  /* the library seems to write into the source data, hence the copy. */
+  /* should be no problem */
+  gst_buffer_map (buffer, &inmap, GST_MAP_READ);
+
+  /* get output */
+  out = gst_buffer_new_and_alloc (sizeof (gint16) * L_FRAME16k);
+  gst_buffer_map (out, &outmap, GST_MAP_WRITE);
+
+  /* decode */
+  D_IF_decode (amrwbdec->handle, (unsigned char *) inmap.data,
+      (short int *) outmap.data, _good_frame);
+
+  gst_buffer_unmap (out, &outmap);
+  gst_buffer_unmap (buffer, &inmap);
+
+  /* send out */
+  return gst_audio_decoder_finish_frame (dec, out, 1);
+}
diff --git a/ext/amrwbdec/amrwbdec.h b/ext/amrwbdec/amrwbdec.h
new file mode 100644
index 0000000000000000000000000000000000000000..601b94388a88be434b6ec7d85d499ff88060834e
--- /dev/null
+++ b/ext/amrwbdec/amrwbdec.h
@@ -0,0 +1,69 @@
+/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin
+ * Copyright (C) 2006 Edgard Lima <edgard.lima@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_AMRWBDEC_H__
+#define __GST_AMRWBDEC_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiodecoder.h>
+
+#include <opencore-amrwb/dec_if.h>
+#include <opencore-amrwb/if_rom.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AMRWBDEC			\
+  (gst_amrwbdec_get_type())
+#define GST_AMRWBDEC(obj)						\
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBDEC, GstAmrwbDec))
+#define GST_AMRWBDEC_CLASS(klass)					\
+  (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBDEC, GstAmrwbDecClass))
+#define GST_IS_AMRWBDEC(obj)					\
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBDEC))
+#define GST_IS_AMRWBDEC_CLASS(klass)			\
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBDEC))
+
+typedef struct _GstAmrwbDec GstAmrwbDec;
+typedef struct _GstAmrwbDecClass GstAmrwbDecClass;
+
+/**
+ * GstAmrwbDec:
+ *
+ * Opaque data structure.
+ */
+struct _GstAmrwbDec {
+  GstAudioDecoder element;
+
+  /* library handle */
+  void *handle;
+
+  /* output settings */
+  gint channels, rate;
+};
+
+struct _GstAmrwbDecClass {
+  GstAudioDecoderClass parent_class;
+};
+
+GType gst_amrwbdec_get_type (void);
+GST_ELEMENT_REGISTER_DECLARE (amrwbdec);
+
+G_END_DECLS
+
+#endif /* __GST_AMRWBDEC_H__ */
diff --git a/ext/amrwbdec/meson.build b/ext/amrwbdec/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..ea1778f9849521b0df20da837baa7d9a17d9f5dd
--- /dev/null
+++ b/ext/amrwbdec/meson.build
@@ -0,0 +1,13 @@
+amrwb_dep = dependency('opencore-amrwb', version : '>= 0.1.3', required : get_option('amrwbdec'))
+
+if amrwb_dep.found()
+  amrwbdec = library('gstamrwbdec',
+    ['amrwb.c', 'amrwbdec.c'],
+    c_args : gst_plugins_good_args,
+    include_directories : [configinc],
+    dependencies : [gstaudio_dep, amrwb_dep],
+    install : true,
+    install_dir : plugins_install_dir,
+  )
+  plugins += [amrwbdec]
+endif
diff --git a/ext/flac/gstflacdec.c b/ext/flac/gstflacdec.c
index 49cb1480b2490abcfd7fa5620dbd0ff59ee4fa57..5a9910f2caaa49ecd73cd23ee06160bc505a6a99 100644
--- a/ext/flac/gstflacdec.c
+++ b/ext/flac/gstflacdec.c
@@ -96,7 +96,7 @@ GST_DEBUG_CATEGORY_STATIC (flacdec_debug);
 
 static FLAC__StreamDecoderReadStatus
 gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
-    FLAC__byte buffer[], size_t * bytes, void *client_data);
+    FLAC__byte buffer[], size_t *bytes, void *client_data);
 static FLAC__StreamDecoderWriteStatus
 gst_flac_dec_write_stream (const FLAC__StreamDecoder * decoder,
     const FLAC__Frame * frame,
@@ -387,7 +387,7 @@ gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, const guint8 * data,
   if (data[0] != 0xFF || (data[1] & 0xFC) != 0xF8)
     return FALSE;
 
-  vbs = ! !(data[1] & 1);       /* variable blocksize */
+  vbs = !!(data[1] & 1);        /* variable blocksize */
   bs = (data[2] & 0xF0) >> 4;   /* blocksize marker   */
   sr = (data[2] & 0x0F);        /* samplerate marker  */
   ca = (data[3] & 0xF0) >> 4;   /* channel assignment */
@@ -567,7 +567,7 @@ gst_flac_dec_error_cb (const FLAC__StreamDecoder * d,
 
 static FLAC__StreamDecoderReadStatus
 gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
-    FLAC__byte buffer[], size_t * bytes, void *client_data)
+    FLAC__byte buffer[], size_t *bytes, void *client_data)
 {
   GstFlacDec *dec = GST_FLAC_DEC (client_data);
   guint len;
diff --git a/ext/flac/gstflacenc.c b/ext/flac/gstflacenc.c
index 1993e7d28894acd9ac43ccd281eb601f92931bd1..5378a6578869fc6fb54a68299b4ef7f46ae74025 100644
--- a/ext/flac/gstflacenc.c
+++ b/ext/flac/gstflacenc.c
@@ -102,7 +102,7 @@ static const GstAudioChannelPosition channel_positions[8][8] = {
 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/x-flac")
+    GST_STATIC_CAPS ("audio/x-flac, framed=true")
     );
 
 enum
@@ -484,7 +484,7 @@ static gboolean
 add_cuesheet (const GstToc * toc, guint sample_rate,
     FLAC__StreamMetadata * cuesheet)
 {
-  gint8 track_num = 0;
+  guint8 track_num = 0;
   gint64 start, stop;
   gchar *isrc = NULL;
   const gchar *is_legal;
@@ -687,7 +687,89 @@ gst_flac_enc_set_metadata (GstFlacEnc * flacenc, GstAudioInfo * info,
       gst_buffer_unmap (buffer, &map);
 
       GST_LOG_OBJECT (flacenc, "Setting picture type %d", image_type);
-      flacenc->meta[entries]->data.picture.type = image_type;
+      switch (image_type) {
+        case GST_TAG_IMAGE_TYPE_NONE:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER;
+          break;
+        case GST_TAG_IMAGE_TYPE_FRONT_COVER:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
+          break;
+        case GST_TAG_IMAGE_TYPE_BACK_COVER:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER;
+          break;
+        case GST_TAG_IMAGE_TYPE_LEAFLET_PAGE:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE;
+          break;
+        case GST_TAG_IMAGE_TYPE_MEDIUM:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA;
+          break;
+        case GST_TAG_IMAGE_TYPE_LEAD_ARTIST:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST;
+          break;
+        case GST_TAG_IMAGE_TYPE_ARTIST:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST;
+          break;
+        case GST_TAG_IMAGE_TYPE_CONDUCTOR:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR;
+          break;
+        case GST_TAG_IMAGE_TYPE_BAND_ORCHESTRA:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_BAND;
+          break;
+        case GST_TAG_IMAGE_TYPE_COMPOSER:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER;
+          break;
+        case GST_TAG_IMAGE_TYPE_LYRICIST:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST;
+          break;
+        case GST_TAG_IMAGE_TYPE_RECORDING_LOCATION:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION;
+          break;
+        case GST_TAG_IMAGE_TYPE_DURING_RECORDING:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING;
+          break;
+        case GST_TAG_IMAGE_TYPE_DURING_PERFORMANCE:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE;
+          break;
+        case GST_TAG_IMAGE_TYPE_VIDEO_CAPTURE:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE;
+          break;
+        case GST_TAG_IMAGE_TYPE_FISH:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_FISH;
+          break;
+        case GST_TAG_IMAGE_TYPE_ILLUSTRATION:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION;
+          break;
+        case GST_TAG_IMAGE_TYPE_BAND_ARTIST_LOGO:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE;
+          break;
+        case GST_TAG_IMAGE_TYPE_PUBLISHER_STUDIO_LOGO:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE;
+          break;
+        case GST_TAG_IMAGE_TYPE_UNDEFINED:
+        default:
+          flacenc->meta[entries]->data.picture.type =
+              FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED;
+          break;
+      }
 
       if (width > 0 && height > 0) {
         flacenc->meta[entries]->data.picture.width = width;
@@ -1076,6 +1158,7 @@ gst_flac_enc_process_stream_headers (GstFlacEnc * enc)
       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (enc));
 
   caps = gst_caps_new_simple ("audio/x-flac",
+      "framed", G_TYPE_BOOLEAN, TRUE,
       "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
       "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
 
diff --git a/ext/flac/meson.build b/ext/flac/meson.build
index f8d5cfef74b3ee5dde452f711252beeb2967b7aa..f5786dc390c12e91f97ba7764bdc39c2e99f2511 100644
--- a/ext/flac/meson.build
+++ b/ext/flac/meson.build
@@ -6,7 +6,8 @@ flac_sources = [
   'gstflactag.c',
 ]
 
-flac_dep = dependency('flac', version : '>=1.1.4', required : get_option('flac'))
+flac_dep = dependency('flac', version : '>=1.1.4', required : get_option('flac'),
+                      allow_fallback: true)
 
 if flac_dep.found()
   gstflac = library('gstflac',
diff --git a/ext/gdk_pixbuf/gstgdkpixbufdec.c b/ext/gdk_pixbuf/gstgdkpixbufdec.c
index 5482998c0d60b35ab2425b5e56d1ce23063378fc..de5f05496466117f0d0bb4d22727277ff9abbc0f 100644
--- a/ext/gdk_pixbuf/gstgdkpixbufdec.c
+++ b/ext/gdk_pixbuf/gstgdkpixbufdec.c
@@ -322,7 +322,8 @@ gst_gdk_pixbuf_dec_flush (GstGdkPixbufDec * filter)
 
 
     gst_video_info_init (&info);
-    gst_video_info_set_format (&info, fmt, width, height);
+    if (!gst_video_info_set_format (&info, fmt, width, height))
+      goto format_not_supported;
     info.fps_n = filter->in_fps_n;
     info.fps_d = filter->in_fps_d;
     caps = gst_video_info_to_caps (&info);
@@ -384,6 +385,12 @@ channels_not_supported:
         ("%d channels not supported", n_channels));
     return GST_FLOW_ERROR;
   }
+format_not_supported:
+  {
+    GST_ELEMENT_ERROR (filter, STREAM, DECODE, (NULL),
+        ("%d channels with %dx%d not supported", n_channels, width, height));
+    return GST_FLOW_ERROR;
+  }
 no_buffer:
   {
     GST_DEBUG ("Failed to create outbuffer - %s", gst_flow_get_name (ret));
diff --git a/ext/gdk_pixbuf/gstgdkpixbufsink.c b/ext/gdk_pixbuf/gstgdkpixbufsink.c
index 01e75bb026eccba68e6a6751b5037bb75430c190..56592a7e6a98d1b415beb81afdc5c23614a15f10 100644
--- a/ext/gdk_pixbuf/gstgdkpixbufsink.c
+++ b/ext/gdk_pixbuf/gstgdkpixbufsink.c
@@ -267,7 +267,7 @@ gst_gdk_pixbuf_sink_pixbuf_destroy_notify (guchar * pixels,
 {
   gst_video_frame_unmap (frame);
   gst_buffer_unref (frame->buffer);
-  g_slice_free (GstVideoFrame, frame);
+  g_free (frame);
 }
 
 static GdkPixbuf *
@@ -281,7 +281,7 @@ gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (GstGdkPixbufSink * sink,
   g_return_val_if_fail (GST_VIDEO_SINK_WIDTH (sink) > 0, NULL);
   g_return_val_if_fail (GST_VIDEO_SINK_HEIGHT (sink) > 0, NULL);
 
-  frame = g_slice_new0 (GstVideoFrame);
+  frame = g_new0 (GstVideoFrame, 1);
   gst_video_frame_map (frame, &sink->info, buf, GST_MAP_READ);
 
   bytes_per_pixel = (sink->has_alpha) ? 4 : 3;
diff --git a/ext/gtk/gstgtkbasesink.c b/ext/gtk/gstgtkbasesink.c
index 243522dd4b7fa6ae9fe83b6230bbd8b55be166fe..4eaf6ee72391e9773cf39efcea2162926bfdce6c 100644
--- a/ext/gtk/gstgtkbasesink.c
+++ b/ext/gtk/gstgtkbasesink.c
@@ -203,6 +203,16 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink)
   if (gtk_sink->widget != NULL)
     return g_object_ref (gtk_sink->widget);
 
+  /* Fix segfault when GTK3 and GTK4 are loaded (e.g. `gst-inspect-1.0 -a`)
+   * into the same process.
+   *
+   * GtkNoMediaFile only exists in GTK4 and is registered as part of
+   * gtk_init(). */
+  if (g_type_from_name ("GtkNoMediaFile")) {
+    GST_INFO_OBJECT (gtk_sink, "GTK4 is already initialized");
+    return NULL;
+  }
+
   /* Ensure GTK is initialized, this has no side effect if it was already
    * initialized. Also, we do that lazily, so the application can be first */
   if (!gtk_init_check (NULL, NULL)) {
@@ -405,7 +415,7 @@ gst_gtk_base_sink_start_on_main (GstBaseSink * bsink)
 static gboolean
 gst_gtk_base_sink_start (GstBaseSink * bsink)
 {
-  return ! !gst_gtk_invoke_on_main ((GThreadFunc)
+  return !!gst_gtk_invoke_on_main ((GThreadFunc)
       gst_gtk_base_sink_start_on_main, bsink);
 }
 
@@ -429,7 +439,7 @@ gst_gtk_base_sink_stop (GstBaseSink * bsink)
   GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
 
   if (gst_sink->window)
-    return ! !gst_gtk_invoke_on_main ((GThreadFunc)
+    return !!gst_gtk_invoke_on_main ((GThreadFunc)
         gst_gtk_base_sink_stop_on_main, bsink);
 
   return TRUE;
diff --git a/ext/gtk/gstgtkglsink.c b/ext/gtk/gstgtkglsink.c
index 8c6605359a623e11c7cc446b057cce7146d7823d..c8511a37339113fc767776e4e23122185ba6e7b1 100644
--- a/ext/gtk/gstgtkglsink.c
+++ b/ext/gtk/gstgtkglsink.c
@@ -57,10 +57,12 @@ static GstStaticPadTemplate gst_gtk_gl_sink_template =
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
-        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
+        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA")
+        ", texture-target = { 2D, rectangle }; "
         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
-            GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
+            GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")
+        ", texture-target = { 2D, rectangle }"));
 
 #define gst_gtk_gl_sink_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
diff --git a/ext/gtk/gtkgstbasewidget.c b/ext/gtk/gtkgstbasewidget.c
index ed7cfa66094d683618148a665ed4a8cd99d0b701..96addf2b5e1aab4c806904e0f1267ad63c4636cd 100644
--- a/ext/gtk/gtkgstbasewidget.c
+++ b/ext/gtk/gtkgstbasewidget.c
@@ -323,7 +323,8 @@ _fit_stream_to_allocated_size (GtkGstBaseWidget * base_widget,
     dst.w = allocation->width;
     dst.h = allocation->height;
 
-    gst_video_sink_center_rect (src, dst, result, TRUE);
+    if (base_widget->display_width > 0 && base_widget->display_height > 0)
+      gst_video_sink_center_rect (src, dst, result, TRUE);
   } else {
     result->x = 0;
     result->y = 0;
diff --git a/ext/jack/gstjack.c b/ext/jack/gstjack.c
index 59682bb7b6c53bd410a15f6cfc49de1e5e20042a..89d73dbc04e8592e72cdde9bd9948a50d3aa8b5f 100644
--- a/ext/jack/gstjack.c
+++ b/ext/jack/gstjack.c
@@ -22,6 +22,7 @@
 #endif
 
 #include "gstjack.h"
+#include "gstjackloader.h"
 
 GType
 gst_jack_connect_get_type (void)
@@ -103,6 +104,11 @@ plugin_init (GstPlugin * plugin)
 {
   gboolean ret = FALSE;
 
+  if (!gst_jack_load_library ()) {
+    GST_WARNING ("Failed to load jack library");
+    return TRUE;
+  }
+
   ret |= GST_ELEMENT_REGISTER (jackaudiosrc, plugin);
   ret |= GST_ELEMENT_REGISTER (jackaudiosink, plugin);
 
diff --git a/ext/jack/gstjack.h b/ext/jack/gstjack.h
index 84693f61405e85f9a0c7432a360a23d905741983..5506ba17c5111e51b034274134c31a696bf2ac99 100644
--- a/ext/jack/gstjack.h
+++ b/ext/jack/gstjack.h
@@ -22,8 +22,8 @@
 #ifndef _GST_JACK_H_
 #define _GST_JACK_H_
 
-#include <jack/jack.h>
 #include <gst/audio/audio.h>
+#include "gstjackloader.h"
 
 GST_ELEMENT_REGISTER_DECLARE (jackaudiosrc);
 GST_ELEMENT_REGISTER_DECLARE (jackaudiosink);
diff --git a/ext/jack/gstjackaudioclient.c b/ext/jack/gstjackaudioclient.c
index 5b483a810acaa859a5cedea816748c9118b616b5..fad81b613971c389f9e5dda70de914ad1cc67564 100644
--- a/ext/jack/gstjackaudioclient.c
+++ b/ext/jack/gstjackaudioclient.c
@@ -24,15 +24,34 @@
 #include "gstjackaudioclient.h"
 #include "gstjack.h"
 
-#include <gst/glib-compat-private.h>
-
 GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug);
 #define GST_CAT_DEFAULT gst_jack_audio_client_debug
 
+/* List of threads who are trying to establish an initial server connection.
+ * Poor man's TLS. We don't really want to waste a TLS ID for this, and the
+ * error log callback code path should not be performance sensitive anyway. */
+G_LOCK_DEFINE_STATIC (startups_lock);
+static GList *startups;
+
 static void
 jack_log_error (const gchar * msg)
 {
-  GST_ERROR ("%s", msg);
+  gboolean starting_up;
+
+  G_LOCK (startups_lock);
+  starting_up = startups && g_list_find (startups, g_thread_self ()) != NULL;
+  G_UNLOCK (startups_lock);
+
+  /* Not being able to connect to a JACK server is completely normal in case
+   * jackaudiosink or jackaudiosrc are autoplugged, so we don't want to spew
+   * ERROR log messages in that case. On Linux this is rarely noticable because
+   * pulseaudiosink has a higher rank and is chosen first and alsa comes
+   * alphabetically before jack. */
+  if (starting_up) {
+    GST_WARNING ("%s", msg);
+  } else {
+    GST_ERROR ("%s", msg);
+  }
 }
 
 static void
@@ -47,8 +66,8 @@ gst_jack_audio_client_init (void)
   GST_DEBUG_CATEGORY_INIT (gst_jack_audio_client_debug, "jackclient", 0,
       "jackclient helpers");
 
-  jack_set_error_function (jack_log_error);
-  jack_set_info_function (jack_info_error);
+  gst_jack_set_error_function (jack_log_error);
+  gst_jack_set_info_function (jack_info_error);
 }
 
 /* a list of global connections indexed by id and server. */
@@ -123,7 +142,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
   GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
   GList *walk;
   int res = 0;
-  jack_transport_state_t ts = jack_transport_query (conn->client, NULL);
+  jack_transport_state_t ts = gst_jack_transport_query (conn->client, NULL);
 
   if (ts != conn->cur_ts) {
     conn->cur_ts = ts;
@@ -295,7 +314,7 @@ gst_jack_audio_make_connection (const gchar * id, const gchar * server,
     options |= JackServerName;
   /* open the client */
   if (jclient == NULL)
-    jclient = jack_client_open (id, options, status, server);
+    jclient = gst_jack_client_open (id, options, status, server);
   if (jclient == NULL)
     goto could_not_open;
 
@@ -314,15 +333,15 @@ gst_jack_audio_make_connection (const gchar * id, const gchar * server,
   conn->transport_state = GST_STATE_VOID_PENDING;
 
   /* set our callbacks  */
-  jack_set_process_callback (jclient, jack_process_cb, conn);
+  gst_jack_set_process_callback (jclient, jack_process_cb, conn);
   /* these callbacks cause us to error */
-  jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn);
-  jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn);
-  jack_on_shutdown (jclient, jack_shutdown_cb, conn);
+  gst_jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn);
+  gst_jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn);
+  gst_jack_on_shutdown (jclient, jack_shutdown_cb, conn);
 
   /* all callbacks are set, activate the client */
   GST_INFO ("activate jack_client %p", jclient);
-  if ((res = jack_activate (jclient)))
+  if ((res = gst_jack_activate (jclient)))
     goto could_not_activate;
 
   GST_DEBUG ("opened connection %p", conn);
@@ -417,13 +436,13 @@ gst_jack_audio_unref_connection (GstJackAudioConnection * conn)
      *      jack_process_cb()
      */
     GST_INFO ("deactivate jack_client %p", conn->client);
-    if ((res = jack_deactivate (conn->client))) {
+    if ((res = gst_jack_deactivate (conn->client))) {
       /* we only warn, this means the server is probably shut down and the client
        * is gone anyway. */
       GST_WARNING ("Could not deactivate Jack client (%d)", res);
     }
     /* close connection */
-    if ((res = jack_client_close (conn->client))) {
+    if ((res = gst_jack_client_close (conn->client))) {
       /* we assume the client is gone. */
       GST_WARNING ("close failed (%d)", res);
     }
@@ -504,16 +523,25 @@ gst_jack_audio_client_new (const gchar * id, const gchar * server,
     JackBufferSizeCallback buffer_size, JackSampleRateCallback sample_rate,
     gpointer user_data, jack_status_t * status)
 {
-  GstJackAudioClient *client;
+  GstJackAudioClient *client = NULL;
   GstJackAudioConnection *conn;
 
   g_return_val_if_fail (id != NULL, NULL);
   g_return_val_if_fail (status != NULL, NULL);
 
+  /* So the error log callback knows that we're doing an initial connection
+   * attempt */
+  G_LOCK (startups_lock);
+  startups = g_list_prepend (startups, g_thread_self ());
+  G_UNLOCK (startups_lock);
+
   /* first get a connection for the id/server pair */
   conn = gst_jack_audio_get_connection (id, server, jclient, status);
-  if (conn == NULL)
+
+  if (conn == NULL) {
+    GST_DEBUG ("Could not get server connection (%d)", *status);
     goto no_connection;
+  }
 
   GST_INFO ("new client %s", id);
 
@@ -532,14 +560,14 @@ gst_jack_audio_client_new (const gchar * id, const gchar * server,
   /* add the client to the connection */
   gst_jack_audio_connection_add_client (conn, client);
 
-  return client;
-
-  /* ERRORS */
 no_connection:
-  {
-    GST_DEBUG ("Could not get server connection (%d)", *status);
-    return NULL;
-  }
+
+  /* done connecting */
+  G_LOCK (startups_lock);
+  startups = g_list_remove (startups, g_thread_self ());
+  G_UNLOCK (startups_lock);
+
+  return client;
 }
 
 /**
@@ -664,7 +692,7 @@ gst_jack_audio_client_get_port_names_from_string (jack_client_t * jclient,
     goto invalid;
 
   for (i = 0; i < len; i++) {
-    jack_port_t *port = jack_port_by_name (jclient, p[i]);
+    jack_port_t *port = gst_jack_port_by_name (jclient, p[i]);
     int flags;
 
     if (!port) {
@@ -672,7 +700,7 @@ gst_jack_audio_client_get_port_names_from_string (jack_client_t * jclient,
       goto invalid;
     }
 
-    flags = jack_port_flags (port);
+    flags = gst_jack_port_flags (port);
     if ((flags & port_flags) != port_flags) {
       GST_WARNING ("Port flags 0x%x doesn't match expected flags 0x%x",
           flags, port_flags);
diff --git a/ext/jack/gstjackaudioclient.h b/ext/jack/gstjackaudioclient.h
index 455edd2e78908f02b39f50a2abb1c6172e690f67..c3f3e3868fdcd1f3e8a4f26616e23d26ab043030 100644
--- a/ext/jack/gstjackaudioclient.h
+++ b/ext/jack/gstjackaudioclient.h
@@ -22,9 +22,8 @@
 #ifndef __GST_JACK_AUDIO_CLIENT_H__
 #define __GST_JACK_AUDIO_CLIENT_H__
 
-#include <jack/jack.h>
-
 #include <gst/gst.h>
+#include "gstjackloader.h"
 
 G_BEGIN_DECLS
 
diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c
index aa150378df7554e1d0d2059a983631f77f1e6bb1..c69c6a6e323a26a8c1f58c4bb95ab356ca5d4743 100644
--- a/ext/jack/gstjackaudiosink.c
+++ b/ext/jack/gstjackaudiosink.c
@@ -64,6 +64,7 @@
 
 #include "gstjackaudiosink.h"
 #include "gstjackringbuffer.h"
+#include "gstjackloader.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_sink_debug);
 #define GST_CAT_DEFAULT gst_jack_audio_sink_debug
@@ -77,7 +78,7 @@ gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels)
 
   /* remove ports we don't need */
   while (sink->port_count > channels) {
-    jack_port_unregister (client, sink->ports[--sink->port_count]);
+    gst_jack_port_unregister (client, sink->ports[--sink->port_count]);
   }
 
   /* alloc enough output ports */
@@ -93,7 +94,7 @@ gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels)
         g_strdup_printf ("out_%s_%d", GST_ELEMENT_NAME (sink),
         sink->port_count + 1);
     sink->ports[sink->port_count] =
-        jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE,
+        gst_jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE,
         JackPortIsOutput, 0);
     if (sink->ports[sink->port_count] == NULL)
       return FALSE;
@@ -116,7 +117,7 @@ gst_jack_audio_sink_free_channels (GstJackAudioSink * sink)
   /* get rid of all ports */
   while (sink->port_count) {
     GST_LOG_OBJECT (sink, "unregister port %d", i);
-    if ((res = jack_port_unregister (client, sink->ports[i++]))) {
+    if ((res = gst_jack_port_unregister (client, sink->ports[i++]))) {
       GST_DEBUG_OBJECT (sink, "unregister of port failed (%d)", res);
     }
     sink->port_count--;
@@ -202,7 +203,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
   /* get target buffers */
   for (i = 0; i < channels; i++) {
     sink->buffers[i] =
-        (sample_t *) jack_port_get_buffer (sink->ports[i], nframes);
+        (sample_t *) gst_jack_port_get_buffer (sink->ports[i], nframes);
   }
 
   if (gst_audio_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
@@ -415,7 +416,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
   rate = GST_AUDIO_INFO_RATE (&spec->info);
 
   /* sample rate must be that of the server */
-  sample_rate = jack_get_sample_rate (client);
+  sample_rate = gst_jack_get_sample_rate (client);
   if (sample_rate != rate)
     goto wrong_samplerate;
 
@@ -425,7 +426,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
   if (!gst_jack_audio_sink_allocate_channels (sink, channels))
     goto out_of_ports;
 
-  buffer_size = jack_get_buffer_size (client);
+  buffer_size = gst_jack_get_buffer_size (client);
 
   /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats
    * for all channels  */
@@ -480,10 +481,10 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
 
     if (!available_ports) {
       if (!sink->port_pattern) {
-        jack_ports = jack_get_ports (client, NULL, NULL,
+        jack_ports = gst_jack_get_ports (client, NULL, NULL,
             JackPortIsPhysical | JackPortIsInput);
       } else {
-        jack_ports = jack_get_ports (client, sink->port_pattern, NULL,
+        jack_ports = gst_jack_get_ports (client, sink->port_pattern, NULL,
             JackPortIsInput);
       }
 
@@ -507,19 +508,19 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
         break;
       }
       GST_DEBUG_OBJECT (sink, "try connecting to %s",
-          jack_port_name (sink->ports[i]));
+          gst_jack_port_name (sink->ports[i]));
       /* connect the port to a physical port */
-      res = jack_connect (client,
-          jack_port_name (sink->ports[i]), available_ports[i]);
+      res = gst_jack_connect (client,
+          gst_jack_port_name (sink->ports[i]), available_ports[i]);
       if (res != 0 && res != EEXIST) {
-        jack_free (jack_ports);
+        gst_jack_free (jack_ports);
         g_strfreev (user_ports);
 
         goto cannot_connect;
       }
     }
 
-    jack_free (jack_ports);
+    gst_jack_free (jack_ports);
     g_strfreev (user_ports);
   }
 done:
@@ -609,7 +610,7 @@ gst_jack_ring_buffer_start (GstAudioRingBuffer * buf)
     jack_client_t *client;
 
     client = gst_jack_audio_client_get_client (sink->client);
-    jack_transport_start (client);
+    gst_jack_transport_start (client);
   }
 
   return TRUE;
@@ -628,7 +629,7 @@ gst_jack_ring_buffer_pause (GstAudioRingBuffer * buf)
     jack_client_t *client;
 
     client = gst_jack_audio_client_get_client (sink->client);
-    jack_transport_stop (client);
+    gst_jack_transport_stop (client);
   }
 
   return TRUE;
@@ -647,13 +648,12 @@ gst_jack_ring_buffer_stop (GstAudioRingBuffer * buf)
     jack_client_t *client;
 
     client = gst_jack_audio_client_get_client (sink->client);
-    jack_transport_stop (client);
+    gst_jack_transport_stop (client);
   }
 
   return TRUE;
 }
 
-#if defined (HAVE_JACK_0_120_1) || defined(HAVE_JACK_1_9_7)
 static guint
 gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
 {
@@ -664,7 +664,8 @@ gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
   sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
 
   for (i = 0; i < sink->port_count; i++) {
-    jack_port_get_latency_range (sink->ports[i], JackPlaybackLatency, &range);
+    gst_jack_port_get_latency_range (sink->ports[i], JackPlaybackLatency,
+        &range);
     if (range.max > res)
       res = range.max;
   }
@@ -673,29 +674,6 @@ gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
 
   return res;
 }
-#else /* !(defined (HAVE_JACK_0_120_1) || defined(HAVE_JACK_1_9_7)) */
-static guint
-gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
-{
-  GstJackAudioSink *sink;
-  guint i, res = 0;
-  guint latency;
-  jack_client_t *client;
-
-  sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
-  client = gst_jack_audio_client_get_client (sink->client);
-
-  for (i = 0; i < sink->port_count; i++) {
-    latency = jack_port_get_total_latency (client, sink->ports[i]);
-    if (latency > res)
-      res = latency;
-  }
-
-  GST_LOG_OBJECT (sink, "delay %u", res);
-
-  return res;
-}
-#endif
 
 static GstStaticPadTemplate jackaudiosink_sink_factory =
 GST_STATIC_PAD_TEMPLATE ("sink",
@@ -1039,11 +1017,11 @@ gst_jack_audio_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
 
     /* get a port count, this is the number of channels we can automatically
      * connect. */
-    ports = jack_get_ports (client, NULL, NULL,
+    ports = gst_jack_get_ports (client, NULL, NULL,
         JackPortIsPhysical | JackPortIsInput);
     if (ports != NULL) {
       for (; ports[max]; max++);
-      jack_free (ports);
+      gst_jack_free (ports);
     } else
       max = 0;
   } else {
@@ -1059,7 +1037,7 @@ found:
     min = MIN (1, max);
   }
 
-  rate = jack_get_sample_rate (client);
+  rate = gst_jack_get_sample_rate (client);
 
   GST_DEBUG_OBJECT (sink, "got %d-%d ports, samplerate: %d", min, max, rate);
 
diff --git a/ext/jack/gstjackaudiosink.h b/ext/jack/gstjackaudiosink.h
index 088289de46b16f46c84358d17f9fb6df3fe62f0f..4169a2b2362d9834335029706175c829c3f4765e 100644
--- a/ext/jack/gstjackaudiosink.h
+++ b/ext/jack/gstjackaudiosink.h
@@ -22,13 +22,12 @@
 #ifndef __GST_JACK_AUDIO_SINK_H__
 #define __GST_JACK_AUDIO_SINK_H__
 
-#include <jack/jack.h>
-
 #include <gst/gst.h>
 #include <gst/audio/gstaudiobasesink.h>
 
 #include "gstjack.h"
 #include "gstjackaudioclient.h"
+#include "gstjackloader.h"
 
 G_BEGIN_DECLS
 
diff --git a/ext/jack/gstjackaudiosrc.c b/ext/jack/gstjackaudiosrc.c
index c3e9f11c05c266940b1e630721df82642c581fc8..302b9b10e5110108fa6d3e5412f1bc0ec896740c 100644
--- a/ext/jack/gstjackaudiosrc.c
+++ b/ext/jack/gstjackaudiosrc.c
@@ -85,6 +85,7 @@
 #include "gstjackaudiosrc.h"
 #include "gstjackringbuffer.h"
 #include "gstjackutil.h"
+#include "gstjackloader.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_src_debug);
 #define GST_CAT_DEFAULT gst_jack_audio_src_debug
@@ -98,7 +99,7 @@ gst_jack_audio_src_allocate_channels (GstJackAudioSrc * src, gint channels)
 
   /* remove ports we don't need */
   while (src->port_count > channels)
-    jack_port_unregister (client, src->ports[--src->port_count]);
+    gst_jack_port_unregister (client, src->ports[--src->port_count]);
 
   /* alloc enough input ports */
   src->ports = g_realloc (src->ports, sizeof (jack_port_t *) * channels);
@@ -113,7 +114,7 @@ gst_jack_audio_src_allocate_channels (GstJackAudioSrc * src, gint channels)
         g_strdup_printf ("in_%s_%d", GST_ELEMENT_NAME (src),
         src->port_count + 1);
     src->ports[src->port_count] =
-        jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE,
+        gst_jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE,
         JackPortIsInput, 0);
     if (src->ports[src->port_count] == NULL)
       return FALSE;
@@ -136,7 +137,7 @@ gst_jack_audio_src_free_channels (GstJackAudioSrc * src)
   /* get rid of all ports */
   while (src->port_count) {
     GST_LOG_OBJECT (src, "unregister port %d", i);
-    if ((res = jack_port_unregister (client, src->ports[i++])))
+    if ((res = gst_jack_port_unregister (client, src->ports[i++])))
       GST_DEBUG_OBJECT (src, "unregister of port failed (%d)", res);
 
     src->port_count--;
@@ -220,7 +221,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
   /* get input buffers */
   for (i = 0; i < channels; i++)
     src->buffers[i] =
-        (sample_t *) jack_port_get_buffer (src->ports[i], nframes);
+        (sample_t *) gst_jack_port_get_buffer (src->ports[i], nframes);
 
   if (gst_audio_ring_buffer_prepare_read (buf, &writeseg, &writeptr, &len)) {
     flen = len / channels;
@@ -421,7 +422,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
   rate = GST_AUDIO_INFO_RATE (&spec->info);
 
   /* sample rate must be that of the server */
-  sample_rate = jack_get_sample_rate (client);
+  sample_rate = gst_jack_get_sample_rate (client);
   if (sample_rate != rate)
     goto wrong_samplerate;
 
@@ -433,7 +434,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
 
   gst_jack_set_layout (buf, spec);
 
-  buffer_size = jack_get_buffer_size (client);
+  buffer_size = gst_jack_get_buffer_size (client);
 
   /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats
    * for all channels  */
@@ -489,12 +490,13 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
 
     if (!available_ports) {
       if (!src->port_pattern) {
-        jack_ports = jack_get_ports (client, NULL, NULL,
+        jack_ports = gst_jack_get_ports (client, NULL, NULL,
             JackPortIsPhysical | JackPortIsOutput);
       } else {
-        jack_ports = jack_get_ports (client, src->port_pattern, NULL,
+        jack_ports = gst_jack_get_ports (client, src->port_pattern, NULL,
             JackPortIsOutput);
       }
+      available_ports = jack_ports;
     }
 
     if (!available_ports) {
@@ -514,20 +516,20 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
         break;
       }
       GST_DEBUG_OBJECT (src, "try connecting to %s",
-          jack_port_name (src->ports[i]));
+          gst_jack_port_name (src->ports[i]));
 
       /* connect the physical port to a port */
-      res = jack_connect (client,
-          available_ports[i], jack_port_name (src->ports[i]));
+      res = gst_jack_connect (client,
+          available_ports[i], gst_jack_port_name (src->ports[i]));
       if (res != 0 && res != EEXIST) {
-        jack_free (jack_ports);
+        gst_jack_free (jack_ports);
         g_strfreev (user_ports);
 
         goto cannot_connect;
       }
     }
 
-    jack_free (jack_ports);
+    gst_jack_free (jack_ports);
     g_strfreev (user_ports);
   }
 done:
@@ -617,7 +619,7 @@ gst_jack_ring_buffer_start (GstAudioRingBuffer * buf)
     jack_client_t *client;
 
     client = gst_jack_audio_client_get_client (src->client);
-    jack_transport_start (client);
+    gst_jack_transport_start (client);
   }
 
   return TRUE;
@@ -636,7 +638,7 @@ gst_jack_ring_buffer_pause (GstAudioRingBuffer * buf)
     jack_client_t *client;
 
     client = gst_jack_audio_client_get_client (src->client);
-    jack_transport_stop (client);
+    gst_jack_transport_stop (client);
   }
 
   return TRUE;
@@ -655,13 +657,12 @@ gst_jack_ring_buffer_stop (GstAudioRingBuffer * buf)
     jack_client_t *client;
 
     client = gst_jack_audio_client_get_client (src->client);
-    jack_transport_stop (client);
+    gst_jack_transport_stop (client);
   }
 
   return TRUE;
 }
 
-#if defined (HAVE_JACK_0_120_1) || defined(HAVE_JACK_1_9_7)
 static guint
 gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
 {
@@ -672,7 +673,7 @@ gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
   src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf));
 
   for (i = 0; i < src->port_count; i++) {
-    jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range);
+    gst_jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range);
     if (range.max > res)
       res = range.max;
   }
@@ -681,30 +682,6 @@ gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
 
   return res;
 }
-#else /* !(defined (HAVE_JACK_0_120_1) || defined(HAVE_JACK_1_9_7)) */
-static guint
-gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf)
-{
-  GstJackAudioSrc *src;
-  guint i, res = 0;
-  guint latency;
-  jack_client_t *client;
-
-  src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf));
-
-  client = gst_jack_audio_client_get_client (src->client);
-
-  for (i = 0; i < src->port_count; i++) {
-    latency = jack_port_get_total_latency (client, src->ports[i]);
-    if (latency > res)
-      res = latency;
-  }
-
-  GST_DEBUG_OBJECT (src, "delay %u", res);
-
-  return res;
-}
-#endif
 
 /* Audiosrc signals and args */
 enum
@@ -1050,12 +1027,12 @@ gst_jack_audio_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
 
     /* get a port count, this is the number of channels we can automatically
      * connect. */
-    ports = jack_get_ports (client, NULL, NULL,
+    ports = gst_jack_get_ports (client, NULL, NULL,
         JackPortIsPhysical | JackPortIsOutput);
     if (ports != NULL) {
       for (; ports[max]; max++);
 
-      jack_free (ports);
+      gst_jack_free (ports);
     } else
       max = 0;
   } else {
@@ -1071,7 +1048,7 @@ found:
     min = MIN (1, max);
   }
 
-  rate = jack_get_sample_rate (client);
+  rate = gst_jack_get_sample_rate (client);
 
   GST_DEBUG_OBJECT (src, "got %d-%d ports, samplerate: %d", min, max, rate);
 
diff --git a/ext/jack/gstjackaudiosrc.h b/ext/jack/gstjackaudiosrc.h
index 3657c60ceade3ff6f01e86d7fcc6ce380fc4a453..98c31e095f4bf000102a3d5446ea3b20bec2162b 100644
--- a/ext/jack/gstjackaudiosrc.h
+++ b/ext/jack/gstjackaudiosrc.h
@@ -43,13 +43,12 @@
 #ifndef __GST_JACK_AUDIO_SRC_H__
 #define __GST_JACK_AUDIO_SRC_H__
 
-#include <jack/jack.h>
-
 #include <gst/gst.h>
 #include <gst/audio/gstaudiosrc.h>
 
 #include "gstjackaudioclient.h"
 #include "gstjack.h"
+#include "gstjackloader.h"
 
 G_BEGIN_DECLS
 
diff --git a/ext/jack/gstjackloader.c b/ext/jack/gstjackloader.c
new file mode 100644
index 0000000000000000000000000000000000000000..cbbf716251c57c3caa862ed1ee3d7c5e1ce108ec
--- /dev/null
+++ b/ext/jack/gstjackloader.c
@@ -0,0 +1,472 @@
+/* GStreamer
+ * Copyright (C) 2023 Jordan Petridis <jordan@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstjackloader.h"
+#include <stdio.h>
+#include <gmodule.h>
+
+#ifdef __APPLE__
+#define JACK_LIBNAME "libjack.0.dylib"
+#elif defined(G_OS_WIN32)
+#ifdef _WIN64
+#define JACK_LIBNAME "libjack64.dll"
+#else
+#define JACK_LIBNAME "libjack.dll"
+#endif /* End ifdef _WIN64 */
+#else /* End ifdef G_OS_WIN32 */
+#define JACK_LIBNAME "libjack.so.0"
+#endif
+
+#define LOAD_SYMBOL(name,func,mandatory) G_STMT_START { \
+  if (!g_module_symbol (module, G_STRINGIFY (name), (gpointer *) &vtable->func)) { \
+    if (mandatory) { \
+      GST_ERROR ("Failed to load '%s' from %s, %s", G_STRINGIFY (name), filename, g_module_error()); \
+      goto error; \
+    } \
+    GST_WARNING ("Failed to load '%s' from %s, %s", G_STRINGIFY (name), filename, g_module_error()); \
+  } \
+} G_STMT_END;
+
+typedef struct _GstJackVTable
+{
+  gboolean loaded;
+
+  gint major_version;
+  gint minor_version;
+  gint micro_version;
+
+  const char *(*GstJackGetVersionString) (void);
+
+  jack_client_t *(*GstJackClientOpen) (const char *client_name,
+      jack_options_t options, jack_status_t * status, ...);
+
+  jack_client_t *(*GstJackClientNew) (const char *client_name);
+
+  int (*GstJackClientClose) (jack_client_t * client);
+
+  int (*GstJackActivate) (jack_client_t * client);
+
+  int (*GstJackDeactivate) (jack_client_t * client);
+
+  void (*GstJackOnShutdown) (jack_client_t * client,
+      JackShutdownCallback shutdown_callback, void *arg);
+
+  int (*GstJackSetProcessCallback) (jack_client_t * client,
+      JackProcessCallback process_callback, void *arg);
+
+
+  int (*GstJackSetBufferSizeCallback) (jack_client_t * client,
+      JackBufferSizeCallback bufsize_callback, void *arg);
+
+  int (*GstJackSetSampleRateCallback) (jack_client_t * client,
+      JackSampleRateCallback srate_callback, void *arg);
+
+  int (*GstJackSetBufferSize) (jack_client_t * client, jack_nframes_t nframes);
+
+    jack_nframes_t (*GstJackGetSampleRate) (jack_client_t *);
+
+    jack_nframes_t (*GstJackGetBufferSize) (jack_client_t *);
+
+  jack_port_t *(*GstJackPortRegister) (jack_client_t * client,
+      const char *port_name,
+      const char *port_type, unsigned long flags, unsigned long buffer_size);
+
+  int (*GstJackPortUnregister) (jack_client_t * client, jack_port_t * port);
+
+  void *(*GstJackPortGetBuffer) (jack_port_t * port, jack_nframes_t nframes);
+
+  const char *(*GstJackPortName) (const jack_port_t * port);
+
+  int (*GstJackPortFlags) (const jack_port_t * port);
+
+  int (*GstJackConnect) (jack_client_t * client,
+      const char *source_port, const char *destination_port);
+
+  void (*GstJackPortGetLatencyRange) (jack_port_t * port,
+      jack_latency_callback_mode_t mode, jack_latency_range_t * range);
+
+  const char **(*GstJackGetPorts) (jack_client_t * client,
+      const char *port_name_pattern,
+      const char *type_name_pattern, unsigned long flags);
+
+  jack_port_t *(*GstJackPortByName) (jack_client_t * client,
+      const char *port_name);
+
+  void (*GstJackSetErrorFunction) (void (*func) (const char *));
+
+  void (*GstJackSetInfoFunction) (void (*func) (const char *));
+
+  void (*GstJackFree) (void *ptr);
+
+  void (*GstJackTransportStart) (jack_client_t * client);
+
+  void (*GstJackTransportStop) (jack_client_t * client);
+
+    jack_transport_state_t (*GstJackTransportQuery) (const jack_client_t *
+      client, jack_position_t * pos);
+
+} GstJackVTable;
+
+static GstJackVTable gst_jack_vtable = { 0, };
+
+static const char *
+gst_jack_get_version_string (void)
+{
+  g_assert (gst_jack_vtable.GstJackGetVersionString != NULL);
+
+  const char *ret = gst_jack_vtable.GstJackGetVersionString ();
+  g_assert (ret != NULL);
+  return ret;
+}
+
+static gboolean
+gst_jack_check_api_version (void)
+{
+  /* hardcoded minimum supported version */
+  gint supported_major_ver = 1;
+  gint minimum_minor_ver = 9;
+  gint minimum_micro_ver = 7;
+
+  const char *jack_version = gst_jack_get_version_string ();
+
+  if (jack_version == NULL || *jack_version == '\0') {
+    GST_ERROR ("No JACK version string");
+    return FALSE;
+  }
+
+  GST_INFO ("Checking JACK client library version: %s", jack_version);
+
+  if (strstr (jack_version, "PipeWire")) {
+    GST_INFO ("Using Pipewire as the Jack server: %s", jack_version);
+  } else {
+    int major, minor, micro;
+    if (sscanf (jack_version, "%u.%u.%u", &major, &minor, &micro) == 3 &&
+        major == supported_major_ver && ((minor == minimum_minor_ver
+                && micro >= minimum_micro_ver) || minor > minimum_minor_ver)) {
+      GST_INFO ("Compatible Jack Server version: %s", jack_version);
+    } else {
+      GST_ERROR ("Unsupported Jack version: %s", jack_version);
+    }
+  }
+
+  return TRUE;
+}
+
+gboolean
+gst_jack_load_library (void)
+{
+  GModule *module;
+  const gchar *filename = JACK_LIBNAME;
+  GstJackVTable *vtable;
+
+  if (gst_jack_vtable.loaded)
+    return TRUE;
+
+  module = g_module_open (filename, G_MODULE_BIND_LAZY);
+  if (module == NULL) {
+    GST_WARNING ("Could not open library %s, %s", filename, g_module_error ());
+    return FALSE;
+  }
+
+  vtable = &gst_jack_vtable;
+  LOAD_SYMBOL (jack_get_version_string, GstJackGetVersionString, TRUE);
+
+  if (!gst_jack_check_api_version ())
+    goto error;
+
+  LOAD_SYMBOL (jack_client_open, GstJackClientOpen, TRUE);
+  LOAD_SYMBOL (jack_client_new, GstJackClientNew, TRUE);
+  LOAD_SYMBOL (jack_client_close, GstJackClientClose, TRUE);
+  LOAD_SYMBOL (jack_activate, GstJackActivate, TRUE);
+  LOAD_SYMBOL (jack_deactivate, GstJackDeactivate, TRUE);
+  LOAD_SYMBOL (jack_on_shutdown, GstJackOnShutdown, TRUE);
+  LOAD_SYMBOL (jack_set_process_callback, GstJackSetProcessCallback, TRUE);
+  LOAD_SYMBOL (jack_set_buffer_size_callback, GstJackSetBufferSizeCallback,
+      TRUE);
+  LOAD_SYMBOL (jack_set_sample_rate_callback, GstJackSetSampleRateCallback,
+      TRUE);
+  LOAD_SYMBOL (jack_set_buffer_size, GstJackSetBufferSize, TRUE);
+  LOAD_SYMBOL (jack_get_sample_rate, GstJackGetSampleRate, TRUE);
+  LOAD_SYMBOL (jack_get_buffer_size, GstJackGetBufferSize, TRUE);
+  LOAD_SYMBOL (jack_port_register, GstJackPortRegister, TRUE);
+  LOAD_SYMBOL (jack_port_unregister, GstJackPortUnregister, TRUE);
+  LOAD_SYMBOL (jack_port_get_buffer, GstJackPortGetBuffer, TRUE);
+  LOAD_SYMBOL (jack_port_name, GstJackPortName, TRUE);
+  LOAD_SYMBOL (jack_port_flags, GstJackPortFlags, TRUE);
+  LOAD_SYMBOL (jack_connect, GstJackConnect, TRUE);
+  LOAD_SYMBOL (jack_port_get_latency_range, GstJackPortGetLatencyRange, TRUE);
+  LOAD_SYMBOL (jack_get_ports, GstJackGetPorts, TRUE);
+  LOAD_SYMBOL (jack_port_by_name, GstJackPortByName, TRUE);
+  LOAD_SYMBOL (jack_set_error_function, GstJackSetErrorFunction, TRUE);
+  LOAD_SYMBOL (jack_set_info_function, GstJackSetInfoFunction, TRUE);
+  LOAD_SYMBOL (jack_free, GstJackFree, TRUE);
+  LOAD_SYMBOL (jack_transport_start, GstJackTransportStart, TRUE);
+  LOAD_SYMBOL (jack_transport_stop, GstJackTransportStop, TRUE);
+  LOAD_SYMBOL (jack_transport_query, GstJackTransportQuery, TRUE);
+
+  vtable->loaded = TRUE;
+
+  return TRUE;
+
+error:
+  g_module_close (module);
+
+  return FALSE;
+}
+
+#define _gst_jack_client_open(client_name,options,status,...) (gst_jack_vtable.GstJackClientOpen(client_name, options, status, ##__VA_ARGS__))
+
+jack_client_t *
+gst_jack_client_open (const char *client_name,
+    jack_options_t options, jack_status_t * status, ...)
+{
+  g_assert (gst_jack_vtable.GstJackClientOpen != NULL);
+
+  return _gst_jack_client_open (client_name, options, status);
+};
+
+jack_client_t *
+gst_jack_client_new (const char *client_name)
+{
+  g_assert (gst_jack_vtable.GstJackClientNew != NULL);
+
+  return gst_jack_vtable.GstJackClientNew (client_name);
+}
+
+int
+gst_jack_client_close (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackClientClose != NULL);
+
+  return gst_jack_vtable.GstJackClientClose (client);
+}
+
+int
+gst_jack_activate (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackActivate != NULL);
+
+  return gst_jack_vtable.GstJackActivate (client);
+};
+
+int
+gst_jack_deactivate (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackDeactivate != NULL);
+
+  return gst_jack_vtable.GstJackDeactivate (client);
+};
+
+
+void
+gst_jack_on_shutdown (jack_client_t * client,
+    JackShutdownCallback shutdown_callback, void *arg)
+{
+  g_assert (gst_jack_vtable.GstJackOnShutdown != NULL);
+
+  gst_jack_vtable.GstJackOnShutdown (client, shutdown_callback, arg);
+};
+
+int
+gst_jack_set_process_callback (jack_client_t * client,
+    JackProcessCallback process_callback, void *arg)
+{
+  g_assert (gst_jack_vtable.GstJackSetProcessCallback != NULL);
+
+  return gst_jack_vtable.GstJackSetProcessCallback (client, process_callback,
+      arg);
+};
+
+
+int
+gst_jack_set_buffer_size_callback (jack_client_t * client,
+    JackBufferSizeCallback bufsize_callback, void *arg)
+{
+  g_assert (gst_jack_vtable.GstJackSetBufferSizeCallback != NULL);
+
+  return gst_jack_vtable.GstJackSetBufferSizeCallback (client, bufsize_callback,
+      arg);
+};
+
+int
+gst_jack_set_sample_rate_callback (jack_client_t * client,
+    JackSampleRateCallback srate_callback, void *arg)
+{
+  g_assert (gst_jack_vtable.GstJackSetSampleRateCallback != NULL);
+
+  return gst_jack_vtable.GstJackSetSampleRateCallback (client, srate_callback,
+      arg);
+};
+
+int
+gst_jack_set_buffer_size (jack_client_t * client, jack_nframes_t nframes)
+{
+  g_assert (gst_jack_vtable.GstJackSetBufferSize != NULL);
+
+  return gst_jack_vtable.GstJackSetBufferSize (client, nframes);
+};
+
+jack_nframes_t
+gst_jack_get_sample_rate (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackGetSampleRate != NULL);
+
+  return gst_jack_vtable.GstJackGetSampleRate (client);
+};
+
+jack_nframes_t
+gst_jack_get_buffer_size (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackGetBufferSize != NULL);
+
+  return gst_jack_vtable.GstJackGetBufferSize (client);
+};
+
+jack_port_t *
+gst_jack_port_register (jack_client_t * client,
+    const char *port_name,
+    const char *port_type, unsigned long flags, unsigned long buffer_size)
+{
+  g_assert (gst_jack_vtable.GstJackPortRegister != NULL);
+
+  return gst_jack_vtable.GstJackPortRegister (client, port_name, port_type,
+      flags, buffer_size);
+
+};
+
+int
+gst_jack_port_unregister (jack_client_t * client, jack_port_t * port)
+{
+  g_assert (gst_jack_vtable.GstJackPortUnregister != NULL);
+
+  return gst_jack_vtable.GstJackPortUnregister (client, port);
+};
+
+void *
+gst_jack_port_get_buffer (jack_port_t * port, jack_nframes_t nframes)
+{
+  g_assert (gst_jack_vtable.GstJackPortGetBuffer != NULL);
+
+  return gst_jack_vtable.GstJackPortGetBuffer (port, nframes);
+};
+
+const char *
+gst_jack_port_name (const jack_port_t * port)
+{
+  g_assert (gst_jack_vtable.GstJackPortName != NULL);
+
+  return gst_jack_vtable.GstJackPortName (port);
+};
+
+int
+gst_jack_port_flags (const jack_port_t * port)
+{
+  g_assert (gst_jack_vtable.GstJackPortFlags != NULL);
+
+  return gst_jack_vtable.GstJackPortFlags (port);
+};
+
+int
+gst_jack_connect (jack_client_t * client,
+    const char *source_port, const char *destination_port)
+{
+  g_assert (gst_jack_vtable.GstJackConnect != NULL);
+
+  return gst_jack_vtable.GstJackConnect (client, source_port, destination_port);
+};
+
+void
+gst_jack_port_get_latency_range (jack_port_t * port,
+    jack_latency_callback_mode_t mode, jack_latency_range_t * range)
+{
+  g_assert (gst_jack_vtable.GstJackPortGetLatencyRange != NULL);
+
+  gst_jack_vtable.GstJackPortGetLatencyRange (port, mode, range);
+};
+
+const char **
+gst_jack_get_ports (jack_client_t * client,
+    const char *port_name_pattern,
+    const char *type_name_pattern, unsigned long flags)
+{
+  g_assert (gst_jack_vtable.GstJackGetPorts != NULL);
+
+  return gst_jack_vtable.GstJackGetPorts (client, port_name_pattern,
+      type_name_pattern, flags);
+};
+
+jack_port_t *
+gst_jack_port_by_name (jack_client_t * client, const char *port_name)
+{
+  g_assert (gst_jack_vtable.GstJackPortByName != NULL);
+
+  return gst_jack_vtable.GstJackPortByName (client, port_name);
+};
+
+void
+gst_jack_set_error_function (void (*func) (const char *))
+{
+  g_assert (gst_jack_vtable.GstJackSetErrorFunction != NULL);
+
+  gst_jack_vtable.GstJackSetErrorFunction (func);
+};
+
+void
+gst_jack_set_info_function (void (*func) (const char *))
+{
+  g_assert (gst_jack_vtable.GstJackSetInfoFunction != NULL);
+
+  gst_jack_vtable.GstJackSetInfoFunction (func);
+};
+
+void
+gst_jack_free (void *ptr)
+{
+  g_assert (gst_jack_vtable.GstJackFree != NULL);
+
+  gst_jack_vtable.GstJackFree (ptr);
+}
+
+void
+gst_jack_transport_start (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackTransportStart != NULL);
+
+  gst_jack_vtable.GstJackTransportStart (client);
+};
+
+void
+gst_jack_transport_stop (jack_client_t * client)
+{
+  g_assert (gst_jack_vtable.GstJackTransportStop != NULL);
+
+  gst_jack_vtable.GstJackTransportStop (client);
+};
+
+jack_transport_state_t
+gst_jack_transport_query (const jack_client_t * client, jack_position_t * pos)
+{
+  g_assert (gst_jack_vtable.GstJackTransportQuery != NULL);
+
+  return gst_jack_vtable.GstJackTransportQuery (client, pos);
+};
diff --git a/ext/jack/gstjackloader.h b/ext/jack/gstjackloader.h
new file mode 100644
index 0000000000000000000000000000000000000000..41cf8bce0718abb33c6b3bb6e3244a8df136707a
--- /dev/null
+++ b/ext/jack/gstjackloader.h
@@ -0,0 +1,207 @@
+/* Jack
+
+  Jack definitions copied from:
+    - jack/jack.h
+    - jack/types.h
+    - jack/transport.h
+
+  Copyright (C) 2001 Paul Davis
+  Copyright (C) 2003 Jack O'Quin
+  Copyright (C) 2004 Jack O'Quin
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  This program 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 Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _GST_JACK_WRAPPER_H_
+#define _GST_JACK_WRAPPER_H_
+
+#include <stdint.h>
+#include <gst/gst.h>
+#include "gstjackloader.h"
+
+G_BEGIN_DECLS
+
+typedef uint32_t                   jack_nframes_t;
+typedef struct _jack_client        jack_client_t;
+typedef struct _jack_port          jack_port_t;
+typedef struct _jack_position      jack_position_t;
+typedef struct _jack_latency_range jack_latency_range_t;
+
+typedef void (* JackShutdownCallback)   (void *arg);
+typedef int  (* JackProcessCallback)    (jack_nframes_t nframes, void *arg);
+typedef int  (* JackBufferSizeCallback) (jack_nframes_t nframes, void *arg);
+typedef int  (* JackSampleRateCallback) (jack_nframes_t nframes, void *arg);
+
+#define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
+
+struct _jack_latency_range
+ {
+     jack_nframes_t min;
+     jack_nframes_t max;
+ };
+
+enum JackLatencyCallbackMode {
+    JackCaptureLatency,
+    JackPlaybackLatency
+};
+
+typedef enum JackLatencyCallbackMode jack_latency_callback_mode_t;
+
+enum JackOptions {
+     JackNullOption = 0x00,
+     JackNoStartServer = 0x01,
+     JackUseExactName = 0x02,
+     JackServerName = 0x04,
+     JackLoadName = 0x08,
+     JackLoadInit = 0x10,
+     JackSessionID = 0x20
+};
+
+typedef enum JackOptions jack_options_t;
+
+enum JackStatus {
+    JackFailure = 0x01,
+    JackInvalidOption = 0x02,
+    JackNameNotUnique = 0x04,
+    JackServerStarted = 0x08,
+    JackServerFailed = 0x10,
+    JackServerError = 0x20,
+    JackNoSuchClient = 0x40,
+    JackLoadFailure = 0x80,
+    JackInitFailure = 0x100,
+    JackShmFailure = 0x200,
+    JackVersionError = 0x400,
+    /*
+    * BackendError
+    */
+    JackBackendError = 0x800,
+    /*
+    * Client is being shutdown against its will
+    */
+    JackClientZombie = 0x1000
+};
+
+typedef enum JackStatus jack_status_t;
+
+typedef float jack_default_audio_sample_t;
+
+ enum JackPortFlags {
+      JackPortIsInput = 0x1,
+      JackPortIsOutput = 0x2,
+      JackPortIsPhysical = 0x4,
+      JackPortCanMonitor = 0x8,
+      JackPortIsTerminal = 0x10
+ };
+
+typedef enum {
+    /* the order matters for binary compatibility */
+    JackTransportStopped = 0,       /* Transport halted */
+    JackTransportRolling = 1,       /* Transport playing */
+    JackTransportLooping = 2,       /* For OLD_TRANSPORT, now ignored */
+    JackTransportStarting = 3,      /* Waiting for sync ready */
+    JackTransportNetStarting = 4,   /* Waiting for sync ready on the network*/
+} jack_transport_state_t;
+
+
+gboolean gst_jack_load_library (void);
+
+//
+// jack/jack.h
+//
+
+jack_client_t * gst_jack_client_open          (const char *client_name,
+                                               jack_options_t options,
+                                               jack_status_t *status, ...);
+
+jack_client_t * gst_jack_client_new           (const char *client_name) ;
+
+int             gst_jack_client_close         (jack_client_t *client);
+
+int             gst_jack_activate             (jack_client_t *client);
+
+int             gst_jack_deactivate           (jack_client_t *client);
+
+void            gst_jack_on_shutdown          (jack_client_t * client,
+                                               JackShutdownCallback shutdown_callback,
+                                               void *arg);
+
+int             gst_jack_set_process_callback    (jack_client_t * client,
+                                                  JackProcessCallback process_callback,
+                                                  void *arg);
+
+
+int            gst_jack_set_buffer_size_callback (jack_client_t *client,
+                                                  JackBufferSizeCallback bufsize_callback,
+                                                  void *arg);
+
+int            gst_jack_set_sample_rate_callback (jack_client_t * client,
+                                                  JackSampleRateCallback srate_callback, void *arg);
+
+int gst_jack_set_buffer_size (jack_client_t * client, jack_nframes_t nframes);
+
+jack_nframes_t gst_jack_get_sample_rate (jack_client_t * client);
+
+jack_nframes_t gst_jack_get_buffer_size (jack_client_t * client);
+
+jack_port_t *  gst_jack_port_register   (jack_client_t *client,
+                                         const char *port_name,
+                                         const char *port_type,
+                                         unsigned long flags,
+                                         unsigned long buffer_size);
+
+int            gst_jack_port_unregister (jack_client_t *client, jack_port_t *port);
+
+
+void * gst_jack_port_get_buffer (jack_port_t* port, jack_nframes_t nframes);
+
+const char * gst_jack_port_name (const jack_port_t* port);
+
+int gst_jack_port_flags (const jack_port_t*port);
+
+int gst_jack_connect (jack_client_t * client,
+                      const char *source_port,
+                      const char *destination_port);
+
+void gst_jack_port_get_latency_range (jack_port_t* port,
+                                      jack_latency_callback_mode_t mode,
+                                      jack_latency_range_t * range);
+
+const char **  gst_jack_get_ports              (jack_client_t *client,
+                                                const char *port_name_pattern,
+                                                const char *type_name_pattern,
+                                                unsigned long flags);
+
+jack_port_t *  gst_jack_port_by_name           (jack_client_t * client,
+                                                const char *port_name);
+
+void gst_jack_set_error_function (void (*func)(const char *));
+
+void gst_jack_set_info_function (void (*func)(const char *));
+
+void gst_jack_free (void* ptr);
+
+//
+// jack/transport.h
+//
+
+void                   gst_jack_transport_start (jack_client_t *client);
+
+void                   gst_jack_transport_stop  (jack_client_t *client);
+
+jack_transport_state_t gst_jack_transport_query (const jack_client_t *client,
+                                                 jack_position_t *pos);
+
+G_END_DECLS
+#endif  // _GST_JACK_UTIL_H_
diff --git a/ext/jack/meson.build b/ext/jack/meson.build
index 756cd3be334e6638db13f0db5988f129d08e9963..f5780e266bd4d42cb267b1f3140692030a6b307c 100644
--- a/ext/jack/meson.build
+++ b/ext/jack/meson.build
@@ -4,6 +4,7 @@ jack_sources = [
   'gstjackaudiosrc.c',
   'gstjack.c',
   'gstjackutil.c',
+  'gstjackloader.c',
 ]
 
 jack_option = get_option('jack')
@@ -11,90 +12,11 @@ if jack_option.disabled()
   subdir_done()
 endif
 
-jack_incdirs = [configinc, libsinc]
-
-libjack_dep = dependency('jack', version : '>= 1.9.7', required : false)
-
-if not libjack_dep.found()
-  fs = import('fs')
-  host_cpu = host_machine.cpu_family()
-  jack_maybe_installed = false
-  error_msg = '"jack" option is enabled but '
-  if (host_system == 'windows' and build_machine.system() == 'windows')
-    # Need to detect whether we're running on 64-bit Windows or not.
-    # If `C:/Program Files (x86)/` exists, we're running on 64-bit Windows, and
-    # C:/Program Files/ contains 64-bit programs. Else, we're on 32-bit Windows
-    # and C:/Program Files/ contains 32-bit programs.
-    #
-    # The user could either have a 32-bit JACK installation or a 64-bit one.
-    # When building for 32-bit x86, we need to check for both.
-    if fs.is_dir('C:/Program Files (x86)')
-      jack64_install_dir = 'C:/Program Files/JACK2'
-      jack32_install_dir = 'C:/Program Files (x86)/JACK2'
-    else
-      jack64_install_dir = ''
-      jack32_install_dir = 'C:/Program Files/JACK2'
-    endif
-
-    if host_cpu == 'x86'
-      jack_install_dir = jack32_install_dir
-      jack_maybe_installed = fs.is_dir(jack32_install_dir / 'include')
-      if not jack_maybe_installed and jack64_install_dir != ''
-        jack_maybe_installed = fs.is_dir(jack64_install_dir / 'include')
-        jack_install_dir = jack64_install_dir
-      endif
-    elif jack64_install_dir != ''
-      jack_maybe_installed = import('fs').is_dir(jack64_install_dir / 'include')
-      jack_install_dir = jack64_install_dir
-    endif
-
-    error_msg += 'JACK2 installation could not be found'
-  else
-    error_msg += 'JACK dependency could not be found'
-  endif
-
-  if not jack_maybe_installed
-    if jack_option.enabled()
-      error(error_msg)
-    endif
-    subdir_done()
-  endif
-
-  if not host_cpu.startswith('x86')
-    if jack_option.enabled()
-      error('On Windows, JACK only supports x86 32-bit and 64-bit')
-    endif
-    subdir_done()
-  endif
-
-  if host_cpu == 'x86'
-    jack_libname = 'libjack'
-    if jack_install_dir == jack64_install_dir
-      jack_libdir = jack_install_dir / 'lib32'
-    else
-      jack_libdir = jack_install_dir / 'lib'
-    endif
-  else
-    jack_libname = 'libjack64'
-    jack_libdir = jack_install_dir / 'lib'
-  endif
-
-  inc = include_directories(jack_install_dir / 'include')
-  libjack_dep = cc.find_library(jack_libname,
-      dirs: jack_libdir,
-      has_headers: 'jack/jack.h',
-      header_include_directories: inc,
-      required: jack_option)
-  # This won't be needed once we require a meson version that includes this:
-  # https://github.com/mesonbuild/meson/pull/10428
-  jack_incdirs += inc
-endif
-
 gstjack = library('gstjack',
   jack_sources,
-  c_args : gst_plugins_good_args + ['-DHAVE_JACK_1_9_7'],
-  include_directories : jack_incdirs,
-  dependencies : [gst_dep, gstbase_dep, gstaudio_dep, libjack_dep],
+  c_args : gst_plugins_good_args,
+  include_directories : [configinc],
+  dependencies : [gst_dep, gstbase_dep, gstaudio_dep, gmodule_dep],
   install : true,
   install_dir : plugins_install_dir,
 )
diff --git a/ext/jpeg/gstjpegdec.c b/ext/jpeg/gstjpegdec.c
index dd1a039a925958c425f98afd39fd0d6903cd4ff9..7523419835eea6e4b157b08d7230e00bcda60d02 100644
--- a/ext/jpeg/gstjpegdec.c
+++ b/ext/jpeg/gstjpegdec.c
@@ -234,20 +234,20 @@ gst_jpeg_dec_term_source (j_decompress_ptr cinfo)
 }
 
 METHODDEF (void)
-    gst_jpeg_dec_my_output_message (j_common_ptr cinfo)
+gst_jpeg_dec_my_output_message (j_common_ptr cinfo)
 {
   return;                       /* do nothing */
 }
 
 METHODDEF (void)
-    gst_jpeg_dec_my_emit_message (j_common_ptr cinfo, int msg_level)
+gst_jpeg_dec_my_emit_message (j_common_ptr cinfo, int msg_level)
 {
   /* GST_LOG_OBJECT (CINFO_GET_JPEGDEC (&cinfo), "msg_level=%d", msg_level); */
   return;
 }
 
 METHODDEF (void)
-    gst_jpeg_dec_my_error_exit (j_common_ptr cinfo)
+gst_jpeg_dec_my_error_exit (j_common_ptr cinfo)
 {
   struct GstJpegDecErrorMgr *err_mgr = (struct GstJpegDecErrorMgr *) cinfo->err;
 
@@ -880,7 +880,7 @@ gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame,
   gint lines, v_samp[3];
   guchar *base[3], *last[3];
   gint stride[3];
-  guint height, field_height;
+  guint field_height;
 
   line[0] = y;
   line[1] = u;
@@ -893,7 +893,7 @@ gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame,
   if (G_UNLIKELY (v_samp[0] > 2 || v_samp[1] > 2 || v_samp[2] > 2))
     goto format_not_supported;
 
-  height = field_height = GST_VIDEO_FRAME_HEIGHT (frame);
+  field_height = GST_VIDEO_FRAME_HEIGHT (frame);
 
   /* XXX: division by 2 here might not be a good idea yes. But we are doing this
    * already in gst_jpeg_dec_handle_frame() for interlaced jpeg */
@@ -943,7 +943,7 @@ gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame,
   } else
 #endif
   {
-    for (i = 0; i < height; i += v_samp[0] * DCTSIZE) {
+    for (i = 0; i < field_height; i += v_samp[0] * DCTSIZE) {
       for (j = 0; j < (v_samp[0] * DCTSIZE); ++j) {
         /* Y */
         line[0][j] = base[0] + (i + j) * stride[0];
@@ -1068,13 +1068,14 @@ gst_jpeg_turbo_parse_ext_fmt_convert (GstJpegDec * dec, gint * clrspc)
 }
 #endif
 
-static void
+static gboolean
 gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc,
     gboolean interlaced)
 {
   GstVideoCodecState *outstate;
   GstVideoInfo *info;
   GstVideoFormat format;
+  gboolean res;
 
 #ifdef JCS_EXTENSIONS
   if (dec->format_convert) {
@@ -1104,7 +1105,7 @@ gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc,
         height == GST_VIDEO_INFO_HEIGHT (info) &&
         format == GST_VIDEO_INFO_FORMAT (info)) {
       gst_video_codec_state_unref (outstate);
-      return;
+      return TRUE;
     }
     gst_video_codec_state_unref (outstate);
   }
@@ -1118,6 +1119,8 @@ gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc,
   outstate =
       gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec), format,
       width, height, dec->input_state);
+  if (!outstate)
+    return FALSE;
 
   switch (clrspc) {
     case JCS_RGB:
@@ -1142,10 +1145,12 @@ gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc,
 
   gst_video_codec_state_unref (outstate);
 
-  gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec));
+  res = gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec));
 
   GST_DEBUG_OBJECT (dec, "max_v_samp_factor=%d", dec->cinfo.max_v_samp_factor);
   GST_DEBUG_OBJECT (dec, "max_h_samp_factor=%d", dec->cinfo.max_h_samp_factor);
+
+  return res;
 }
 
 static GstFlowReturn
@@ -1301,7 +1306,7 @@ gst_jpeg_dec_decode (GstJpegDec * dec, GstVideoFrame * vframe, guint width,
     GST_LOG_OBJECT (dec, "decompressing (required scanline buffer height = %u)",
         dec->cinfo.rec_outbuf_height);
 
-    /* For some widths jpeglib requires more horizontal padding than I420 
+    /* For some widths jpeglib requires more horizontal padding than I420
      * provides. In those cases we need to decode into separate buffers and then
      * copy over the data into our final picture buffer, otherwise jpeglib might
      * write over the end of a line into the beginning of the next line,
@@ -1407,7 +1412,8 @@ gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame)
    * to see if there are two SOF markers in the packet to detect this) */
   if (gst_video_decoder_get_packetized (bdec) &&
       dec->input_state &&
-      dec->input_state->info.height > height &&
+      dec->input_state->info.height != height && height > DCTSIZE &&
+      dec->input_state->info.height > (2 * (height - DCTSIZE)) &&
       dec->input_state->info.height <= (height * 2)
       && dec->input_state->info.width == width) {
     GST_LOG_OBJECT (dec,
@@ -1424,8 +1430,9 @@ gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame)
     num_fields = 1;
   }
 
-  gst_jpeg_dec_negotiate (dec, width, output_height,
-      dec->cinfo.jpeg_color_space, num_fields == 2);
+  if (!gst_jpeg_dec_negotiate (dec, width, output_height,
+          dec->cinfo.jpeg_color_space, num_fields == 2))
+    goto negotiation_failed;
 
   state = gst_video_decoder_get_output_state (bdec);
   ret = gst_video_decoder_allocate_output_frame (bdec, frame);
@@ -1557,6 +1564,12 @@ map_failed:
     ret = GST_FLOW_ERROR;
     goto exit;
   }
+negotiation_failed:
+  {
+    GST_ELEMENT_ERROR (dec, CORE, NEGOTIATION, (NULL), ("failed to negotiate"));
+    ret = GST_FLOW_NOT_NEGOTIATED;
+    goto exit;
+  }
 decode_error:
   {
     gchar err_msg[JMSG_LENGTH_MAX];
diff --git a/ext/libcaca/gstcacasink.c b/ext/libcaca/gstcacasink.c
index 93e27418494741037fcbdf818a1a0145fbf0cfe8..25de7f8507a4b712b75105241eefd2bbfced33e4 100644
--- a/ext/libcaca/gstcacasink.c
+++ b/ext/libcaca/gstcacasink.c
@@ -62,7 +62,8 @@ enum
   PROP_SCREEN_WIDTH,
   PROP_SCREEN_HEIGHT,
   PROP_DITHER,
-  PROP_ANTIALIASING
+  PROP_ANTIALIASING,
+  PROP_DRIVER
 };
 
 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
@@ -112,6 +113,49 @@ gst_cacasink_dither_get_type (void)
   return dither_type;
 }
 
+#define GST_TYPE_CACADRIVER (gst_cacasink_driver_get_type())
+
+/**
+ * GstCACASinkDriver:
+ *
+ * Libcaca output driver.
+ *
+ * Since: 1.24
+ */
+static GType
+gst_cacasink_driver_get_type (void)
+{
+  static GType driver_type = 0;
+
+  if (g_once_init_enter (&driver_type)) {
+    char const *const *list;
+    gint i, n_drivers = 0;
+    GEnumValue *driver;
+    GType _driver_type;
+
+    list = caca_get_display_driver_list ();
+
+    /* Read number of available drivers */
+    for (i = 0; list[i]; i += 2, ++n_drivers);
+
+    driver = g_new0 (GEnumValue, n_drivers + 1);
+
+    for (i = 0; i < n_drivers; i++) {
+      driver[i].value = i;
+      driver[i].value_nick = g_strdup (list[2 * i]);
+      driver[i].value_name = g_strdup (list[2 * i + 1]);
+    }
+    driver[i].value = 0;
+    driver[i].value_name = NULL;
+    driver[i].value_nick = NULL;
+
+    _driver_type = g_enum_register_static ("GstCACASinkDriver", driver);
+    g_once_init_leave (&driver_type, _driver_type);
+  }
+
+  return driver_type;
+}
+
 static void
 gst_cacasink_class_init (GstCACASinkClass * klass)
 {
@@ -146,6 +190,17 @@ gst_cacasink_class_init (GstCACASinkClass * klass)
           "Enables Anti-Aliasing", GST_CACA_DEFAULT_ANTIALIASING,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstCACASink:driver:
+   *
+   * The libcaca output driver.
+   *
+   * Since: 1.24
+   **/
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DRIVER,
+      g_param_spec_enum ("driver", "driver", "Output driver",
+          GST_TYPE_CACADRIVER, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstelement_class->change_state = gst_cacasink_change_state;
 
   gst_element_class_set_static_metadata (gstelement_class,
@@ -159,6 +214,8 @@ gst_cacasink_class_init (GstCACASinkClass * klass)
   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_cacasink_render);
 
   gst_type_mark_as_plugin_api (GST_TYPE_CACADITHER, 0);
+  gst_type_mark_as_plugin_api (GST_TYPE_CACADRIVER,
+      GST_PLUGIN_API_FLAG_IGNORE_ENUM_MEMBERS);
 }
 
 static void
@@ -250,6 +307,7 @@ gst_cacasink_init (GstCACASink * cacasink)
 
   cacasink->dither = GST_CACA_DEFAULT_DITHER;
   cacasink->antialiasing = GST_CACA_DEFAULT_ANTIALIASING;
+  cacasink->driver = 0;
 }
 
 static GstFlowReturn
@@ -263,11 +321,11 @@ gst_cacasink_render (GstBaseSink * basesink, GstBuffer * buffer)
   if (!gst_video_frame_map (&frame, &cacasink->info, buffer, GST_MAP_READ))
     goto invalid_frame;
 
-  caca_clear ();
-  caca_draw_bitmap (0, 0, cacasink->screen_width - 1,
+  caca_clear_canvas (cacasink->cv);
+  caca_dither_bitmap (cacasink->cv, 0, 0, cacasink->screen_width - 1,
       cacasink->screen_height - 1, cacasink->bitmap,
       GST_VIDEO_FRAME_PLANE_DATA (&frame, 0));
-  caca_refresh ();
+  caca_refresh_display (cacasink->dp);
 
   gst_video_frame_unmap (&frame);
 
@@ -306,6 +364,10 @@ gst_cacasink_set_property (GObject * object, guint prop_id,
       }
       break;
     }
+    case PROP_DRIVER:{
+      cacasink->driver = g_value_get_enum (value);
+      break;
+    }
     default:
       break;
   }
@@ -336,6 +398,10 @@ gst_cacasink_get_property (GObject * object, guint prop_id, GValue * value,
       g_value_set_boolean (value, cacasink->antialiasing);
       break;
     }
+    case PROP_DRIVER:{
+      g_value_set_enum (value, cacasink->driver);
+      break;
+    }
     default:{
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -346,13 +412,26 @@ gst_cacasink_get_property (GObject * object, guint prop_id, GValue * value,
 static gboolean
 gst_cacasink_open (GstCACASink * cacasink)
 {
+  GEnumClass *enum_class;
+  GEnumValue *ev;
+
   cacasink->bitmap = NULL;
 
-  if (caca_init () < 0)
+  if (!(cacasink->cv = caca_create_canvas (0, 0)))
     goto init_failed;
 
-  cacasink->screen_width = caca_get_width ();
-  cacasink->screen_height = caca_get_height ();
+  enum_class = g_type_class_peek (GST_TYPE_CACADRIVER);
+  ev = g_enum_get_value (enum_class, cacasink->driver);
+
+  cacasink->dp = caca_create_display_with_driver (cacasink->cv, ev->value_nick);
+
+  if (!cacasink->dp) {
+    caca_free_canvas (cacasink->cv);
+    return FALSE;
+  }
+
+  cacasink->screen_width = caca_get_canvas_width (cacasink->cv);
+  cacasink->screen_height = caca_get_canvas_height (cacasink->cv);
   cacasink->antialiasing = TRUE;
   caca_set_feature (CACA_ANTIALIASING_MAX);
   cacasink->dither = 0;
@@ -376,7 +455,11 @@ gst_cacasink_close (GstCACASink * cacasink)
     caca_free_bitmap (cacasink->bitmap);
     cacasink->bitmap = NULL;
   }
-  caca_end ();
+
+  caca_free_display (cacasink->dp);
+  cacasink->dp = NULL;
+  caca_free_canvas (cacasink->cv);
+  cacasink->cv = NULL;
 }
 
 static GstStateChangeReturn
diff --git a/ext/libcaca/gstcacasink.h b/ext/libcaca/gstcacasink.h
index 862e744be21aa5a8641d1d846fcdbde165a08470..5a9fad2b39032a5e811fd788cd1b22b86caba17d 100644
--- a/ext/libcaca/gstcacasink.h
+++ b/ext/libcaca/gstcacasink.h
@@ -38,12 +38,17 @@ G_DECLARE_FINAL_TYPE (GstCACASink, gst_cacasink, GST, CACASINK, GstBaseSink)
 struct _GstCACASink {
   GstBaseSink parent;
 
+  caca_canvas_t *cv;
+  caca_display_t *dp;
+
   GstVideoInfo info;
   gint screen_width, screen_height;
 
   guint dither;
   gboolean antialiasing;
 
+  guint driver;
+
   struct caca_bitmap *bitmap;
 };
 
diff --git a/ext/libpng/gstpngdec.c b/ext/libpng/gstpngdec.c
index 3775227dbd17143c1e5be76d6f9912f691d6591d..dc2043fda3524ec995bee4fd31908fee31be8fbc 100644
--- a/ext/libpng/gstpngdec.c
+++ b/ext/libpng/gstpngdec.c
@@ -250,7 +250,7 @@ gst_pngdec_caps_create_and_set (GstPngDec * pngdec)
 
   /* Add alpha channel if 16-bit depth, but not for GRAY images */
   if ((bpc > 8) && (color_type != PNG_COLOR_TYPE_GRAY)) {
-    png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE);
+    png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_AFTER);
     png_set_swap (pngdec->png);
   }
 #if 0
diff --git a/ext/libpng/gstpngenc.c b/ext/libpng/gstpngenc.c
index f807e4c9cd442d4098eb44b9b82371e744e48cef..002062349421fb99c07cb9a221898a0d09de54f6 100644
--- a/ext/libpng/gstpngenc.c
+++ b/ext/libpng/gstpngenc.c
@@ -86,6 +86,8 @@ static GstFlowReturn gst_pngenc_handle_frame (GstVideoEncoder * encoder,
     GstVideoCodecFrame * frame);
 static gboolean gst_pngenc_set_format (GstVideoEncoder * encoder,
     GstVideoCodecState * state);
+static gboolean gst_pngenc_flush (GstVideoEncoder * encoder);
+static gboolean gst_pngenc_start (GstVideoEncoder * encoder);
 static gboolean gst_pngenc_propose_allocation (GstVideoEncoder * encoder,
     GstQuery * query);
 
@@ -143,6 +145,8 @@ gst_pngenc_class_init (GstPngEncClass * klass)
   venc_class->set_format = gst_pngenc_set_format;
   venc_class->handle_frame = gst_pngenc_handle_frame;
   venc_class->propose_allocation = gst_pngenc_propose_allocation;
+  venc_class->flush = gst_pngenc_flush;
+  venc_class->start = gst_pngenc_start;
   gobject_class->finalize = gst_pngenc_finalize;
 
   GST_DEBUG_CATEGORY_INIT (pngenc_debug, "pngenc", 0, "PNG image encoder");
@@ -228,38 +232,106 @@ user_flush_data (png_structp png_ptr G_GNUC_UNUSED)
 {
 }
 
+/* Copied from glib/gutilsprivate.h
+ *
+ * Returns the smallest power of 2 greater than or equal to n,
+ * or 0 if such power does not fit in a gsize
+ */
+static inline gsize
+gst_pngenc_g_nearest_pow (gsize num)
+{
+  gsize n = num - 1;
+
+  g_assert (num > 0 && num <= G_MAXSIZE / 2);
+
+  n |= n >> 1;
+  n |= n >> 2;
+  n |= n >> 4;
+  n |= n >> 8;
+  n |= n >> 16;
+#if GLIB_SIZEOF_SIZE_T == 8
+  n |= n >> 32;
+#endif
+
+  return n + 1;
+}
+
+static void
+ensure_memory_is_enough (GstPngEnc * pngenc, unsigned int extra_length)
+{
+  GstMemory *new_memory;
+  GstMapInfo map;
+  gsize old_size, desired_size;
+  guint8 *new_data;
+
+  old_size = pngenc->output_map.size;
+  desired_size = gst_pngenc_g_nearest_pow (old_size + extra_length);
+  g_assert (desired_size != 0);
+
+  /* Our output memory wasn't big enough.
+   * Make a new memory that's twice the size, */
+  new_memory = gst_allocator_alloc (NULL, desired_size, NULL);
+  gst_memory_map (new_memory, &map, GST_MAP_READWRITE);
+  new_data = map.data;
+
+  /* copy previous data  */
+  memcpy (new_data, pngenc->output_map.data, old_size);
+  gst_memory_unmap (pngenc->output_mem, &pngenc->output_map);
+  gst_memory_unref (pngenc->output_mem);
+
+  /* drop it into place, */
+  pngenc->output_mem = new_memory;
+  pngenc->output_map = map;
+}
+
 static void
 user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length)
 {
   GstPngEnc *pngenc;
-  GstMemory *mem;
-  GstMapInfo minfo;
 
   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
 
-  mem = gst_allocator_alloc (NULL, length, NULL);
-  if (!mem) {
-    GST_ERROR_OBJECT (pngenc, "Failed to allocate memory");
-    png_error (png_ptr, "Failed to allocate memory");
+  GST_TRACE_OBJECT (pngenc,
+      "Memory size: %" G_GSIZE_FORMAT "\nLength to be written: %u",
+      pngenc->output_map.size, length);
+
+  if (pngenc->output_map.size > G_MAXSIZE - length) {
+    GST_ERROR_OBJECT (pngenc,
+        "Memory buffer would overflow after the png write, aborting.");
+    png_error (png_ptr, "Buffer would overflow, aborting the write.");
 
     /* never reached */
+    /* libpng will longjmp and we will catch it later on */
     return;
   }
 
-  if (!gst_memory_map (mem, &minfo, GST_MAP_WRITE)) {
-    GST_ERROR_OBJECT (pngenc, "Failed to map memory");
-    gst_memory_unref (mem);
+  if ((pngenc->output_mem_pos + length) > pngenc->output_map.size) {
+    GST_INFO_OBJECT (pngenc, "Memory not enough, Allocating more.");
+    ensure_memory_is_enough (pngenc, length);
+  }
 
-    png_error (png_ptr, "Failed to map memory");
+  memcpy (&pngenc->output_map.data[pngenc->output_mem_pos], data, length);
+  pngenc->output_mem_pos += length;
+}
 
-    /* never reached */
-    return;
-  }
+static gboolean
+gst_pngenc_flush (GstVideoEncoder * encoder)
+{
+  GstPngEnc *pngenc = GST_PNGENC (encoder);
+
+  pngenc->frame_count = 0;
+
+  return TRUE;
+}
+
+static gboolean
+gst_pngenc_start (GstVideoEncoder * encoder)
+{
+  GstPngEnc *pngenc = GST_PNGENC (encoder);
 
-  memcpy (minfo.data, data, length);
-  gst_memory_unmap (mem, &minfo);
+  pngenc->frame_count = 0;
 
-  gst_buffer_append_memory (pngenc->buffer_out, mem);
+  return TRUE;
 }
 
 static GstFlowReturn
@@ -269,10 +341,16 @@ gst_pngenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
   gint row_index;
   png_byte **row_pointers;
   GstFlowReturn ret = GST_FLOW_OK;
-  GstVideoInfo *info;
+  const GstVideoInfo *info;
   GstVideoFrame vframe;
+  gsize memory_size;
+  GstBuffer *outbuf;
 
   pngenc = GST_PNGENC (encoder);
+
+  if (pngenc->snapshot && pngenc->frame_count > 0)
+    return GST_FLOW_EOS;
+
   info = &pngenc->input_state->info;
 
   GST_DEBUG_OBJECT (pngenc, "BEGINNING");
@@ -323,7 +401,15 @@ gst_pngenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
   }
 
   /* allocate the output buffer */
-  pngenc->buffer_out = gst_buffer_new ();
+  pngenc->output_mem_pos = 0;
+  pngenc->output_mem =
+      gst_allocator_alloc (NULL, MAX (4096, GST_VIDEO_INFO_SIZE (info)), NULL);
+  if (!pngenc->output_mem) {
+    GST_ERROR_OBJECT (pngenc, "Failed to allocate memory");
+    return GST_FLOW_ERROR;
+  }
+
+  gst_memory_map (pngenc->output_mem, &pngenc->output_map, GST_MAP_READWRITE);
 
   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
   png_write_image (pngenc->png_struct_ptr, row_pointers);
@@ -336,13 +422,24 @@ gst_pngenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
 
   /* Set final size and store */
-  frame->output_buffer = pngenc->buffer_out;
+  gst_memory_unmap (pngenc->output_mem, &pngenc->output_map);
+  /* Trim the buffer size */
+  memory_size = pngenc->output_mem_pos;
+  gst_memory_resize (pngenc->output_mem, 0, memory_size);
+  pngenc->output_mem_pos = 0;
 
-  pngenc->buffer_out = NULL;
+  outbuf = gst_buffer_new ();
+  gst_buffer_append_memory (outbuf, pngenc->output_mem);
+  pngenc->output_mem = NULL;
+  frame->output_buffer = outbuf;
+
+  GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
 
   if ((ret = gst_video_encoder_finish_frame (encoder, frame)) != GST_FLOW_OK)
     goto done;
 
+  ++pngenc->frame_count;
+
   if (pngenc->snapshot)
     ret = GST_FLOW_EOS;
 
diff --git a/ext/libpng/gstpngenc.h b/ext/libpng/gstpngenc.h
index 878038c08248f33e1b73782ea41023d567af7213..d2be351b94eb93e60d9d5aea231614aa383dd844 100644
--- a/ext/libpng/gstpngenc.h
+++ b/ext/libpng/gstpngenc.h
@@ -37,7 +37,10 @@ struct _GstPngEnc
   GstVideoEncoder parent;
 
   GstVideoCodecState *input_state;
-  GstBuffer *buffer_out;
+
+  GstMemory *output_mem;
+  GstMapInfo output_map;
+  gsize output_mem_pos;
 
   png_structp png_struct_ptr;
   png_infop png_info_ptr;
@@ -47,7 +50,7 @@ struct _GstPngEnc
   guint compression_level;
 
   gboolean snapshot;
-  gboolean newmedia;
+  guint    frame_count;
 };
 
 GST_ELEMENT_REGISTER_DECLARE (pngenc);
diff --git a/ext/meson.build b/ext/meson.build
index 00ce467f84e9545cdf72a5cd4ce5bf7b6f670778..5b0a6752bf1b34f63570452e8f1cd966028bcb29 100644
--- a/ext/meson.build
+++ b/ext/meson.build
@@ -1,5 +1,7 @@
 subdir('aalib')
 subdir('adaptivedemux2')
+subdir('amrnb')
+subdir('amrwbdec')
 subdir('cairo')
 subdir('flac')
 subdir('gdk_pixbuf')
diff --git a/ext/mpg123/gstmpg123audiodec.c b/ext/mpg123/gstmpg123audiodec.c
index 83d159af89531230ddd527cb411489eda4c39061..ed6430ec8e94f8f68c374150fa8c1129c2e3c4c1 100644
--- a/ext/mpg123/gstmpg123audiodec.c
+++ b/ext/mpg123/gstmpg123audiodec.c
@@ -99,7 +99,7 @@ static guint gst_mpg123_audio_dec_get_info_queue_size (GstMpg123AudioDec *
 
 G_DEFINE_TYPE (GstMpg123AudioDec, gst_mpg123_audio_dec, GST_TYPE_AUDIO_DECODER);
 GST_ELEMENT_REGISTER_DEFINE (mpg123audiodec, "mpg123audiodec",
-    GST_RANK_MARGINAL, GST_TYPE_MPG123_AUDIO_DEC);
+    GST_RANK_PRIMARY, GST_TYPE_MPG123_AUDIO_DEC);
 
 static void
 gst_mpg123_audio_dec_class_init (GstMpg123AudioDecClass * klass)
@@ -321,6 +321,7 @@ gst_mpg123_audio_dec_push_decoded_bytes (GstMpg123AudioDec * mpg123_decoder,
 {
   GstBuffer *output_buffer;
   GstAudioDecoder *dec;
+  GstMapInfo info;
 
   output_buffer = NULL;
   dec = GST_AUDIO_DECODER (mpg123_decoder);
@@ -336,7 +337,7 @@ gst_mpg123_audio_dec_push_decoded_bytes (GstMpg123AudioDec * mpg123_decoder,
     return GST_FLOW_OK;
   }
 
-  if (G_UNLIKELY (clip_end >= num_decoded_bytes)) {
+  if (G_UNLIKELY (clip_start + clip_end >= num_decoded_bytes)) {
     /* Fully-clipped frames still need to be finished, since they got
      * decoded properly, they are just made of padding samples. */
     GST_LOG_OBJECT (mpg123_decoder, "frame is fully clipped; "
@@ -351,24 +352,16 @@ gst_mpg123_audio_dec_push_decoded_bytes (GstMpg123AudioDec * mpg123_decoder,
   output_buffer = gst_audio_decoder_allocate_output_buffer (dec,
       num_decoded_bytes);
 
-  if (output_buffer == NULL) {
-    /* This is necessary to advance playback in time,
-     * even when nothing was decoded. */
-    return gst_audio_decoder_finish_frame (dec, NULL, 1);
+  if (gst_buffer_map (output_buffer, &info, GST_MAP_WRITE)) {
+    memcpy (info.data, decoded_bytes, num_decoded_bytes);
+    gst_buffer_unmap (output_buffer, &info);
   } else {
-    GstMapInfo info;
-
-    if (gst_buffer_map (output_buffer, &info, GST_MAP_WRITE)) {
-      memcpy (info.data, decoded_bytes, num_decoded_bytes);
-      gst_buffer_unmap (output_buffer, &info);
-    } else {
-      GST_ERROR_OBJECT (mpg123_decoder, "gst_buffer_map() returned NULL");
-      gst_buffer_unref (output_buffer);
-      output_buffer = NULL;
-    }
-
-    return gst_audio_decoder_finish_frame (dec, output_buffer, 1);
+    GST_ERROR_OBJECT (mpg123_decoder, "gst_buffer_map() returned NULL");
+    gst_buffer_unref (output_buffer);
+    output_buffer = NULL;
   }
+
+  return gst_audio_decoder_finish_frame (dec, output_buffer, 1);
 }
 
 
diff --git a/ext/pulse/pulsedeviceprovider.c b/ext/pulse/pulsedeviceprovider.c
index dbc7c10d921a171ec060fc353039e3654e508d74..85d287a3cfaa9eed92f2c890888bca77f686aae3 100644
--- a/ext/pulse/pulsedeviceprovider.c
+++ b/ext/pulse/pulsedeviceprovider.c
@@ -193,12 +193,36 @@ context_state_cb (pa_context * c, void *userdata)
   }
 }
 
+static gboolean
+is_default_device_name (GstPulseDeviceProvider * self,
+    const char *name, GstPulseDeviceType type)
+{
+  gboolean ret = FALSE;
+
+  GST_OBJECT_LOCK (self);
+  switch (type) {
+    case GST_PULSE_DEVICE_TYPE_SINK:
+      ret = !g_strcmp0 (name, self->default_sink_name);
+      break;
+    case GST_PULSE_DEVICE_TYPE_SOURCE:
+      ret = !g_strcmp0 (name, self->default_source_name);
+      break;
+    default:
+      GST_ERROR_OBJECT (self, "Unknown pulse device type!");
+      break;
+  }
+  GST_OBJECT_UNLOCK (self);
+
+  return ret;
+}
+
 static GstDevice *
 new_source (GstPulseDeviceProvider * self, const pa_source_info * info)
 {
   GstCaps *caps;
   GstStructure *props;
   guint i;
+  gboolean is_default = FALSE;
 
   caps = gst_caps_new_empty ();
 
@@ -211,9 +235,11 @@ new_source (GstPulseDeviceProvider * self, const pa_source_info * info)
     gst_device_provider_hide_provider (GST_DEVICE_PROVIDER (self),
         "alsadeviceprovider");
 
+  is_default = is_default_device_name (self, info->name,
+      GST_PULSE_DEVICE_TYPE_SOURCE);
+
   return gst_pulse_device_new (info->index, info->description,
-      caps, info->name, GST_PULSE_DEVICE_TYPE_SOURCE, props,
-      !g_strcmp0 (info->name, self->default_source_name));
+      caps, info->name, GST_PULSE_DEVICE_TYPE_SOURCE, props, is_default);
 }
 
 static GstDevice *
@@ -222,6 +248,8 @@ new_sink (GstPulseDeviceProvider * self, const pa_sink_info * info)
   GstCaps *caps;
   GstStructure *props;
   guint i;
+  gboolean is_default = FALSE;
+
 
   caps = gst_caps_new_empty ();
 
@@ -230,9 +258,11 @@ new_sink (GstPulseDeviceProvider * self, const pa_sink_info * info)
 
   props = gst_pulse_make_structure (info->proplist);
 
+  is_default = is_default_device_name (self, info->name,
+      GST_PULSE_DEVICE_TYPE_SINK);
+
   return gst_pulse_device_new (info->index, info->description,
-      caps, info->name, GST_PULSE_DEVICE_TYPE_SINK, props,
-      !g_strcmp0 (info->name, self->default_sink_name));
+      caps, info->name, GST_PULSE_DEVICE_TYPE_SINK, props, is_default);
 }
 
 static void
@@ -280,10 +310,14 @@ get_server_info_cb (pa_context * context, const pa_server_info * info,
     gst_structure_get_boolean (props, "is-default", &was_default);
     switch (dev->type) {
       case GST_PULSE_DEVICE_TYPE_SINK:
-        is_default = !g_strcmp0 (dev->internal_name, self->default_sink_name);
+        is_default =
+            is_default_device_name (self, dev->internal_name,
+            GST_PULSE_DEVICE_TYPE_SINK);
         break;
       case GST_PULSE_DEVICE_TYPE_SOURCE:
-        is_default = !g_strcmp0 (dev->internal_name, self->default_source_name);
+        is_default =
+            is_default_device_name (self, dev->internal_name,
+            GST_PULSE_DEVICE_TYPE_SOURCE);
         break;
     }
 
@@ -440,8 +474,8 @@ gst_pulse_device_provider_probe (GstDeviceProvider * provider)
     state = pa_context_get_state (c);
 
     if (!PA_CONTEXT_IS_GOOD (state)) {
-      GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Failed to connect: %s",
-              pa_strerror (pa_context_errno (c))), (NULL));
+      GST_ERROR_OBJECT (self, "Failed to connect: %s",
+          pa_strerror (pa_context_errno (c)));
       goto failed;
     }
 
diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c
index c0ffff388e005d67e80643000b49370cb936504e..348150dd87ed9ef5dcac197eec150fbec6524bbf 100644
--- a/ext/pulse/pulsesink.c
+++ b/ext/pulse/pulsesink.c
@@ -178,9 +178,9 @@ struct _GstPulseRingBuffer
   gint64 m_offset;
   gint64 m_lastoffset;
 
-  gboolean corked:1;
-  gboolean in_commit:1;
-  gboolean paused:1;
+  gboolean corked;
+  gboolean in_commit;
+  gboolean paused;
 };
 struct _GstPulseRingBufferClass
 {
@@ -371,7 +371,7 @@ gst_pulsering_destroy_context (GstPulseRingBuffer * pbuf)
         g_hash_table_remove (gst_pulse_shared_contexts, pbuf->context_name);
 
         pa_context_unref (pctx->context);
-        g_slice_free (GstPulseContext, pctx);
+        g_free (pctx);
       }
     }
     g_free (pbuf->context_name);
@@ -528,7 +528,7 @@ gst_pulseringbuffer_open_device (GstAudioRingBuffer * buf)
 
   pctx = g_hash_table_lookup (gst_pulse_shared_contexts, pbuf->context_name);
   if (pctx == NULL) {
-    pctx = g_slice_new0 (GstPulseContext);
+    pctx = g_new0 (GstPulseContext, 1);
 
     /* get the mainloop api and create a context */
     GST_INFO_OBJECT (psink, "new context with name %s, pbuf=%p, pctx=%p",
@@ -608,7 +608,7 @@ create_failed:
   {
     GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
         ("Failed to create context"), (NULL));
-    g_slice_free (GstPulseContext, pctx);
+    g_free (pctx);
     goto unlock_and_fail;
   }
 connect_failed:
@@ -1567,7 +1567,7 @@ gst_pulseringbuffer_commit (GstAudioRingBuffer * buf, guint64 * sample,
           goto fake_done;
         }
 
-        if (pbuf->m_writable == (size_t) - 1)
+        if (pbuf->m_writable == (size_t) -1)
           goto writable_size_failed;
 
         pbuf->m_writable /= bpf;
diff --git a/ext/pulse/pulsesink.h b/ext/pulse/pulsesink.h
index 51ec86a25ad668c0caacbcdfa9d51d7a48719552..7f45cb25051276aec6954f1dffd6102571640ab2 100644
--- a/ext/pulse/pulsesink.h
+++ b/ext/pulse/pulsesink.h
@@ -57,9 +57,9 @@ struct _GstPulseSink
   GstPulseDeviceInfo device_info;
 
   gdouble volume;
-  gboolean volume_set:1;
-  gboolean mute:1;
-  gboolean mute_set:1;
+  gboolean volume_set;
+  gboolean mute;
+  gboolean mute_set;
   guint32 current_sink_idx;
   gchar *current_sink_name;
 
diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c
index a337a59fb567197df5d28a3c23896cd2e063e0f9..41252bdf725b16302a81389d0fc49be4ea3f59ce 100644
--- a/ext/pulse/pulsesrc.c
+++ b/ext/pulse/pulsesrc.c
@@ -1638,7 +1638,7 @@ gst_pulsesrc_success_cb (pa_stream * s, int success, void *userdata)
 {
   GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (userdata);
 
-  pulsesrc->operation_success = ! !success;
+  pulsesrc->operation_success = !!success;
   pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
 }
 
diff --git a/ext/pulse/pulsesrc.h b/ext/pulse/pulsesrc.h
index c532afb189d0466a0a42b7c33fded69adcaa9498..a39a043910582a4fef4ce849b352f3ad33b4e5ed 100644
--- a/ext/pulse/pulsesrc.h
+++ b/ext/pulse/pulsesrc.h
@@ -56,19 +56,19 @@ struct _GstPulseSrc
   gchar *device_description;
 
   gdouble volume;
-  gboolean volume_set:1;
-  gboolean mute:1;
-  gboolean mute_set:1;
+  gboolean volume_set;
+  gboolean mute;
+  gboolean mute_set;
   guint32 current_source_idx;
   gchar *current_source_name;
 
   gint notify; /* atomic */
 
-  gboolean corked:1;
-  gboolean stream_connected:1;
-  gboolean operation_success:1;
-  gboolean paused:1;
-  gboolean in_read:1;
+  gboolean corked;
+  gboolean stream_connected;
+  gboolean operation_success;
+  gboolean paused;
+  gboolean in_read;
 
   GstStructure *properties;
   pa_proplist *proplist;
diff --git a/ext/pulse/pulseutil.c b/ext/pulse/pulseutil.c
index aa90ce9734abc6eeb9ca30fa151479d0999bbffc..69fae00cc3ff17c75fca714c7c7a12c980c6d0f3 100644
--- a/ext/pulse/pulseutil.c
+++ b/ext/pulse/pulseutil.c
@@ -40,33 +40,35 @@ static const struct
   pa_channel_position_t pa_pos;
 } gst_pa_pos_table[] = {
   {
-  GST_AUDIO_CHANNEL_POSITION_MONO, PA_CHANNEL_POSITION_MONO}, {
-  GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT}, {
-  GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT}, {
-  GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, PA_CHANNEL_POSITION_REAR_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_LEFT}, {
-  GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_REAR_RIGHT}, {
-  GST_AUDIO_CHANNEL_POSITION_LFE1, PA_CHANNEL_POSITION_LFE}, {
-  GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_FRONT_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
-        PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
-        PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT}, {
-  GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, PA_CHANNEL_POSITION_SIDE_RIGHT}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, PA_CHANNEL_POSITION_TOP_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT,
-        PA_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT,
-        PA_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER,
-        PA_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, PA_CHANNEL_POSITION_TOP_REAR_LEFT}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT,
-        PA_CHANNEL_POSITION_TOP_REAR_RIGHT}, {
-  GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER,
-        PA_CHANNEL_POSITION_TOP_REAR_CENTER}, {
-  GST_AUDIO_CHANNEL_POSITION_NONE, PA_CHANNEL_POSITION_INVALID}
+      GST_AUDIO_CHANNEL_POSITION_MONO, PA_CHANNEL_POSITION_MONO}, {
+      GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT}, {
+      GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT}, {
+      GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, PA_CHANNEL_POSITION_REAR_CENTER}, {
+      GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_LEFT}, {
+      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_REAR_RIGHT}, {
+      GST_AUDIO_CHANNEL_POSITION_LFE1, PA_CHANNEL_POSITION_LFE}, {
+        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
+      PA_CHANNEL_POSITION_FRONT_CENTER}, {
+        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+      PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
+        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+      PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
+      GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT}, {
+      GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, PA_CHANNEL_POSITION_SIDE_RIGHT}, {
+      GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, PA_CHANNEL_POSITION_TOP_CENTER}, {
+        GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT,
+      PA_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
+        GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+      PA_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
+        GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER,
+      PA_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
+        GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT,
+      PA_CHANNEL_POSITION_TOP_REAR_LEFT}, {
+        GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT,
+      PA_CHANNEL_POSITION_TOP_REAR_RIGHT}, {
+        GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER,
+      PA_CHANNEL_POSITION_TOP_REAR_CENTER}, {
+      GST_AUDIO_CHANNEL_POSITION_NONE, PA_CHANNEL_POSITION_INVALID}
 };
 
 static gboolean
diff --git a/ext/qt/gstqsgmaterial.cc b/ext/qt/gstqsgmaterial.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5a2b48e69032acf57e18b559254f25a2fa08de8e
--- /dev/null
+++ b/ext/qt/gstqsgmaterial.cc
@@ -0,0 +1,561 @@
+/*
+ * GStreamer
+ * Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <vector>
+#include <stdio.h>
+
+#include <gst/video/video.h>
+#include <gst/gl/gl.h>
+#include <gst/gl/gstglfuncs.h>
+#include "gstqsgmaterial.h"
+
+#define GST_CAT_DEFAULT gst_qsg_texture_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+#define ATTRIBUTE_POSITION_NAME "a_position"
+#define ATTRIBUTE_TEXCOORD_NAME "a_texcoord"
+#define UNIFORM_POSITION_MATRIX_NAME "u_transformation"
+#define UNIFORM_OPACITY_NAME "opacity"
+#define UNIFORM_SWIZZLE_COMPONENTS_NAME "swizzle_components"
+#define UNIFORM_TEXTURE0_NAME "tex"
+#define UNIFORM_YUV_OFFSET_NAME "yuv_offset"
+#define UNIFORM_YUV_YCOEFF_NAME "yuv_ycoeff"
+#define UNIFORM_YUV_UCOEFF_NAME "yuv_ucoeff"
+#define UNIFORM_YUV_VCOEFF_NAME "yuv_vcoeff"
+#define UNIFORM_TRIPLANAR_PLANE0 "Ytex"
+#define UNIFORM_TRIPLANAR_PLANE1 "Utex"
+#define UNIFORM_TRIPLANAR_PLANE2 "Vtex"
+
+/* matrices from glcolorconvert */
+/* FIXME: use the colormatrix support from videoconvert */
+
+/* BT. 601 standard with the following ranges:
+ * Y = [16..235] (of 255)
+ * Cb/Cr = [16..240] (of 255)
+ */
+static const gfloat from_yuv_bt601_offset[] = {-0.0625f, -0.5f, -0.5f};
+static const gfloat from_yuv_bt601_rcoeff[] = {1.164f, 0.000f, 1.596f};
+static const gfloat from_yuv_bt601_gcoeff[] = {1.164f,-0.391f,-0.813f};
+static const gfloat from_yuv_bt601_bcoeff[] = {1.164f, 2.018f, 0.000f};
+
+/* BT. 709 standard with the following ranges:
+ * Y = [16..235] (of 255)
+ * Cb/Cr = [16..240] (of 255)
+ */
+static const gfloat from_yuv_bt709_offset[] = {-0.0625f, -0.5f, -0.5f};
+static const gfloat from_yuv_bt709_rcoeff[] = {1.164f, 0.000f, 1.787f};
+static const gfloat from_yuv_bt709_gcoeff[] = {1.164f,-0.213f,-0.531f};
+static const gfloat from_yuv_bt709_bcoeff[] = {1.164f,2.112f, 0.000f};
+
+class GstQSGMaterialShader : public QSGMaterialShader {
+public:
+  GstQSGMaterialShader(GstVideoFormat v_format, char *vertex, char *fragment);
+  ~GstQSGMaterialShader();
+
+  void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
+  {
+    Q_ASSERT(program()->isLinked());
+    if (state.isMatrixDirty())
+       program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+    if (state.isOpacityDirty())
+      program()->setUniformValue(m_id_opacity, state.opacity());
+
+    GstQSGMaterial *mat = static_cast<GstQSGMaterial *>(newMaterial);
+    mat->bind(this, this->v_format);
+  }
+
+  char const *const *attributeNames() const override
+  {
+    static char const *const names[] = { ATTRIBUTE_POSITION_NAME, ATTRIBUTE_TEXCOORD_NAME, 0 };
+    return names;
+  }
+
+  void initialize() override
+  {
+    const GstVideoFormatInfo *finfo = gst_video_format_get_info (v_format);
+    QSGMaterialShader::initialize();
+    m_id_matrix = program()->uniformLocation(UNIFORM_POSITION_MATRIX_NAME);
+    m_id_opacity = program()->uniformLocation(UNIFORM_OPACITY_NAME);
+    int swizzle_components = program()->uniformLocation(UNIFORM_SWIZZLE_COMPONENTS_NAME);
+    int reorder[4];
+
+    gst_gl_video_format_swizzle (v_format, reorder);
+    program()->setUniformValueArray(swizzle_components, reorder, G_N_ELEMENTS (reorder));
+
+    const char *tex_names[GST_VIDEO_MAX_PLANES];
+    switch (v_format) {
+      case GST_VIDEO_FORMAT_RGB:
+      case GST_VIDEO_FORMAT_RGBA:
+      case GST_VIDEO_FORMAT_BGRA:
+        tex_names[0] = UNIFORM_TEXTURE0_NAME;
+        break;
+      case GST_VIDEO_FORMAT_YV12:
+        tex_names[0] = UNIFORM_TRIPLANAR_PLANE0;
+        tex_names[1] = UNIFORM_TRIPLANAR_PLANE1;
+        tex_names[2] = UNIFORM_TRIPLANAR_PLANE2;
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+
+    for (guint i = 0; i < finfo->n_planes; i++) {
+      this->tex_uniforms[i] = program()->uniformLocation(tex_names[i]);
+      GST_TRACE ("%p tex uniform %i for tex %s", this, this->tex_uniforms[i], tex_names[i]);
+    }
+
+    this->cms_uniform_offset = program()->uniformLocation(UNIFORM_YUV_OFFSET_NAME);
+    this->cms_uniform_ycoeff = program()->uniformLocation(UNIFORM_YUV_YCOEFF_NAME);
+    this->cms_uniform_ucoeff = program()->uniformLocation(UNIFORM_YUV_UCOEFF_NAME);
+    this->cms_uniform_vcoeff = program()->uniformLocation(UNIFORM_YUV_VCOEFF_NAME);
+  }
+
+  const char *vertexShader() const override;
+  const char *fragmentShader() const override;
+
+  int cms_uniform_offset;
+  int cms_uniform_ycoeff;
+  int cms_uniform_ucoeff;
+  int cms_uniform_vcoeff;
+  int tex_uniforms[GST_VIDEO_MAX_PLANES];
+
+private:
+  int m_id_matrix;
+  int m_id_opacity;
+  GstVideoFormat v_format;
+  char *vertex;
+  char *fragment;
+};
+
+GstQSGMaterialShader::GstQSGMaterialShader(GstVideoFormat v_format, char * vertex, char * fragment)
+  : v_format(v_format),
+  vertex(vertex),
+  fragment(fragment)
+{
+}
+
+GstQSGMaterialShader::~GstQSGMaterialShader()
+{
+  g_clear_pointer (&this->vertex, g_free);
+  g_clear_pointer (&this->fragment, g_free);
+}
+
+const char *
+GstQSGMaterialShader::vertexShader() const
+{
+  return vertex;
+}
+
+const char *
+GstQSGMaterialShader::fragmentShader() const
+{
+  return fragment;
+}
+
+#define DEFINE_MATERIAL(format) \
+class G_PASTE(GstQSGMaterial_,format) : public GstQSGMaterial { \
+public: \
+  G_PASTE(GstQSGMaterial_,format)(); \
+  ~G_PASTE(GstQSGMaterial_,format)(); \
+  QSGMaterialType *type() const override { static QSGMaterialType type; return &type; }; \
+}; \
+G_PASTE(GstQSGMaterial_,format)::G_PASTE(GstQSGMaterial_,format)() {} \
+G_PASTE(GstQSGMaterial_,format)::~G_PASTE(GstQSGMaterial_,format)() {}
+
+DEFINE_MATERIAL(RGBA);
+DEFINE_MATERIAL(RGBA_SWIZZLE);
+DEFINE_MATERIAL(YUV_TRIPLANAR);
+
+GstQSGMaterial *
+GstQSGMaterial::new_for_format(GstVideoFormat format)
+{
+  switch (format) {
+    case GST_VIDEO_FORMAT_RGB:
+    case GST_VIDEO_FORMAT_RGBA:
+      return static_cast<GstQSGMaterial *>(new GstQSGMaterial_RGBA());
+    case GST_VIDEO_FORMAT_BGRA:
+      return static_cast<GstQSGMaterial *>(new GstQSGMaterial_RGBA_SWIZZLE());
+    case GST_VIDEO_FORMAT_YV12:
+      return static_cast<GstQSGMaterial *>(new GstQSGMaterial_YUV_TRIPLANAR());
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+GstQSGMaterial::GstQSGMaterial ()
+{
+  static gsize _debug;
+
+  if (g_once_init_enter (&_debug)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtqsgmaterial", 0,
+        "Qt Scenegraph Material");
+    g_once_init_leave (&_debug, 1);
+  }
+
+  g_weak_ref_init (&this->qt_context_ref_, NULL);
+  gst_video_info_init (&this->v_info);
+  memset (&this->v_frame, 0, sizeof (this->v_frame));
+
+  this->buffer_ = NULL;
+  this->buffer_was_bound = FALSE;
+  this->sync_buffer_ = gst_buffer_new ();
+  memset (&this->dummy_textures, 0, sizeof (this->dummy_textures));
+}
+
+GstQSGMaterial::~GstQSGMaterial ()
+{
+  g_weak_ref_clear (&this->qt_context_ref_);
+  gst_buffer_replace (&this->buffer_, NULL);
+  gst_buffer_replace (&this->sync_buffer_, NULL);
+  this->buffer_was_bound = FALSE;
+
+  if (this->v_frame.buffer) {
+    gst_video_frame_unmap (&this->v_frame);
+    memset (&this->v_frame, 0, sizeof (this->v_frame));
+  }
+}
+
+bool
+GstQSGMaterial::compatibleWith(GstVideoInfo * v_info)
+{
+  if (GST_VIDEO_INFO_FORMAT (&this->v_info) != GST_VIDEO_INFO_FORMAT (v_info))
+    return FALSE;
+
+  return TRUE;
+}
+
+static char *
+vertexShaderForFormat(GstVideoFormat v_format)
+{
+  return g_strdup (gst_gl_shader_string_vertex_mat4_vertex_transform);
+}
+
+#define qt_inputs \
+  "attribute vec4 " ATTRIBUTE_POSITION ";\n" \
+  "attribute vec2 " ATTRIBUTE_TEXCOORD ";\n" \
+
+#define gles2_precision \
+  "precision mediump float;\n"
+
+#define texcoord_input \
+  "varying vec2 v_texcoord;\n"
+#define single_texture_input \
+  "uniform sampler2D " UNIFORM_TEXTURE0_NAME ";\n"
+#define triplanar_texture_input \
+  "uniform sampler2D " UNIFORM_TRIPLANAR_PLANE0 ";\n" \
+  "uniform sampler2D " UNIFORM_TRIPLANAR_PLANE1 ";\n" \
+  "uniform sampler2D " UNIFORM_TRIPLANAR_PLANE2 ";\n"
+
+#define uniform_swizzle \
+  "uniform int " UNIFORM_SWIZZLE_COMPONENTS_NAME "[4];\n"
+#define uniform_opacity \
+  "uniform float " UNIFORM_OPACITY_NAME ";\n"
+#define uniform_yuv_to_rgb_color_matrix \
+  "uniform vec3 " UNIFORM_YUV_OFFSET_NAME ";\n" \
+  "uniform vec3 " UNIFORM_YUV_YCOEFF_NAME ";\n" \
+  "uniform vec3 " UNIFORM_YUV_UCOEFF_NAME ";\n" \
+  "uniform vec3 " UNIFORM_YUV_VCOEFF_NAME ";\n"
+
+static char *
+fragmentShaderForFormat(GstVideoFormat v_format, GstGLContext * context)
+{
+  gboolean is_gles2 = (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2) != 0;
+
+  switch (v_format) {
+    case GST_VIDEO_FORMAT_RGB:
+    case GST_VIDEO_FORMAT_RGBA: {
+      char *swizzle = gst_gl_color_convert_swizzle_shader_string (context);
+      char *ret = g_strdup_printf ("%s" texcoord_input single_texture_input uniform_opacity
+          "%s\n"
+          "void main(void) {\n"
+          "  gl_FragColor = texture2D(tex, v_texcoord) * " UNIFORM_OPACITY_NAME ";\n"
+          "}\n", is_gles2 ? gles2_precision : "", swizzle);
+      g_clear_pointer (&swizzle, g_free);
+      return ret;
+    }
+    case GST_VIDEO_FORMAT_BGRA: {
+      char *swizzle = gst_gl_color_convert_swizzle_shader_string (context);
+      char *ret = g_strdup_printf ("%s" texcoord_input single_texture_input uniform_swizzle uniform_opacity
+          "%s\n"
+          "void main(void) {\n"
+          "  gl_FragColor = swizzle(texture2D(tex, v_texcoord), " UNIFORM_SWIZZLE_COMPONENTS_NAME ") * " UNIFORM_OPACITY_NAME ";\n"
+          "}\n", is_gles2 ? gles2_precision : "", swizzle);
+      g_clear_pointer (&swizzle, g_free);
+      return ret;
+    }
+    case GST_VIDEO_FORMAT_YV12: {
+      char *yuv_to_rgb = gst_gl_color_convert_yuv_to_rgb_shader_string (context);
+      char *swizzle = gst_gl_color_convert_swizzle_shader_string (context);
+      char *ret = g_strdup_printf ("%s" texcoord_input triplanar_texture_input uniform_swizzle uniform_yuv_to_rgb_color_matrix uniform_opacity
+        "%s\n"
+        "%s\n"
+        "void main(void) {\n"
+        "  vec4 yuva, rgba;\n"
+        "  yuva.x = texture2D(Ytex, v_texcoord).r;\n"
+        "  yuva.y = texture2D(Utex, v_texcoord).r;\n"
+        "  yuva.z = texture2D(Vtex, v_texcoord).r;\n"
+        "  yuva.a = 1.0;\n"
+        "  yuva = swizzle(yuva, " UNIFORM_SWIZZLE_COMPONENTS_NAME ");\n"
+        "  rgba.rgb = yuv_to_rgb (yuva.xyz, " UNIFORM_YUV_OFFSET_NAME ", " UNIFORM_YUV_YCOEFF_NAME ", " UNIFORM_YUV_UCOEFF_NAME ", " UNIFORM_YUV_VCOEFF_NAME ");\n"
+        "  rgba.a = yuva.a;\n"
+        "  gl_FragColor = rgba * " UNIFORM_OPACITY_NAME ";\n"
+        //"  gl_FragColor = vec4(yuva.x, 0.0, 0.0, 1.0);\n"
+        "}\n", is_gles2 ? gles2_precision : "", yuv_to_rgb, swizzle);
+      g_clear_pointer (&yuv_to_rgb, g_free);
+      g_clear_pointer (&swizzle, g_free);
+      return ret;
+    }
+    default:
+      return NULL;
+  }
+}
+
+QSGMaterialShader *
+GstQSGMaterial::createShader() const
+{
+  GstVideoFormat v_format = GST_VIDEO_INFO_FORMAT (&this->v_info);
+  char *vertex = vertexShaderForFormat(v_format);
+  char *fragment = fragmentShaderForFormat(v_format, gst_gl_context_get_current ());
+
+  if (!vertex || !fragment)
+    return nullptr;
+
+  return new GstQSGMaterialShader(v_format, vertex, fragment);
+}
+
+void
+GstQSGMaterial::initYuvShaders (GstQSGMaterialShader *shader,
+    const GstVideoColorimetry *cinfo)
+{
+  g_return_if_fail (shader);
+
+  if (cinfo && gst_video_colorimetry_matches (cinfo,
+          GST_VIDEO_COLORIMETRY_BT709)) {
+    this->cms_offset = (gfloat *) from_yuv_bt709_offset;
+    this->cms_ycoeff = (gfloat *) from_yuv_bt709_rcoeff;
+    this->cms_ucoeff = (gfloat *) from_yuv_bt709_gcoeff;
+    this->cms_vcoeff = (gfloat *) from_yuv_bt709_bcoeff;
+  } else {
+    /* defaults/bt601 */
+    this->cms_offset = (gfloat *) from_yuv_bt601_offset;
+    this->cms_ycoeff = (gfloat *) from_yuv_bt601_rcoeff;
+    this->cms_ucoeff = (gfloat *) from_yuv_bt601_gcoeff;
+    this->cms_vcoeff = (gfloat *) from_yuv_bt601_bcoeff;
+  }
+
+  shader->program()->setUniformValue(shader->cms_uniform_offset, QVector3D(this->cms_offset[0], this->cms_offset[1], this->cms_offset[2]));
+  shader->program()->setUniformValue(shader->cms_uniform_ycoeff, QVector3D(this->cms_ycoeff[0], this->cms_ycoeff[1], this->cms_ycoeff[2]));
+  shader->program()->setUniformValue(shader->cms_uniform_ucoeff, QVector3D(this->cms_ucoeff[0], this->cms_ucoeff[1], this->cms_ucoeff[2]));
+  shader->program()->setUniformValue(shader->cms_uniform_vcoeff, QVector3D(this->cms_vcoeff[0], this->cms_vcoeff[1], this->cms_vcoeff[2]));
+}
+
+/* only called from the streaming thread with scene graph thread blocked */
+void
+GstQSGMaterial::setCaps (GstCaps * caps)
+{
+  GST_LOG ("%p setCaps %" GST_PTR_FORMAT, this, caps);
+
+  gst_video_info_from_caps (&this->v_info, caps);
+}
+
+/* only called from the streaming thread with scene graph thread blocked */
+gboolean
+GstQSGMaterial::setBuffer (GstBuffer * buffer)
+{
+  GstGLContext *qt_context;
+  gboolean ret = FALSE;
+
+  /* FIXME: update more state here */
+  if (gst_buffer_replace (&this->buffer_, buffer)) {
+    GST_LOG ("%p setBuffer new buffer %" GST_PTR_FORMAT, this, buffer);
+
+    this->buffer_was_bound = FALSE;
+    ret = TRUE;
+  }
+
+  qt_context = gst_gl_context_get_current ();
+  GST_DEBUG ("%p setBuffer with qt context %" GST_PTR_FORMAT, this, qt_context);
+
+  g_weak_ref_set (&this->qt_context_ref_, qt_context);
+
+  return ret;
+}
+
+/* only called from the streaming thread with scene graph thread blocked */
+GstBuffer *
+GstQSGMaterial::getBuffer (gboolean * was_bound)
+{
+  GstBuffer *buffer = NULL;
+
+  if (this->buffer_)
+    buffer = gst_buffer_ref (this->buffer_);
+  if (was_bound)
+    *was_bound = this->buffer_was_bound;
+
+  return buffer;
+}
+
+void
+GstQSGMaterial::bind(GstQSGMaterialShader *shader, GstVideoFormat v_format)
+{
+  const GstGLFuncs *gl;
+  GstGLContext *context, *qt_context = NULL;
+  GstGLSyncMeta *sync_meta;
+  GstMemory *mem;
+  gboolean use_dummy_tex = TRUE;
+
+  if (this->v_frame.buffer) {
+    gst_video_frame_unmap (&this->v_frame);
+    memset (&this->v_frame, 0, sizeof (this->v_frame));
+  }
+
+  if (!this->buffer_)
+    goto out;
+  if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN)
+    goto out;
+
+  this->mem_ = gst_buffer_peek_memory (this->buffer_, 0);
+  if (!this->mem_)
+    goto out;
+
+  qt_context = GST_GL_CONTEXT (g_weak_ref_get (&this->qt_context_ref_));
+  if (!qt_context)
+    goto out;
+
+  gl = qt_context->gl_vtable;
+
+  /* FIXME: should really lock the memory to prevent write access */
+  if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_,
+        (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
+    g_assert_not_reached ();
+    goto out;
+  }
+
+  GST_DEBUG ("%p attempting to bind with context %" GST_PTR_FORMAT, this, qt_context);
+
+  mem = gst_buffer_peek_memory (this->buffer_, 0);
+  g_assert (gst_is_gl_memory (mem));
+
+  context = ((GstGLBaseMemory *)mem)->context;
+
+  sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_);
+  if (!sync_meta)
+    sync_meta = gst_buffer_add_gl_sync_meta (context, this->sync_buffer_);
+
+  gst_gl_sync_meta_set_sync_point (sync_meta, context);
+
+  gst_gl_sync_meta_wait (sync_meta, qt_context);
+
+  if (this->v_frame.info.finfo->flags & GST_VIDEO_FORMAT_FLAG_YUV)
+    initYuvShaders (shader, &this->v_frame.info.colorimetry);
+  else
+    this->cms_offset = this->cms_ycoeff = this->cms_ucoeff = this->cms_vcoeff = NULL;
+
+  /* reversed iteration order so that glActiveTexture(GL_TEXTURE0) is last which keeps
+   * us in the default GL state expected by several other qml components
+   */
+  for (int i = GST_VIDEO_FRAME_N_PLANES (&this->v_frame) - 1; i >= 0; i--) {
+    guint tex_id = *(guint *) this->v_frame.data[i];
+    shader->program()->setUniformValue(shader->tex_uniforms[i], i);
+    gl->ActiveTexture (GL_TEXTURE0 + i);
+    GST_LOG ("%p binding for plane %d Qt texture %u", this, i, tex_id);
+
+    gl->BindTexture (GL_TEXTURE_2D, tex_id);
+  }
+
+  /* Texture was successfully bound, so we do not need
+   * to use the dummy texture */
+  use_dummy_tex = FALSE;
+
+  this->buffer_was_bound = TRUE;
+
+out:
+  gst_clear_object (&qt_context);
+
+  if (G_UNLIKELY (use_dummy_tex)) {
+    QOpenGLContext *qglcontext = QOpenGLContext::currentContext ();
+    QOpenGLFunctions *funcs = qglcontext->functions ();
+    const GstVideoFormatInfo *finfo = gst_video_format_get_info (v_format);
+
+    if (finfo->flags == GST_VIDEO_FORMAT_FLAG_YUV)
+      initYuvShaders (shader, nullptr);
+
+    /* Create dummy texture if not already present.
+     * Use the Qt OpenGL functions instead of the GstGL ones,
+     * since we are using the Qt OpenGL context here, and we must
+     * be able to delete the texture in the destructor. */
+    for (int i = finfo->n_planes - 1; i >= 0; i--) {
+      shader->program()->setUniformValue(shader->tex_uniforms[i], i);
+      funcs->glActiveTexture(GL_TEXTURE0 + i);
+
+      if (this->dummy_textures[i] == 0) {
+        /* Make this a black 64x64 pixel RGBA texture.
+         * This size and format is supported pretty much everywhere, so these
+         * are a safe pick. (64 pixel sidelength must be supported according
+         * to the GLES2 spec, table 6.18.)
+         * Set min/mag filters to GL_LINEAR to make sure no mipmapping is used. */
+        const int tex_sidelength = 64;
+
+        std::vector < guint8 > dummy_data (tex_sidelength * tex_sidelength * 4, 0);
+        guint8 *data = dummy_data.data();
+
+        switch (v_format) {
+          case GST_VIDEO_FORMAT_RGBA:
+          case GST_VIDEO_FORMAT_BGRA:
+          case GST_VIDEO_FORMAT_RGB:
+            for (gsize j = 0; j < tex_sidelength; j++) {
+              for (gsize k = 0; k < tex_sidelength; k++) {
+                data[(j * tex_sidelength + k) * 4 + 3] = 0xFF; // opaque
+              }
+            }
+            break;
+          case GST_VIDEO_FORMAT_YV12:
+            if (i == 1 || i == 2) {
+              for (gsize j = 0; j < tex_sidelength; j++) {
+                for (gsize k = 0; k < tex_sidelength; k++) {
+                  data[(j * tex_sidelength + k) * 4 + 0] = 0x7F;
+                }
+              }
+            }
+            break;
+          default:
+            g_assert_not_reached ();
+            break;
+        }
+
+        funcs->glGenTextures (1, &this->dummy_textures[i]);
+        funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_textures[i]);
+        funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        funcs->glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_sidelength,
+            tex_sidelength, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+      }
+
+      g_assert (this->dummy_textures[i] != 0);
+
+      funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_textures[i]);
+      GST_LOG ("%p binding for plane %d fallback dummy Qt texture %u", this, i, this->dummy_textures[i]);
+    }
+  }
+}
diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgmaterial.h
similarity index 61%
rename from ext/qt/gstqsgtexture.h
rename to ext/qt/gstqsgmaterial.h
index d61b1cdea36badee454571212f964df57e8c8587..b9fa88ae835fa9e3309af9e0f248518d999bce87 100644
--- a/ext/qt/gstqsgtexture.h
+++ b/ext/qt/gstqsgmaterial.h
@@ -18,44 +18,55 @@
  * Boston, MA 02110-1301, USA.
  */
 
-#ifndef __GST_QSG_TEXTURE_H__
-#define __GST_QSG_TEXTURE_H__
+#ifndef __GST_QSG_MATERIAL_H__
+#define __GST_QSG_MATERIAL_H__
 
 #include <gst/gst.h>
 #include <gst/video/video.h>
 #include <gst/gl/gl.h>
 
 #include "gstqtgl.h"
-#include <QtQuick/QSGTexture>
+#include <QtQuick/QSGMaterial>
+#include <QtQuick/QSGMaterialShader>
 #include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLShaderProgram>
 
-class GstQSGTexture : public QSGTexture, protected QOpenGLFunctions
+class GstQSGMaterialShader;
+
+class GstQSGMaterial : public QSGMaterial
 {
-    Q_OBJECT
+protected:
+    GstQSGMaterial();
+    ~GstQSGMaterial();
 public:
-    GstQSGTexture ();
-    ~GstQSGTexture ();
+    static GstQSGMaterial *new_for_format (GstVideoFormat format);
 
     void setCaps (GstCaps * caps);
     gboolean setBuffer (GstBuffer * buffer);
     GstBuffer * getBuffer (gboolean * was_bound);
+    bool compatibleWith(GstVideoInfo *v_info);
+
+    void bind(GstQSGMaterialShader *, GstVideoFormat);
 
-    /* QSGTexture */
-    void bind ();
-    int textureId () const;
-    QSize textureSize () const;
-    bool hasAlphaChannel () const;
-    bool hasMipmaps () const;
+    /* QSGMaterial */
+    QSGMaterialShader *createShader() const override;
 
 private:
+    void initYuvShaders(GstQSGMaterialShader *shader,
+        const GstVideoColorimetry *cinfo);
+
     GstBuffer * buffer_;
     gboolean buffer_was_bound;
     GstBuffer * sync_buffer_;
     GWeakRef qt_context_ref_;
     GstMemory * mem_;
-    GLuint dummy_tex_id_;
     GstVideoInfo v_info;
     GstVideoFrame v_frame;
+    float *cms_offset;
+    float *cms_ycoeff;
+    float *cms_ucoeff;
+    float *cms_vcoeff;
+    guint dummy_textures[GST_VIDEO_MAX_PLANES];
 };
 
-#endif /* __GST_QSG_TEXTURE_H__ */
+#endif /* __GST_QSG_MATERIAL_H__ */
diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc
deleted file mode 100644
index 663696bc5970be00a3b9c38ef139a20b1a3dc380..0000000000000000000000000000000000000000
--- a/ext/qt/gstqsgtexture.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <vector>
-#include <stdio.h>
-
-#include <gst/video/video.h>
-#include <gst/gl/gl.h>
-#include <gst/gl/gstglfuncs.h>
-#include "gstqsgtexture.h"
-
-#define GST_CAT_DEFAULT gst_qsg_texture_debug
-GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
-
-GstQSGTexture::GstQSGTexture ()
-{
-  static gsize _debug;
-
-  initializeOpenGLFunctions();
-
-  if (g_once_init_enter (&_debug)) {
-    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtqsgtexture", 0,
-        "Qt Scenegraph Texture");
-    g_once_init_leave (&_debug, 1);
-  }
-
-  g_weak_ref_init (&this->qt_context_ref_, NULL);
-  gst_video_info_init (&this->v_info);
-
-  this->buffer_ = NULL;
-  this->buffer_was_bound = FALSE;
-  this->sync_buffer_ = gst_buffer_new ();
-  this->dummy_tex_id_ = 0;
-}
-
-GstQSGTexture::~GstQSGTexture ()
-{
-  g_weak_ref_clear (&this->qt_context_ref_);
-  gst_buffer_replace (&this->buffer_, NULL);
-  gst_buffer_replace (&this->sync_buffer_, NULL);
-  this->buffer_was_bound = FALSE;
-  if (this->dummy_tex_id_ && QOpenGLContext::currentContext ()) {
-    QOpenGLContext::currentContext ()->functions ()->glDeleteTextures (1,
-        &this->dummy_tex_id_);
-  }
-}
-
-/* only called from the streaming thread with scene graph thread blocked */
-void
-GstQSGTexture::setCaps (GstCaps * caps)
-{
-  GST_LOG ("%p setCaps %" GST_PTR_FORMAT, this, caps);
-
-  gst_video_info_from_caps (&this->v_info, caps);
-}
-
-/* only called from the streaming thread with scene graph thread blocked */
-gboolean
-GstQSGTexture::setBuffer (GstBuffer * buffer)
-{
-  GST_LOG ("%p setBuffer %" GST_PTR_FORMAT, this, buffer);
-  /* FIXME: update more state here */
-  if (!gst_buffer_replace (&this->buffer_, buffer))
-    return FALSE;
-
-  this->buffer_was_bound = FALSE;
-
-  g_weak_ref_set (&this->qt_context_ref_, gst_gl_context_get_current ());
-
-  return TRUE;
-}
-
-/* only called from the streaming thread with scene graph thread blocked */
-GstBuffer *
-GstQSGTexture::getBuffer (gboolean * was_bound)
-{
-  GstBuffer *buffer = NULL;
-
-  if (this->buffer_)
-    buffer = gst_buffer_ref (this->buffer_);
-  if (was_bound)
-    *was_bound = this->buffer_was_bound;
-
-  return buffer;
-}
-
-/* only called from qt's scene graph render thread */
-void
-GstQSGTexture::bind ()
-{
-  const GstGLFuncs *gl;
-  GstGLContext *context, *qt_context;
-  GstGLSyncMeta *sync_meta;
-  GstMemory *mem;
-  guint tex_id;
-  gboolean use_dummy_tex = TRUE;
-
-  qt_context = GST_GL_CONTEXT (g_weak_ref_get (&this->qt_context_ref_));
-  if (!qt_context)
-    goto out;
-
-  if (!this->buffer_)
-    goto out;
-  if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN)
-    goto out;
-
-  this->mem_ = gst_buffer_peek_memory (this->buffer_, 0);
-  if (!this->mem_)
-    goto out;
-
-  gl = qt_context->gl_vtable;
-
-  /* FIXME: should really lock the memory to prevent write access */
-  if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_,
-        (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
-    g_assert_not_reached ();
-    goto out;
-  }
-
-  mem = gst_buffer_peek_memory (this->buffer_, 0);
-  g_assert (gst_is_gl_memory (mem));
-
-  context = ((GstGLBaseMemory *)mem)->context;
-
-  sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_);
-  if (!sync_meta)
-    sync_meta = gst_buffer_add_gl_sync_meta (context, this->sync_buffer_);
-
-  gst_gl_sync_meta_set_sync_point (sync_meta, context);
-
-  gst_gl_sync_meta_wait (sync_meta, qt_context);
-
-  tex_id = *(guint *) this->v_frame.data[0];
-  GST_LOG ("%p binding Qt texture %u", this, tex_id);
-
-  gl->BindTexture (GL_TEXTURE_2D, tex_id);
-
-  gst_video_frame_unmap (&this->v_frame);
-
-  /* Texture was successfully bound, so we do not need
-   * to use the dummy texture */
-  use_dummy_tex = FALSE;
-
-  this->buffer_was_bound = TRUE;
-
-out:
-  gst_clear_object (&qt_context);
-
-  if (G_UNLIKELY (use_dummy_tex)) {
-    QOpenGLContext *qglcontext = QOpenGLContext::currentContext ();
-    QOpenGLFunctions *funcs = qglcontext->functions ();
-
-    /* Create dummy texture if not already present.
-     * Use the Qt OpenGL functions instead of the GstGL ones,
-     * since we are using the Qt OpenGL context here, and we must
-     * be able to delete the texture in the destructor. */
-    if (this->dummy_tex_id_ == 0) {
-      /* Make this a black 64x64 pixel RGBA texture.
-       * This size and format is supported pretty much everywhere, so these
-       * are a safe pick. (64 pixel sidelength must be supported according
-       * to the GLES2 spec, table 6.18.)
-       * Set min/mag filters to GL_LINEAR to make sure no mipmapping is used. */
-      const int tex_sidelength = 64;
-      std::vector < guint8 > dummy_data (tex_sidelength * tex_sidelength * 4, 0);
-
-      funcs->glGenTextures (1, &this->dummy_tex_id_);
-      funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_tex_id_);
-      funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-      funcs->glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_sidelength,
-          tex_sidelength, 0, GL_RGBA, GL_UNSIGNED_BYTE, &dummy_data[0]);
-    }
-
-    g_assert (this->dummy_tex_id_ != 0);
-
-    funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_tex_id_);
-    GST_LOG ("%p binding fallback dummy Qt texture %u", this, this->dummy_tex_id_);
-  }
-}
-
-/* can be called from any thread */
-int
-GstQSGTexture::textureId () const
-{
-  int tex_id = 0;
-
-  if (this->buffer_) {
-    GstMemory *mem = gst_buffer_peek_memory (this->buffer_, 0);
-
-    tex_id = ((GstGLMemory *) mem)->tex_id;
-  }
-
-  GST_LOG ("%p get texture id %u", this, tex_id);
-
-  return tex_id;
-}
-
-/* can be called from any thread */
-QSize
-GstQSGTexture::textureSize () const
-{
-  if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN)
-    return QSize (0, 0);
-
-  GST_TRACE ("%p get texture size %ux%u", this, this->v_info.width,
-      this->v_info.height);
-
-  return QSize (this->v_info.width, this->v_info.height);
-}
-
-/* can be called from any thread */
-bool
-GstQSGTexture::hasAlphaChannel () const
-{
-  const bool has_alpha = GST_VIDEO_FORMAT_INFO_HAS_ALPHA(this->v_info.finfo);
-
-  GST_LOG ("%p get has alpha channel %u", this, has_alpha);
-
-  return has_alpha;
-}
-
-/* can be called from any thread */
-bool
-GstQSGTexture::hasMipmaps () const
-{
-  return false;
-}
diff --git a/ext/qt/gstqtglutility.cc b/ext/qt/gstqtglutility.cc
index 84d56189675389feda9043d6c2f5df9343cf596f..a9d2f38fe35cbb0087b3a35307bf42970d7b4775 100644
--- a/ext/qt/gstqtglutility.cc
+++ b/ext/qt/gstqtglutility.cc
@@ -44,7 +44,7 @@
 #include <gst/gl/wayland/gstgldisplay_wayland.h>
 #endif
 
-#if GST_GL_HAVE_WINDOW_VIV_FB
+#if GST_GL_HAVE_WINDOW_VIV_FB && defined (HAVE_QT_VIV_FB)
 #include <gst/gl/viv-fb/gstgldisplay_viv_fb.h>
 #endif
 
@@ -117,7 +117,7 @@ gst_qt_get_gl_display (gboolean sink)
   }
 #elif GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS)
   if (QString::fromUtf8("eglfs") == app->platformName()) {
-#if GST_GL_HAVE_WINDOW_VIV_FB
+#if GST_GL_HAVE_WINDOW_VIV_FB && defined (HAVE_QT_VIV_FB)
     /* FIXME: Could get the display directly from Qt like this
      * QPlatformNativeInterface *native =
      *     QGuiApplication::platformNativeInterface();
@@ -202,7 +202,7 @@ gst_qt_get_gl_wrapcontext (GstGLDisplay * display,
   }
 #endif
 #if GST_GL_HAVE_PLATFORM_EGL && defined (HAVE_QT_EGLFS)
-#if GST_GL_HAVE_WINDOW_VIV_FB
+#if GST_GL_HAVE_WINDOW_VIV_FB && defined (HAVE_QT_VIV_FB)
   if (GST_IS_GL_DISPLAY_VIV_FB (display)) {
 #else
   if (GST_IS_GL_DISPLAY_EGL (display)) {
diff --git a/ext/qt/gstqtoverlay.cc b/ext/qt/gstqtoverlay.cc
index 75de99e60d241510464d0592b27a92e1009833dd..646ef0e026044039967c983746c61d8683125649 100644
--- a/ext/qt/gstqtoverlay.cc
+++ b/ext/qt/gstqtoverlay.cc
@@ -91,12 +91,41 @@
 #define GST_CAT_DEFAULT gst_debug_qt_gl_overlay
 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
 
+/* *INDENT-OFF* */
+static GstStaticPadTemplate qt_overlay_src_pad_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
+      "format = (string) RGBA, "
+      "width = " GST_VIDEO_SIZE_RANGE ", "
+      "height = " GST_VIDEO_SIZE_RANGE ", "
+      "framerate = " GST_VIDEO_FPS_RANGE ","
+      "texture-target = (string) 2D"
+    ));
+
+static GstStaticPadTemplate qt_overlay_sink_pad_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw(ANY), "
+      "format = (string) { RGBA, BGRA, YV12 }, "
+      "width = " GST_VIDEO_SIZE_RANGE ", "
+      "height = " GST_VIDEO_SIZE_RANGE ", "
+      "framerate = " GST_VIDEO_FPS_RANGE ","
+      "texture-target = (string) 2D"
+    ));
+/* *INDENT-ON* */
+
 static void gst_qt_overlay_finalize (GObject * object);
 static void gst_qt_overlay_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * param_spec);
 static void gst_qt_overlay_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * param_spec);
 
+static GstCaps * gst_qt_overlay_transform_internal_caps (GstGLFilter * filter,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
+
 static gboolean gst_qt_overlay_gl_start (GstGLBaseFilter * bfilter);
 static void gst_qt_overlay_gl_stop (GstGLBaseFilter * bfilter);
 static gboolean gst_qt_overlay_gl_set_caps (GstGLBaseFilter * bfilter,
@@ -193,7 +222,10 @@ gst_qt_overlay_class_init (GstQtOverlayClass * klass)
       g_signal_new ("qml-scene-destroyed", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
 
-  gst_gl_filter_add_rgba_pad_templates (glfilter_class);
+  gst_element_class_add_static_pad_template (element_class,
+      &qt_overlay_src_pad_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &qt_overlay_sink_pad_template);
 
   btrans_class->prepare_output_buffer = gst_qt_overlay_prepare_output_buffer;
   btrans_class->transform = gst_qt_overlay_transform;
@@ -202,6 +234,8 @@ gst_qt_overlay_class_init (GstQtOverlayClass * klass)
   glbasefilter_class->gl_stop = gst_qt_overlay_gl_stop;
   glbasefilter_class->gl_set_caps = gst_qt_overlay_gl_set_caps;
 
+  glfilter_class->transform_internal_caps = gst_qt_overlay_transform_internal_caps;
+
   element_class->change_state = gst_qt_overlay_change_state;
 }
 
@@ -398,6 +432,24 @@ gst_qt_overlay_gl_set_caps (GstGLBaseFilter * bfilter, GstCaps * in_caps,
   return TRUE;
 }
 
+static GstCaps *
+gst_qt_overlay_transform_internal_caps (GstGLFilter * filter,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
+{
+  GstCaps *tmp = GST_GL_FILTER_CLASS (parent_class)->transform_internal_caps (filter, direction, caps, filter_caps);
+  int i, n;
+
+  n = gst_caps_get_size (tmp);
+  for (i = 0; i < n; i++) {
+    GstStructure *s = gst_caps_get_structure (tmp, i);
+
+    gst_structure_remove_fields (s, "format", "colorimetry", "chroma-site",
+        "texture-target", NULL);
+  }
+
+  return tmp;
+}
+
 static GstFlowReturn
 gst_qt_overlay_prepare_output_buffer (GstBaseTransform * btrans,
     GstBuffer * buffer, GstBuffer ** outbuf)
diff --git a/ext/qt/gstqtsink.cc b/ext/qt/gstqtsink.cc
index e0f062b8e55cbd8b0b065d594e2a7d325558b6dc..25c643528e359c172b7d8ff56a43ea21f15b9188 100644
--- a/ext/qt/gstqtsink.cc
+++ b/ext/qt/gstqtsink.cc
@@ -108,7 +108,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
-    "format = (string) { RGB, RGBA }, "
+    "format = (string) { RGB, RGBA, BGRA, YV12 }, "
     "width = " GST_VIDEO_SIZE_RANGE ", "
     "height = " GST_VIDEO_SIZE_RANGE ", "
     "framerate = " GST_VIDEO_FPS_RANGE ", "
diff --git a/ext/qt/gstqtsrc.cc b/ext/qt/gstqtsrc.cc
index a405172925e6bb4b8c1923eb1bb9027d7d213e84..159d6ef747559cc7322de1019df4d6bf7c97c42c 100644
--- a/ext/qt/gstqtsrc.cc
+++ b/ext/qt/gstqtsrc.cc
@@ -434,6 +434,8 @@ static GstFlowReturn
 gst_qt_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
 {
   GstQtSrc *qt_src = GST_QT_SRC (psrc);
+  GstGLContext* context = qt_src->context;
+  GstGLSyncMeta *sync_meta;
 
   GST_DEBUG_OBJECT (qt_src, "setting buffer %p", buffer);
 
@@ -442,6 +444,10 @@ gst_qt_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
     return GST_FLOW_ERROR;
   }
 
+  sync_meta = gst_buffer_get_gl_sync_meta(buffer);
+  if (sync_meta)
+      gst_gl_sync_meta_wait(sync_meta, context);
+
   if (!qt_src->downstream_supports_affine_meta) {
     if (qt_src->pending_image_orientation) {
       /* let downstream know the image orientation is vertical filp */
diff --git a/ext/qt/meson.build b/ext/qt/meson.build
index b1c47f05fecdf875bdbfffdbd5fccbd41e8aaa43..192a644e48fbe70cdbe1cef635695b11cc0a42b6 100644
--- a/ext/qt/meson.build
+++ b/ext/qt/meson.build
@@ -1,7 +1,7 @@
 sources = [
   'gstplugin.cc',
   'gstqtelement.cc',
-  'gstqsgtexture.cc',
+  'gstqsgmaterial.cc',
   'gstqtglutility.cc',
   'gstqtoverlay.cc',
   'gstqtsink.cc',
@@ -14,7 +14,6 @@ sources = [
 moc_headers = [
   'qtitem.h',
   'qtwindow.h',
-  'gstqsgtexture.h',
   'qtglrenderer.h',
 ]
 
@@ -22,6 +21,9 @@ moc_headers = [
 # deciding whether to build the qt5 examples
 qt5qml_dep = dependency('', required: false)
 qt5_option = get_option('qt5')
+qt5_egl = get_option('qt-egl')
+qt5_wayland = get_option('qt-wayland')
+qt5_x11 = get_option('qt-x11')
 qt5_method = get_option('qt-method')
 
 if qt5_option.disabled()
@@ -40,23 +42,16 @@ if not add_languages('cpp', native: false, required: qt5_option)
 endif
 
 qt5_mod = import('qt5')
-qt5qml_dep = dependency('qt5', modules : ['Core', 'Gui', 'Qml', 'Quick'],
-                        method: qt5_method, required: qt5_option, static: host_system == 'ios')
-
-# On Linux, distros often have the Qt5 pkg-config files and moc in separate
-# packages, so the user may not have both installed. Check for moc and ensure
-# that it's installed.
-# We don't do this check on other OSes because they need to be able to simply
-# point the `QMAKE` env var to `qmake` to build against a particular Qt5.
-if host_system == 'linux' and not meson.is_cross_build()
-  moc = find_program('moc-qt5', 'moc', required : qt5_option)
-else
-  # We only check if `moc` was found, and then discard it, so we can fake it.
-  # This is also a good unit test of the fact that we *don't* use it.
-  moc = declare_dependency()
+if not qt5_mod.has_tools(method: qt5_method)
+  if qt5_option.enabled()
+    error('qt5 qmlglsink plugin is enabled, but qt specific tools were not found')
+  endif
+  subdir_done()
 endif
 
-if not qt5qml_dep.found() or not moc.found()
+qt5qml_dep = dependency('qt5', modules : ['Core', 'Gui', 'Qml', 'Quick'],
+                        method: qt5_method, required: qt5_option, static: host_system == 'ios')
+if not qt5qml_dep.found()
   subdir_done()
 endif
 
@@ -68,18 +63,23 @@ have_qt_windowing = false
 # Look for the QPA platform native interface header
 qpa_header_path = join_paths(qt5qml_dep.version(), 'QtGui')
 qpa_header = join_paths(qpa_header_path, 'qpa/qplatformnativeinterface.h')
-if cxx.has_header(qpa_header, dependencies : qt5qml_dep)
+need_qpa_include = qt5_option.enabled() and (host_system == 'android' or qt5_wayland.enabled())
+if cxx.has_header(qpa_header, dependencies : qt5qml_dep, required: need_qpa_include)
   qt_defines += '-DHAVE_QT_QPA_HEADER'
   qt_defines += '-DQT_QPA_HEADER=' + '<@0@>'.format(qpa_header)
   have_qpa_include = true
   message('Found QtGui QPA header in ' + qpa_header_path)
 endif
 
-# Try to come up with all the platform/winsys combinations that will work
+## Try to come up with all the platform/winsys combinations that will work
 
-if gst_gl_have_window_x11 and gst_gl_have_platform_glx
-  # FIXME: automagic
-  qt5x11extras = dependency('qt5', modules : ['X11Extras'], method: qt5_method, required : false)
+# X11 windowing
+qt5_x11 = qt5_x11 \
+    .require(gstglx11_dep.found(), error_message: 'gstreamer-gl-x11-1.0 is required') \
+    .require(gst_gl_have_window_x11, error_message: 'x11 windowing support in gstreamer-gl is required') \
+    .require(gst_gl_have_platform_glx, error_message: 'glx platform support in gstreamer-gl is required')
+if qt5_x11.allowed()
+  qt5x11extras = dependency('qt5', modules : ['X11Extras'], method: qt5_method, required: qt5_x11)
   if qt5x11extras.found()
     optional_deps += [qt5x11extras, gstglx11_dep]
     qt_defines += ['-DHAVE_QT_X11']
@@ -87,78 +87,117 @@ if gst_gl_have_window_x11 and gst_gl_have_platform_glx
   endif
 endif
 
-if gst_gl_have_platform_egl
-  # Embedded linux (e.g. i.MX6) with or without windowing support
+# Wayland windowing
+qt5_wayland = qt5_wayland \
+    .require(gstglwayland_dep.found(), error_message: 'gstreamer-gl-wayland-1.0 is required') \
+    .require(gst_gl_have_window_wayland, error_message: 'wayland windowing support in gstreamer-gl is required') \
+    .require(gst_gl_have_platform_egl, error_message: 'egl platform support in gstreamer-gl is required') \
+    .require(have_qpa_include, error_message: 'QPA platform native interface header is required')
+if qt5_wayland.allowed()
+  qt5waylandextras = dependency('qt5', modules : ['WaylandClient'], method: qt5_method, required: qt5_wayland)
+  if qt5waylandextras.found()
+    optional_deps += [qt5waylandextras, gstglwayland_dep]
+    qt_defines += ['-DHAVE_QT_WAYLAND']
+    have_qt_windowing = true
+  endif
+endif
+
+# EGL windowing for Embedded linux (e.g. i.MX6) with or without windowing
+# support
+qt5_egl = qt5_egl \
+    .require(host_system == 'linux') \
+    .require(gstglegl_dep.found(), error_message: 'gstreamer-gl-egl-1.0 is required') \
+    .require(gst_gl_have_platform_egl, error_message: 'egl platform support in gstreamer-gl is required')
+if qt5_egl.allowed()
   qt_defines += ['-DHAVE_QT_EGLFS']
   optional_deps += gstglegl_dep
   have_qt_windowing = true
-  if have_qpa_include
-    # Wayland windowing
-    if gst_gl_have_window_wayland
-      # FIXME: automagic
-      qt5waylandextras = dependency('qt5', modules : ['WaylandClient'], method: qt5_method, required : false)
-      if qt5waylandextras.found()
-        optional_deps += [qt5waylandextras, gstglwayland_dep]
-        qt_defines += ['-DHAVE_QT_WAYLAND']
-        have_qt_windowing = true
-      endif
-    endif
-    # Android windowing
-    if gst_gl_have_window_android
-      # FIXME: automagic
-      qt5androidextras = dependency('qt5', modules : ['AndroidExtras'], method: qt5_method, required : false)
-      # for gl functions in QtGui/qopenglfunctions.h
-      # FIXME: automagic
-      glesv2_dep = cc.find_library('GLESv2', required : false)
-      if glesv2_dep.found() and qt5androidextras.found()
-        optional_deps += [qt5androidextras, glesv2_dep]
-        qt_defines += ['-DHAVE_QT_ANDROID']
-        have_qt_windowing = true
-        # Needed for C++11 support in Cerbero. People building with Android
-        # in some other way need to add the necessary bits themselves.
-        optional_deps += dependency('gnustl', required : false)
-      endif
+
+  # EGL windowing for Vivante Framebuffer (e.g. i.MX6)
+  if gstglviv_fb_dep.found()
+    qt_defines += ['-DHAVE_QT_VIV_FB']
+    optional_deps += gstglviv_fb_dep
+  endif
+endif
+
+# Android windowing
+if host_system == 'android'
+  qt5_android = qt5_option \
+      .require(gst_gl_have_window_android, error_message: 'android windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_egl, error_message: 'egl platform support in gstreamer-gl is required')
+  if gst_gl_have_window_android
+    qt5androidextras = dependency('qt5', modules : ['AndroidExtras'], method: qt5_method, required : qt5_android)
+    # for gl functions in QtGui/qopenglfunctions.h
+    glesv2_dep = cc.find_library('GLESv2', required : qt5_android)
+    if glesv2_dep.found() and qt5androidextras.found()
+      optional_deps += [qt5androidextras, glesv2_dep]
+      qt_defines += ['-DHAVE_QT_ANDROID']
+      have_qt_windowing = true
     endif
   endif
 endif
 
-if gst_gl_have_platform_wgl and gst_gl_have_window_win32
-  # for wglMakeCurrent()
-  # FIXME: automagic
-  opengl32_dep = cc.find_library('opengl32', required : false)
-  if opengl32_dep.found()
-    qt_defines += ['-DHAVE_QT_WIN32']
-    optional_deps += opengl32_dep
-    have_qt_windowing = true
+# Win32 windowing
+if host_system == 'windows'
+  qt5_win32 = qt5_option \
+      .require(gst_gl_have_window_win32, error_message: 'win32 windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_wgl, error_message: 'wgl platform support in gstreamer-gl is required')
+  if qt5_win32.allowed()
+    # for wglMakeCurrent()
+    opengl32_dep = cc.find_library('opengl32', required : qt5_win32)
+    if opengl32_dep.found()
+      qt_defines += ['-DHAVE_QT_WIN32']
+      optional_deps += opengl32_dep
+      have_qt_windowing = true
+    endif
   endif
 endif
 
-if gst_gl_have_window_cocoa and gst_gl_have_platform_cgl
-  # FIXME: automagic
-  qt5macextras = dependency('qt5', modules : ['MacExtras'], method: qt5_method, required : false)
-  if qt5macextras.found()
-    qt_defines += ['-DHAVE_QT_MAC']
-    optional_deps += qt5macextras
-    have_qt_windowing = true
+# macOS windowing
+if host_system == 'darwin'
+  qt5_macos = qt5_option \
+      .require(gst_gl_have_window_cocoa, error_message: 'cocoa windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_cgl, error_message: 'cgl platform support in gstreamer-gl is required')
+  if qt5_macos.allowed()
+    qt5macextras = dependency('qt5', modules : ['MacExtras'], method: qt5_method, required : qt5_macos)
+    if qt5macextras.found()
+      qt_defines += ['-DHAVE_QT_MAC']
+      optional_deps += qt5macextras
+      have_qt_windowing = true
+    endif
   endif
 endif
 
-if gst_gl_have_window_eagl and gst_gl_have_platform_eagl
-  if host_machine.system() == 'ios'
+# iOS windowing
+if host_system == 'ios'
+  qt5_ios = qt5_option \
+      .require(gst_gl_have_window_eagl, error_message: 'eagl windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_eagl, error_message: 'eagl platform support in gstreamer-gl is required')
+  if qt5_ios.allowed()
     qt_defines += ['-DHAVE_QT_IOS']
     have_qt_windowing = true
   endif
 endif
 
-if have_qt_windowing
+if qt5_option.require(have_qt_windowing, error_message: 'No windowing, enable one of the qt-* windowing options').allowed()
+  # rpath is needed to be able to load the plugin on macOS inside the devenv
+  qmlgl_kwargs = {}
+  if host_system == 'darwin'
+    fs = import('fs')
+    qt_bindir = fs.parent(find_program('qmake').full_path())
+    qt_libdir = fs.parent(qt_bindir) / 'lib'
+    qmlgl_kwargs += {'build_rpath': qt_libdir}
+  endif
+
   # Build it!
-  moc_files = qt5_mod.preprocess(moc_headers : moc_headers)
+  moc_files = qt5_mod.preprocess(moc_headers : moc_headers, method: qt5_method)
   gstqmlgl = library('gstqmlgl', sources, moc_files,
     cpp_args : gst_plugins_good_args + qt_defines,
     link_args : noseh_link_args,
     include_directories: [configinc, libsinc],
     dependencies : [gst_dep, gstvideo_dep, gstgl_dep, gstglproto_dep, qt5qml_dep, optional_deps],
     override_options : ['cpp_std=c++11'],
+    kwargs: qmlgl_kwargs,
     install: true,
     install_dir : plugins_install_dir)
   plugins += [gstqmlgl]
diff --git a/ext/qt/qtglrenderer.cc b/ext/qt/qtglrenderer.cc
index ce453a9249e712ed60d2eb7ff9caa254319e4d61..6eacf2d947e3f1963a0f2b03e3370656de377fcb 100644
--- a/ext/qt/qtglrenderer.cc
+++ b/ext/qt/qtglrenderer.cc
@@ -9,6 +9,8 @@
 #include <QOpenGLFunctions>
 #include <QOpenGLFramebufferObject>
 #include <QAnimationDriver>
+#include <QCoreApplication>
+#include <QEventLoop>
 
 #include <gst/gl/gl.h>
 #include "gstqtgl.h"
@@ -152,7 +154,7 @@ shared_render_data_free (struct SharedRenderData * data)
     delete data->m_context;
   data->m_context = nullptr;
   if (data->m_surface)
-    delete data->m_surface;
+    data->m_surface->deleteLater();
   data->m_surface = nullptr;
 }
 
@@ -281,7 +283,6 @@ bool CreateSurfaceWorker::event(QEvent * ev)
         g_mutex_lock (&m_sharedRenderData->lock);
         m_sharedRenderData->m_surface = new GstBackingSurface;
         m_sharedRenderData->m_surface->create();
-        m_sharedRenderData->m_surface->moveToThread (m_sharedRenderData->m_renderThread);
         GST_TRACE ("%p created surface %p", m_sharedRenderData,
             m_sharedRenderData->m_surface);
         g_cond_broadcast (&m_sharedRenderData->cond);
diff --git a/ext/qt/qtitem.cc b/ext/qt/qtitem.cc
index 70370dd41f3ac31e79a1cda56157308c9fb14286..653a0835f2de4dbf0be235b335307effde16f18a 100644
--- a/ext/qt/qtitem.cc
+++ b/ext/qt/qtitem.cc
@@ -26,7 +26,7 @@
 
 #include <gst/video/video.h>
 #include "qtitem.h"
-#include "gstqsgtexture.h"
+#include "gstqsgmaterial.h"
 #include "gstqtglutility.h"
 
 #include <QtCore/QMutexLocker>
@@ -74,6 +74,7 @@ struct _QtGLVideoItemPrivate
   GstCaps *caps;
   GstVideoInfo new_v_info;
   GstVideoInfo v_info;
+  GstVideoRectangle v_rect;
 
   gboolean initted;
   GstGLDisplay *display;
@@ -283,9 +284,10 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode,
   if (!this->priv->initted)
     return oldNode;
 
-  QSGSimpleTextureNode *texNode = static_cast<QSGSimpleTextureNode *> (oldNode);
+  QSGGeometryNode *texNode = static_cast<QSGGeometryNode *> (oldNode);
   GstVideoRectangle src, dst, result;
-  GstQSGTexture *tex;
+  GstQSGMaterial *tex = nullptr;
+  QSGGeometry *geometry = nullptr;
 
   g_mutex_lock (&this->priv->lock);
 
@@ -300,13 +302,24 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode,
   if (gst_gl_context_get_current() == NULL)
     gst_gl_context_activate (this->priv->other_context, TRUE);
 
-  if (!texNode) {
-    texNode = new QSGSimpleTextureNode ();
-    texNode->setOwnsTexture (true);
-    texNode->setTexture (new GstQSGTexture ());
+  if (texNode) {
+    geometry = texNode->geometry();
+    tex = static_cast<GstQSGMaterial *>(texNode->material());
+    if (tex && !tex->compatibleWith(&this->priv->v_info)) {
+      delete texNode;
+      texNode = nullptr;
+    }
   }
 
-  tex = static_cast<GstQSGTexture *> (texNode->texture());
+  if (!texNode) {
+    texNode = new QSGGeometryNode();
+    geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+    texNode->setGeometry(geometry);
+    texNode->setFlag(QSGGeometryNode::Flag::OwnsGeometry);
+    tex = GstQSGMaterial::new_for_format(GST_VIDEO_INFO_FORMAT (&this->priv->v_info));
+    texNode->setMaterial(tex);
+    texNode->setFlag(QSGGeometryNode::Flag::OwnsMaterial);
+  }
 
   if ((old_buffer = tex->getBuffer(&was_bound))) {
     if (old_buffer == this->priv->buffer) {
@@ -360,7 +373,14 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode,
     result.h = boundingRect().height();
   }
 
-  texNode->setRect (QRectF (result.x, result.y, result.w, result.h));
+  QRectF rect(result.x, result.y, result.w, result.h);
+  QRectF sourceRect(0, 0, 1, 1);
+  QSGGeometry::updateTexturedRectGeometry(geometry, rect, sourceRect);
+  if(priv->v_rect.x != result.x || priv->v_rect.y != result.y ||
+     priv->v_rect.w != result.w || priv->v_rect.h != result.h) {
+    texNode->markDirty(QSGNode::DirtyGeometry);
+    priv->v_rect = result;
+  }
 
   g_mutex_unlock (&this->priv->lock);
 
diff --git a/ext/qt/qtplugin.pro b/ext/qt/qtplugin.pro
index d772ae50692afcca201ee3b71107a0eaf4b86124..8c2d0e975161fdfd9d14fa7b95cca1fe443e7948 100644
--- a/ext/qt/qtplugin.pro
+++ b/ext/qt/qtplugin.pro
@@ -81,7 +81,7 @@ SOURCES += \
     gstplugin.cc \
     gstqtelement.cc \
     gstqtglutility.cc \
-    gstqsgtexture.cc \
+    gstqsgmaterial.cc \
     gstqtoverlay.cc \
     gstqtsink.cc \
     gstqtsrc.cc \
@@ -91,7 +91,7 @@ SOURCES += \
 
 HEADERS += \
 	gstqtelements.h \
-    gstqsgtexture.h \
+    gstqsgmaterial.h \
     gstqtgl.h \
     gstqtglutility.h \
     gstqtoverlay.h \
diff --git a/ext/qt/qtwindow.cc b/ext/qt/qtwindow.cc
index daec3222b73e88e0327adc710c42e51f8d2bc4a7..e679bf9d64ed6219a92f27c0ee37a276bdc05ce2 100644
--- a/ext/qt/qtwindow.cc
+++ b/ext/qt/qtwindow.cc
@@ -27,7 +27,6 @@
 #include <gst/video/video.h>
 #include <gst/gl/gstglfuncs.h>
 #include "qtwindow.h"
-#include "gstqsgtexture.h"
 #include "gstqtglutility.h"
 
 #include <QtCore/QDateTime>
@@ -123,6 +122,8 @@ QtGLWindow::~QtGLWindow()
     gst_object_unref(this->priv->display);
   if (this->priv->context)
     gst_object_unref(this->priv->context);
+  if (this->priv->caps)
+      gst_caps_unref(this->priv->caps);
   g_free (this->priv);
   this->priv = NULL;
 }
diff --git a/ext/qt6/RGBA.frag b/ext/qt6/RGBA.frag
new file mode 100644
index 0000000000000000000000000000000000000000..c1ab12010700c575c9f720770bc08d1b246aaf03
--- /dev/null
+++ b/ext/qt6/RGBA.frag
@@ -0,0 +1,24 @@
+#version 440
+
+layout(location = 0) in vec2 vTexCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+  mat4 qt_Matrix;
+  ivec4 swizzle;
+  mat4 color_matrix;
+  float qt_Opacity;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D tex;
+
+vec4 swizzle(in vec4 texel, in ivec4 swizzle) {
+  return vec4(texel[swizzle[0]], texel[swizzle[1]], texel[swizzle[2]], texel[swizzle[3]]);
+}
+
+void main()
+{
+  vec4 texel = swizzle(texture(tex, vTexCoord), ubuf.swizzle);
+  fragColor = texel * ubuf.qt_Opacity;
+}
diff --git a/ext/qt6/YUV_TRIPLANAR.frag b/ext/qt6/YUV_TRIPLANAR.frag
new file mode 100644
index 0000000000000000000000000000000000000000..6c096848933af6128ac2a5809288f3ad2e403ab6
--- /dev/null
+++ b/ext/qt6/YUV_TRIPLANAR.frag
@@ -0,0 +1,37 @@
+#version 440
+
+layout(location = 0) in vec2 vTexCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+  mat4 qt_Matrix;
+  ivec4 swizzle;
+  mat4 color_matrix;
+  float qt_Opacity;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D Ytex;
+layout(binding = 2) uniform sampler2D Utex;
+layout(binding = 3) uniform sampler2D Vtex;
+
+vec4 swizzle(in vec4 texel, in ivec4 swizzle) {
+  return vec4(texel[swizzle[0]], texel[swizzle[1]], texel[swizzle[2]], texel[swizzle[3]]);
+}
+
+vec4 yuva_to_rgba(in vec4 yuva, in mat4 color_matrix) {
+  return yuva * color_matrix;
+}
+
+void main()
+{
+  vec4 yuva;
+  yuva.x = texture(Ytex, vTexCoord).r;
+  yuva.y = texture(Utex, vTexCoord).r;
+  yuva.z = texture(Vtex, vTexCoord).r;
+  yuva.a = 1.0;
+  yuva = swizzle(yuva, ubuf.swizzle);
+  vec4 rgba = yuva_to_rgba (yuva, ubuf.color_matrix);
+  fragColor = rgba * ubuf.qt_Opacity;
+}
+
diff --git a/ext/qt6/gstplugin.cc b/ext/qt6/gstplugin.cc
index 3ab792578f052367f401582f2ea09aa0257fe6c1..d9e6546148eae364775dbe18101c17336e35ea3b 100644
--- a/ext/qt6/gstplugin.cc
+++ b/ext/qt6/gstplugin.cc
@@ -30,6 +30,9 @@ plugin_init (GstPlugin * plugin)
   gboolean ret = FALSE;
 
   ret |= GST_ELEMENT_REGISTER (qml6glsink, plugin);
+  ret |= GST_ELEMENT_REGISTER (qml6glsrc, plugin);
+  ret |= GST_ELEMENT_REGISTER (qml6glmixer, plugin);
+  ret |= GST_ELEMENT_REGISTER (qml6gloverlay, plugin);
 
   return ret;
 }
diff --git a/ext/qt6/gstqml6glmixer.cc b/ext/qt6/gstqml6glmixer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b616cd4ad06ccdbfc2439e31dc32668440a9bc3e
--- /dev/null
+++ b/ext/qt6/gstqml6glmixer.cc
@@ -0,0 +1,628 @@
+/*
+ * GStreamer
+ * Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gstqml6gmixer
+ *
+ * `qml6glmixer` provides a way to render an almost-arbitrary QML scene within
+ * GStreamer pipeline using the same OpenGL context that GStreamer uses
+ * internally.  This avoids attempting to share multiple OpenGL contexts
+ * avoiding increased synchronisation points and attempting to share an OpenGL
+ * context at runtime which some drivers do not like.  The Intel driver on
+ * Windows is a notable example of the last point.
+ *
+ * `qml6glmixer` will attempt to retrieve the windowing system display connection
+ * that Qt is using (#GstGLDisplay).  This may be different to any already
+ * existing window system display connection already in use in the pipeline for
+ * a number of reasons.  A couple of examples of this are:
+ *
+ * 1. Adding `qml6glmixer` to an already running pipeline
+ * 2. Not having any `qml6glmixer` (or `qml6glsink`, or `qml6gloverlay`) element
+ *    start up before any other OpenGL-based element in the pipeline.
+ *
+ * If one of these scenarios occurs, then there will be multiple OpenGL contexts
+ * in use in the pipeline.  This means that either the pipeline will fail to
+ * start up correctly, a downstream element may reject buffers, or a complete
+ * GPU->System memory->GPU transfer is performed for every buffer.
+ *
+ * The requirement to avoid this is that all elements share the same
+ * #GstGLDisplay object and as Qt cannot currently share an existing window
+ * system display connection, GStreamer must use the window system display
+ * connection provided by Qt.  This window system display connection can be
+ * retrieved by either a `qml6glsink` element, a `qml6gloverlay` element or a
+ * `qmlglmixer element. The recommended usage is to have either elements
+ * (`qml6glsink` or `qml6gloverlay` or `qml6glmixer) be the first to propagate
+ * the #GstGLDisplay for the entire pipeline to use by setting either element
+ * to the READY element state before any other OpenGL element in the pipeline.
+ *
+ * In the dynamically adding `qml6glmixer` (or `qml6glsink`, or `qml6gloverlay`)
+ * to a pipeline case, there are some considerations for ensuring that the
+ * window system display and OpenGL contexts are compatible with Qt.  When the
+ * `qml6glmixer` (or `qml6glsink`, or `qml6gloverlay`) element is added and
+ * brought up to READY, it will propagate it's own #GstGLDisplay using the
+ * #GstContext mechanism regardless of any existing #GstGLDisplay used by the
+ * pipeline previously.  In order for the new #GstGLDisplay to be used, the
+ * application must then set the provided #GstGLDisplay containing #GstContext
+ * on the pipeline.  This may effectively cause each OpenGL element to replace
+ * the window system display and also the OpenGL context it is using.  As such
+ * this process may take a significant amount of time and resources as objects
+ * are recreated in the new OpenGL context.
+ *
+ * All instances of `qml6glmixer`, `qml6glsink`, and `qml6gloverlay` will return
+ * the exact same #GstGLDisplay object while the pipeline is running regardless
+ * of whether any `qml6glmixer`, `qml6glsink`, or `qml6gloverlay` elements are
+ * added or removed from the pipeline.
+ *
+ * The Qml scene will run at configured output framerate.  The timestamps on the
+ * output buffers are used to drive the animation time.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstqt6elements.h"
+#include "gstqml6glmixer.h"
+#include "qt6glrenderer.h"
+#include "gstqt6glutility.h"
+
+#include <QtGui/QGuiApplication>
+
+#include <gst/gl/gl.h>
+#include <gst/gl/gstglfuncs.h>
+
+#define GST_CAT_DEFAULT gst_debug_qml6_gl_mixer
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+enum
+{
+  PROP_PAD_0,
+  PROP_PAD_WIDGET,
+};
+
+struct _GstQml6GLMixerPad
+{
+  GstGLMixerPad parent;
+
+  QSharedPointer<Qt6GLVideoItemInterface> widget;
+};
+
+G_DEFINE_FINAL_TYPE (GstQml6GLMixerPad, gst_qml6_gl_mixer_pad, GST_TYPE_GL_MIXER_PAD);
+
+static gboolean
+gst_qml6_gl_mixer_pad_prepare_frame (GstVideoAggregatorPad *vagg_pad, GstVideoAggregator * vagg,
+    GstBuffer *buffer, GstVideoFrame * prepared_frame)
+{
+  GstQml6GLMixerPad *pad = GST_QML6_GL_MIXER_PAD (vagg_pad);
+
+  if (!GST_VIDEO_AGGREGATOR_PAD_CLASS (gst_qml6_gl_mixer_pad_parent_class)->prepare_frame (vagg_pad, vagg, buffer, prepared_frame))
+    return FALSE;
+
+  if (pad->widget) {
+    GstMemory *mem;
+    GstGLMemory *gl_mem;
+    GstCaps *in_caps;
+    GstGLContext *context;
+
+    in_caps = gst_video_info_to_caps (&vagg_pad->info);
+    gst_caps_set_features_simple (in_caps, gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));
+    pad->widget->setCaps (in_caps);
+    gst_clear_caps (&in_caps);
+
+    mem = gst_buffer_peek_memory (buffer, 0);
+    if (!gst_is_gl_memory (mem)) {
+      GST_ELEMENT_ERROR (vagg_pad, RESOURCE, NOT_FOUND,
+          (NULL), ("Input memory must be a GstGLMemory"));
+      return GST_FLOW_ERROR;
+    }
+    gl_mem = (GstGLMemory *) mem;
+    context = gst_gl_base_mixer_get_gl_context (GST_GL_BASE_MIXER (vagg));
+    if (!gst_gl_context_can_share (gl_mem->mem.context, context)) {
+      GST_WARNING_OBJECT (vagg_pad, "Cannot use the current input texture "
+          "(input buffer GL context %" GST_PTR_FORMAT " cannot share "
+          "resources with the configured OpenGL context %" GST_PTR_FORMAT ")",
+          gl_mem->mem.context, context);
+    } else {
+      pad->widget->setBuffer (buffer);
+    }
+  }
+
+  return TRUE;
+}
+
+static void
+gst_qml6_gl_mixer_pad_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLMixerPad *qml6_gl_mixer_pad = GST_QML6_GL_MIXER_PAD (object);
+
+  switch (prop_id) {
+    case PROP_PAD_WIDGET: {
+      Qt6GLVideoItem *qt_item = static_cast<Qt6GLVideoItem *> (g_value_get_pointer (value));
+      if (qt_item)
+        qml6_gl_mixer_pad->widget = qt_item->getInterface();
+      else
+        qml6_gl_mixer_pad->widget.clear();
+      break;
+    }
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qml6_gl_mixer_pad_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLMixerPad *qml6_gl_mixer_pad = GST_QML6_GL_MIXER_PAD (object);
+
+  switch (prop_id) {
+    case PROP_PAD_WIDGET:
+      /* This is not really safe - the app needs to be
+       * sure the widget is going to be kept alive or
+       * this can crash */
+      if (qml6_gl_mixer_pad->widget)
+        g_value_set_pointer (value, qml6_gl_mixer_pad->widget->videoItem());
+      else
+        g_value_set_pointer (value, NULL);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qml6_gl_mixer_pad_finalize (GObject * object)
+{
+  GstQml6GLMixerPad *pad = GST_QML6_GL_MIXER_PAD (object);
+
+  pad->widget.clear();
+
+  G_OBJECT_CLASS (gst_qml6_gl_mixer_pad_parent_class)->finalize (object);
+}
+
+static void
+gst_qml6_gl_mixer_pad_class_init (GstQml6GLMixerPadClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstVideoAggregatorPadClass *vagg_pad_class = (GstVideoAggregatorPadClass *) klass;
+
+  gobject_class->set_property = gst_qml6_gl_mixer_pad_set_property;
+  gobject_class->get_property = gst_qml6_gl_mixer_pad_get_property;
+  gobject_class->finalize = gst_qml6_gl_mixer_pad_finalize;
+
+  g_object_class_install_property (gobject_class, PROP_PAD_WIDGET,
+      g_param_spec_pointer ("widget", "QQuickItem",
+          "The QQuickItem to place the input video in the object hierarchy",
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+  vagg_pad_class->prepare_frame = gst_qml6_gl_mixer_pad_prepare_frame;
+}
+
+static void
+gst_qml6_gl_mixer_pad_init (GstQml6GLMixerPad * pad)
+{
+  pad->widget = QSharedPointer<Qt6GLVideoItemInterface>();
+}
+
+static void gst_qml6_gl_mixer_finalize (GObject * object);
+static void gst_qml6_gl_mixer_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * param_spec);
+static void gst_qml6_gl_mixer_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * param_spec);
+
+static gboolean gst_qml6_gl_mixer_process_buffers (GstGLMixer * btrans,
+    GstBuffer * outbuf);
+
+static gboolean gst_qml6_gl_mixer_gl_start (GstGLBaseMixer * bmixer);
+static void gst_qml6_gl_mixer_gl_stop (GstGLBaseMixer * bmixer);
+
+static GstFlowReturn gst_qml6_gl_mixer_create_output_buffer (GstVideoAggregator * vagg, GstBuffer ** outbuf);
+
+static gboolean gst_qml6_gl_mixer_negotiated_src_caps (GstAggregator * aggregator, GstCaps * out_caps);
+
+static GstStateChangeReturn gst_qml6_gl_mixer_change_state (GstElement * element,
+    GstStateChange transition);
+
+enum
+{
+  PROP_0,
+  PROP_QML_SCENE,
+  PROP_ROOT_ITEM,
+};
+
+enum
+{
+  SIGNAL_0,
+  SIGNAL_QML_SCENE_INITIALIZED,
+  SIGNAL_QML_SCENE_DESTROYED,
+  LAST_SIGNAL
+};
+
+static guint gst_qml6_gl_mixer_signals[LAST_SIGNAL] = { 0 };
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
+        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
+            "RGBA"))
+    );
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
+        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
+            "{ RGBA, BGRA, YV12 }"))
+    );
+
+struct _GstQml6GLMixer {
+  GstGLMixer parent;
+
+  gchar *qml_scene;
+
+  GstQt6QuickRenderer *renderer;
+  GstBuffer *outbuf;
+};
+
+#define gst_qml6_gl_mixer_parent_class parent_class
+G_DEFINE_FINAL_TYPE_WITH_CODE (GstQml6GLMixer, gst_qml6_gl_mixer,
+    GST_TYPE_GL_MIXER, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
+        "qml6glmixer", 0, "Qt6 Video Mixer"));
+GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qml6glmixer, "qml6glmixer",
+    GST_RANK_NONE, GST_TYPE_QML6_GL_MIXER, qt6_element_init (plugin));
+
+static void
+gst_qml6_gl_mixer_class_init (GstQml6GLMixerClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstAggregatorClass *agg_class;
+  GstVideoAggregatorClass *vagg_class;
+  GstGLBaseMixerClass *glbasemixer_class;
+  GstGLMixerClass *glmixer_class;
+  GstElementClass *element_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  glbasemixer_class = (GstGLBaseMixerClass *) klass;
+  glmixer_class = (GstGLMixerClass *) klass;
+  vagg_class = (GstVideoAggregatorClass *) klass;
+  agg_class = (GstAggregatorClass *) klass;
+  element_class = (GstElementClass *) klass;
+
+  gobject_class->set_property = gst_qml6_gl_mixer_set_property;
+  gobject_class->get_property = gst_qml6_gl_mixer_get_property;
+  gobject_class->finalize = gst_qml6_gl_mixer_finalize;
+
+  gst_element_class_set_metadata (gstelement_class, "Qt6 Video Mixer",
+      "Video/QML/Mixer", "A mixer that renders a QML scene",
+      "Matthew Waters <matthew@centricular.com>");
+
+  g_object_class_install_property (gobject_class, PROP_QML_SCENE,
+      g_param_spec_string ("qml-scene", "QML Scene",
+          "The contents of the QML scene", NULL,
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+  g_object_class_install_property (gobject_class, PROP_ROOT_ITEM,
+      g_param_spec_pointer ("root-item", "QQuickItem",
+          "The root QQuickItem from the qml-scene used to render",
+          (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+  /**
+   * GstQmlGLMixer::qml-scene-initialized
+   * @element: the #GstQmlGLMixer
+   * @user_data: user provided data
+   */
+  gst_qml6_gl_mixer_signals[SIGNAL_QML_SCENE_INITIALIZED] =
+      g_signal_new ("qml-scene-initialized", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  /**
+   * GstQmlGLMixer::qml-scene-destroyed
+   * @element: the #GstQmlGLMixer
+   * @user_data: user provided data
+   */
+  gst_qml6_gl_mixer_signals[SIGNAL_QML_SCENE_DESTROYED] =
+      g_signal_new ("qml-scene-destroyed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  glbasemixer_class->gl_start = gst_qml6_gl_mixer_gl_start;
+  glbasemixer_class->gl_stop = gst_qml6_gl_mixer_gl_stop;
+
+  glmixer_class->process_buffers = gst_qml6_gl_mixer_process_buffers;
+
+  vagg_class->create_output_buffer = gst_qml6_gl_mixer_create_output_buffer;
+
+  agg_class->negotiated_src_caps = gst_qml6_gl_mixer_negotiated_src_caps;
+
+  element_class->change_state = gst_qml6_gl_mixer_change_state;
+
+  gst_element_class_add_static_pad_template_with_gtype (element_class,
+      &src_factory, GST_TYPE_AGGREGATOR_PAD);
+  gst_element_class_add_static_pad_template_with_gtype (element_class,
+      &sink_factory, GST_TYPE_QML6_GL_MIXER_PAD);
+}
+
+static void
+gst_qml6_gl_mixer_init (GstQml6GLMixer * qml6_gl_mixer)
+{
+  qml6_gl_mixer->qml_scene = NULL;
+}
+
+static void
+gst_qml6_gl_mixer_finalize (GObject * object)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (object);
+
+  g_free (qml6_gl_mixer->qml_scene);
+  qml6_gl_mixer->qml_scene = NULL;
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_qml6_gl_mixer_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (object);
+
+  switch (prop_id) {
+    case PROP_QML_SCENE:
+      g_free (qml6_gl_mixer->qml_scene);
+      qml6_gl_mixer->qml_scene = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qml6_gl_mixer_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (object);
+
+  switch (prop_id) {
+    case PROP_QML_SCENE:
+      g_value_set_string (value, qml6_gl_mixer->qml_scene);
+      break;
+    case PROP_ROOT_ITEM:
+      GST_OBJECT_LOCK (qml6_gl_mixer);
+      if (qml6_gl_mixer->renderer) {
+        QQuickItem *root = qml6_gl_mixer->renderer->rootItem();
+        if (root)
+          g_value_set_pointer (value, root);
+        else
+          g_value_set_pointer (value, NULL);
+      } else {
+        g_value_set_pointer (value, NULL);
+      }
+      GST_OBJECT_UNLOCK (qml6_gl_mixer);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_qml6_gl_mixer_negotiated_src_caps (GstAggregator * aggregator, GstCaps * out_caps)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (aggregator);
+  GstVideoInfo out_info;
+
+  if (!gst_video_info_from_caps (&out_info, out_caps))
+    return FALSE;
+
+  qml6_gl_mixer->renderer->setSize (GST_VIDEO_INFO_WIDTH (&out_info),
+      GST_VIDEO_INFO_HEIGHT (&out_info));
+  
+  return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (aggregator, out_caps);
+}
+
+static gboolean
+gst_qml6_gl_mixer_gl_start (GstGLBaseMixer * bmixer)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (bmixer);
+
+  QQuickItem *root;
+  GError *error = NULL;
+
+  GST_TRACE_OBJECT (bmixer, "using scene:\n%s", qml6_gl_mixer->qml_scene);
+
+  if (!qml6_gl_mixer->qml_scene || g_strcmp0 (qml6_gl_mixer->qml_scene, "") == 0) {
+    GST_ELEMENT_ERROR (bmixer, RESOURCE, NOT_FOUND, ("qml-scene property not set"), (NULL));
+    return FALSE;
+  }
+
+  if (!GST_GL_BASE_MIXER_CLASS (parent_class)->gl_start (bmixer))
+    return FALSE;
+
+  GST_OBJECT_LOCK (bmixer);
+  qml6_gl_mixer->renderer = new GstQt6QuickRenderer;
+  if (!qml6_gl_mixer->renderer->init (bmixer->context, &error)) {
+    GST_ELEMENT_ERROR (GST_ELEMENT (bmixer), RESOURCE, NOT_FOUND,
+        ("%s", error->message), (NULL));
+    delete qml6_gl_mixer->renderer;
+    qml6_gl_mixer->renderer = NULL;
+    GST_OBJECT_UNLOCK (bmixer);
+    return FALSE;
+  }
+
+  /* FIXME: Qml may do async loading and we need to propagate qml errors in that case as well */
+  if (!qml6_gl_mixer->renderer->setQmlScene (qml6_gl_mixer->qml_scene, &error)) {
+    GST_ELEMENT_ERROR (GST_ELEMENT (bmixer), RESOURCE, NOT_FOUND,
+        ("%s", error->message), (NULL));
+    goto fail_renderer;
+  }
+
+  root = qml6_gl_mixer->renderer->rootItem();
+  if (!root) {
+    GST_ELEMENT_ERROR (GST_ELEMENT (bmixer), RESOURCE, NOT_FOUND,
+        ("Qml scene does not have a root item"), (NULL));
+    goto fail_renderer;
+  }
+  GST_OBJECT_UNLOCK (bmixer);
+
+  g_object_notify (G_OBJECT (qml6_gl_mixer), "root-item");
+  g_signal_emit (qml6_gl_mixer, gst_qml6_gl_mixer_signals[SIGNAL_QML_SCENE_INITIALIZED], 0);
+
+  return TRUE;
+
+fail_renderer:
+  {
+    qml6_gl_mixer->renderer->cleanup();
+    delete qml6_gl_mixer->renderer;
+    qml6_gl_mixer->renderer = NULL;
+    GST_OBJECT_UNLOCK (bmixer);
+    return FALSE;
+  }
+}
+
+static void
+gst_qml6_gl_mixer_gl_stop (GstGLBaseMixer * bmixer)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (bmixer);
+  GstQt6QuickRenderer *renderer = NULL;
+
+  /* notify before actually destroying anything */
+  GST_OBJECT_LOCK (qml6_gl_mixer);
+  if (qml6_gl_mixer->renderer)
+    renderer = qml6_gl_mixer->renderer;
+  qml6_gl_mixer->renderer = NULL;
+  GST_OBJECT_UNLOCK (qml6_gl_mixer);
+
+  g_signal_emit (qml6_gl_mixer, gst_qml6_gl_mixer_signals[SIGNAL_QML_SCENE_DESTROYED], 0);
+  g_object_notify (G_OBJECT (qml6_gl_mixer), "root-item");
+
+  /* TODO: clear all pad buffers in the items?
+  if (qml6_gl_mixer->widget)
+    qml6_gl_mixer->widget->setBuffer (NULL);
+*/
+  if (renderer) {
+    renderer->cleanup();
+    delete renderer;
+  }
+
+  GST_GL_BASE_MIXER_CLASS (parent_class)->gl_stop (bmixer);
+}
+
+static GstFlowReturn
+gst_qml6_gl_mixer_create_output_buffer (GstVideoAggregator * vagg, GstBuffer ** outbuf)
+{
+  *outbuf = gst_buffer_new();
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+qml6_gl_mixer_gl_callback (GstGLContext *context, GstQml6GLMixer * qml6_gl_mixer)
+{
+  GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (qml6_gl_mixer);
+  GstGLMemory *out_mem;
+
+  /* XXX: is this the correct ts to drive the animation */
+  out_mem = qml6_gl_mixer->renderer->generateOutput (GST_BUFFER_PTS (qml6_gl_mixer->outbuf));
+  if (!out_mem) {
+    GST_ERROR_OBJECT (qml6_gl_mixer, "Failed to generate output");
+    return FALSE;
+  }
+
+  gst_buffer_append_memory (qml6_gl_mixer->outbuf, (GstMemory *) out_mem);
+  gst_buffer_add_video_meta (qml6_gl_mixer->outbuf, (GstVideoFrameFlags) 0,
+      GST_VIDEO_INFO_FORMAT (&vagg->info),
+      GST_VIDEO_INFO_WIDTH (&vagg->info),
+      GST_VIDEO_INFO_HEIGHT (&vagg->info));
+
+  return TRUE;
+}
+
+static gboolean
+gst_qml6_gl_mixer_process_buffers (GstGLMixer * mix,
+    GstBuffer * outbuf)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (mix);
+  GstGLBaseMixer *bmix = GST_GL_BASE_MIXER (mix);
+  GstGLContext *context = gst_gl_base_mixer_get_gl_context (bmix);
+
+  qml6_gl_mixer->outbuf = outbuf;
+  gst_gl_context_thread_add (context,
+    (GstGLContextThreadFunc) qml6_gl_mixer_gl_callback, qml6_gl_mixer);
+  qml6_gl_mixer->outbuf = NULL;
+
+  gst_clear_object (&context);
+
+  return TRUE;
+}
+
+static GstStateChangeReturn
+gst_qml6_gl_mixer_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstQml6GLMixer *qml6_gl_mixer = GST_QML6_GL_MIXER (element);
+  GstGLBaseMixer *bmixer = GST_GL_BASE_MIXER (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  GST_DEBUG_OBJECT (element, "changing state: %s => %s",
+      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY: {
+      QGuiApplication *app;
+      GstGLDisplay *display = NULL;
+
+      app = static_cast<QGuiApplication *> (QCoreApplication::instance ());
+      if (!app) {
+        GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
+            ("%s", "Failed to connect to Qt"),
+            ("%s", "Could not retrieve QGuiApplication instance"));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      display = gst_qml6_get_gl_display (FALSE);
+
+      if (display != bmixer->display)
+        /* always propagate. The application may need to choose between window
+         * system display connections */
+        gst_gl_element_propagate_display_context (GST_ELEMENT (qml6_gl_mixer), display);
+      gst_object_unref (display);
+      break;
+    }
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    return ret;
+
+  switch (transition) {
+    default:
+      break;
+  }
+
+  return ret;
+
+}
diff --git a/ext/qt6/gstqml6glmixer.h b/ext/qt6/gstqml6glmixer.h
new file mode 100644
index 0000000000000000000000000000000000000000..e58cb5e16b06da5f5fe94bc38efd5413e5c04fd2
--- /dev/null
+++ b/ext/qt6/gstqml6glmixer.h
@@ -0,0 +1,40 @@
+/*
+ * GStreamer
+ * Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_QML6_GL_MIXER_H__
+#define __GST_QML6_GL_MIXER_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/gl/gl.h>
+#include "qt6glrenderer.h"
+#include "qt6glitem.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_QML6_GL_MIXER_PAD (gst_qml6_gl_mixer_pad_get_type())
+G_DECLARE_FINAL_TYPE(GstQml6GLMixerPad, gst_qml6_gl_mixer_pad, GST, QML6_GL_MIXER_PAD, GstGLMixerPad);
+
+#define GST_TYPE_QML6_GL_MIXER (gst_qml6_gl_mixer_get_type())
+G_DECLARE_FINAL_TYPE(GstQml6GLMixer, gst_qml6_gl_mixer, GST, QML6_GL_MIXER, GstGLMixer);
+
+G_END_DECLS
+
+#endif /* __GST_QML6_GL_MIXER_H__ */
diff --git a/ext/qt6/gstqml6gloverlay.cc b/ext/qt6/gstqml6gloverlay.cc
new file mode 100644
index 0000000000000000000000000000000000000000..85f5a380f6f7cb2bb5768efc3b5c3cdf34e01b96
--- /dev/null
+++ b/ext/qt6/gstqml6gloverlay.cc
@@ -0,0 +1,571 @@
+/*
+ * GStreamer
+ * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gstqml6gloverlay
+ *
+ * `qml6gloverlay` provides a way to render an almost-arbitrary QML scene within
+ * GStreamer pipeline using the same OpenGL context that GStreamer uses
+ * internally.  This avoids attempting to share multiple OpenGL contexts
+ * avoiding increased synchronisation points and attempting to share an OpenGL
+ * context at runtime which some drivers do not like.  The Intel driver on
+ * Windows is a notable example of the last point.
+ *
+ * `qml6gloverlay` will attempt to retrieve the windowing system display connection
+ * that Qt is using (#GstGLDisplay).  This may be different to any already
+ * existing window system display connection already in use in the pipeline for
+ * a number of reasons.  A couple of examples of this are:
+ *
+ * 1. Adding `qml6gloverlay` to an already running pipeline
+ * 2. Not having any `qml6gloverlay` (or `qml6glsink`, or `qml6glmixer`) element
+ *    start up before any other OpenGL-based element in the pipeline.
+ *
+ * If one of these scenarios occurs, then there will be multiple OpenGL contexts
+ * in use in the pipeline.  This means that either the pipeline will fail to
+ * start up correctly, a downstream element may reject buffers, or a complete
+ * GPU->System memory->GPU transfer is performed for every buffer.
+ *
+ * The requirement to avoid this is that all elements share the same
+ * #GstGLDisplay object and as Qt cannot currently share an existing window
+ * system display connection, GStreamer must use the window system display
+ * connection provided by Qt.  This window system display connection can be
+ * retrieved by either a `qml6glsink` element, a `qml6gloverlay`, or a
+ * `qml6glmixer` element. The recommended usage is to have either element
+ * (`qml6glsink` or `qml6gloverlay` or `qml6glmixer`) be the first to propagate
+ * the #GstGLDisplay for the entire pipeline to use by setting either element
+ * to the READY element state before any other OpenGL element in the pipeline.
+ *
+ * In the dynamically adding `qml6gloverlay` (or `qml6glsink`, or `qml6glmixer`)
+ * to a pipeline case, there are some considerations for ensuring that the
+ * window system display and OpenGL contexts are compatible with Qt.  When the
+ * `qml6gloverlay` (or `qml6glsink`, or `qml6glmixer`) element is added and
+ * brought up to READY, it will propagate it's own #GstGLDisplay using the
+ * #GstContext mechanism regardless of any existing #GstGLDisplay used by the
+ * pipeline previously.  In order for the new #GstGLDisplay to be used, the
+ * application must then set the provided #GstGLDisplay containing #GstContext
+ * on the pipeline.  This may effectively cause each OpenGL element to replace
+ * the window system display and also the OpenGL context it is using.  As such
+ * this process may take a significant amount of time and resources as objects
+ * are recreated in the new OpenGL context.
+ *
+ * All instances of `qml6gloverlay`, `qml6glsink`, and `qml6glmixer` will
+ * return the exact same #GstGLDisplay object while the pipeline is running
+ * regardless of whether any `qml6gloverlay` or `qml6glsink` elements are
+ * added or removed from the pipeline.
+ *
+ * The Qml scene will run at the pace of incoming buffers.  One input buffer
+ * will cause a render of one output buffer.  The timestamps on the input
+ * buffers are used to drive the animation time.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstqt6elements.h"
+#include "gstqml6gloverlay.h"
+#include "qt6glrenderer.h"
+#include "gstqt6glutility.h"
+
+#include <QtGui/QGuiApplication>
+
+#include <gst/gl/gstglfuncs.h>
+
+#define GST_CAT_DEFAULT gst_debug_qml6_gl_overlay
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+/* *INDENT-OFF* */
+static GstStaticPadTemplate qml6_overlay_src_pad_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
+      "format = (string) RGBA, "
+      "width = " GST_VIDEO_SIZE_RANGE ", "
+      "height = " GST_VIDEO_SIZE_RANGE ", "
+      "framerate = " GST_VIDEO_FPS_RANGE ","
+      "texture-target = (string) 2D"
+    ));
+
+static GstStaticPadTemplate qml6_overlay_sink_pad_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw(ANY), "
+      "format = (string) { RGBA, BGRA, YV12 }, "
+      "width = " GST_VIDEO_SIZE_RANGE ", "
+      "height = " GST_VIDEO_SIZE_RANGE ", "
+      "framerate = " GST_VIDEO_FPS_RANGE ","
+      "texture-target = (string) 2D"
+    ));
+/* *INDENT-ON* */
+
+static void gst_qml6_gl_overlay_finalize (GObject * object);
+static void gst_qml6_gl_overlay_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * param_spec);
+static void gst_qml6_gl_overlay_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * param_spec);
+
+static GstCaps * gst_qml6_overlay_transform_internal_caps (GstGLFilter * filter,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
+
+static gboolean gst_qml6_gl_overlay_gl_start (GstGLBaseFilter * bfilter);
+static void gst_qml6_gl_overlay_gl_stop (GstGLBaseFilter * bfilter);
+static gboolean gst_qml6_gl_overlay_gl_set_caps (GstGLBaseFilter * bfilter,
+    GstCaps * in_caps, GstCaps * out_caps);
+
+static GstFlowReturn gst_qml6_gl_overlay_prepare_output_buffer (GstBaseTransform * btrans,
+    GstBuffer * buffer, GstBuffer ** outbuf);
+static GstFlowReturn gst_qml6_gl_overlay_transform (GstBaseTransform * btrans,
+    GstBuffer * inbuf, GstBuffer * outbuf);
+
+static GstStateChangeReturn gst_qml6_gl_overlay_change_state (GstElement * element,
+    GstStateChange transition);
+
+enum
+{
+  PROP_0,
+  PROP_WIDGET,
+  PROP_QML_SCENE,
+  PROP_ROOT_ITEM,
+};
+
+enum
+{
+  SIGNAL_0,
+  SIGNAL_QML_SCENE_INITIALIZED,
+  SIGNAL_QML_SCENE_DESTROYED,
+  LAST_SIGNAL
+};
+
+static guint gst_qml6_gl_overlay_signals[LAST_SIGNAL] = { 0 };
+
+#define gst_qml6_gl_overlay_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstQml6GLOverlay, gst_qml6_gl_overlay,
+    GST_TYPE_GL_FILTER, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
+        "qml6gloverlay", 0, "Qt6 Video Overlay"));
+GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qml6gloverlay, "qml6gloverlay",
+    GST_RANK_NONE, GST_TYPE_QML6_GL_OVERLAY, qt6_element_init (plugin));
+
+static void
+gst_qml6_gl_overlay_class_init (GstQml6GLOverlayClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseTransformClass *btrans_class;
+  GstGLBaseFilterClass *glbasefilter_class;
+  GstGLFilterClass *glfilter_class;
+  GstElementClass *element_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  glbasefilter_class = (GstGLBaseFilterClass *) klass;
+  glfilter_class = (GstGLFilterClass *) klass;
+  btrans_class = (GstBaseTransformClass *) klass;
+  element_class = (GstElementClass *) klass;
+
+  gobject_class->set_property = gst_qml6_gl_overlay_set_property;
+  gobject_class->get_property = gst_qml6_gl_overlay_get_property;
+  gobject_class->finalize = gst_qml6_gl_overlay_finalize;
+
+  gst_element_class_set_metadata (gstelement_class, "Qt Video Overlay",
+      "Filter/QML/Overlay", "A filter that renders a QML scene onto a video stream",
+      "Matthew Waters <matthew@centricular.com>");
+
+  g_object_class_install_property (gobject_class, PROP_QML_SCENE,
+      g_param_spec_string ("qml-scene", "QML Scene",
+          "The contents of the QML scene", NULL,
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+  g_object_class_install_property (gobject_class, PROP_WIDGET,
+      g_param_spec_pointer ("widget", "QQuickItem",
+          "The QQuickItem to place the input video in the object hierarchy",
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+  g_object_class_install_property (gobject_class, PROP_ROOT_ITEM,
+      g_param_spec_pointer ("root-item", "QQuickItem",
+          "The root QQuickItem from the qml-scene used to render",
+          (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+  /**
+   * GstQmlGLOverlay::qml-scene-initialized
+   * @element: the #GstQmlGLOverlay
+   * @user_data: user provided data
+   */
+  gst_qml6_gl_overlay_signals[SIGNAL_QML_SCENE_INITIALIZED] =
+      g_signal_new ("qml-scene-initialized", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  /**
+   * GstQmlGLOverlay::qml-scene-destroyed
+   * @element: the #GstQmlGLOverlay
+   * @user_data: user provided data
+   */
+  gst_qml6_gl_overlay_signals[SIGNAL_QML_SCENE_DESTROYED] =
+      g_signal_new ("qml-scene-destroyed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  gst_element_class_add_static_pad_template (element_class,
+      &qml6_overlay_src_pad_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &qml6_overlay_sink_pad_template);
+
+  btrans_class->prepare_output_buffer = gst_qml6_gl_overlay_prepare_output_buffer;
+  btrans_class->transform = gst_qml6_gl_overlay_transform;
+
+  glfilter_class->transform_internal_caps = gst_qml6_overlay_transform_internal_caps;
+
+  glbasefilter_class->gl_start = gst_qml6_gl_overlay_gl_start;
+  glbasefilter_class->gl_stop = gst_qml6_gl_overlay_gl_stop;
+  glbasefilter_class->gl_set_caps = gst_qml6_gl_overlay_gl_set_caps;
+
+  element_class->change_state = gst_qml6_gl_overlay_change_state;
+}
+
+static void
+gst_qml6_gl_overlay_init (GstQml6GLOverlay * qml6_gl_overlay)
+{
+  qml6_gl_overlay->widget = QSharedPointer<Qt6GLVideoItemInterface>();
+  qml6_gl_overlay->qml_scene = NULL;
+}
+
+static void
+gst_qml6_gl_overlay_finalize (GObject * object)
+{
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (object);
+
+  g_free (qml6_gl_overlay->qml_scene);
+  qml6_gl_overlay->qml_scene = NULL;
+
+  qml6_gl_overlay->widget.clear();
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_qml6_gl_overlay_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (object);
+
+  switch (prop_id) {
+    case PROP_WIDGET: {
+      Qt6GLVideoItem *qt_item = static_cast<Qt6GLVideoItem *> (g_value_get_pointer (value));
+      if (qt_item)
+        qml6_gl_overlay->widget = qt_item->getInterface();
+      else
+        qml6_gl_overlay->widget.clear();
+      break;
+    }
+    case PROP_QML_SCENE:
+      g_free (qml6_gl_overlay->qml_scene);
+      qml6_gl_overlay->qml_scene = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qml6_gl_overlay_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (object);
+
+  switch (prop_id) {
+    case PROP_WIDGET:
+      /* This is not really safe - the app needs to be
+       * sure the widget is going to be kept alive or
+       * this can crash */
+      if (qml6_gl_overlay->widget)
+        g_value_set_pointer (value, qml6_gl_overlay->widget->videoItem());
+      else
+        g_value_set_pointer (value, NULL);
+      break;
+    case PROP_QML_SCENE:
+      g_value_set_string (value, qml6_gl_overlay->qml_scene);
+      break;
+    case PROP_ROOT_ITEM:
+      GST_OBJECT_LOCK (qml6_gl_overlay);
+      if (qml6_gl_overlay->renderer) {
+        QQuickItem *root = qml6_gl_overlay->renderer->rootItem();
+        if (root)
+          g_value_set_pointer (value, root);
+        else
+          g_value_set_pointer (value, NULL);
+      } else {
+        g_value_set_pointer (value, NULL);
+      }
+      GST_OBJECT_UNLOCK (qml6_gl_overlay);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_qml6_gl_overlay_gl_start (GstGLBaseFilter * bfilter)
+{
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (bfilter);
+  QQuickItem *root;
+  GError *error = NULL;
+
+  GST_TRACE_OBJECT (bfilter, "using scene:\n%s", qml6_gl_overlay->qml_scene);
+
+  if (!qml6_gl_overlay->qml_scene || g_strcmp0 (qml6_gl_overlay->qml_scene, "") == 0) {
+    GST_ELEMENT_ERROR (bfilter, RESOURCE, NOT_FOUND, ("qml-scene property not set"), (NULL));
+    return FALSE;
+  }
+
+  if (!GST_GL_BASE_FILTER_CLASS (parent_class)->gl_start (bfilter))
+    return FALSE;
+
+  GST_OBJECT_LOCK (bfilter);
+  qml6_gl_overlay->renderer = new GstQt6QuickRenderer;
+  if (!qml6_gl_overlay->renderer->init (bfilter->context, &error)) {
+    GST_ELEMENT_ERROR (GST_ELEMENT (bfilter), RESOURCE, NOT_FOUND,
+        ("%s", error->message), (NULL));
+    delete qml6_gl_overlay->renderer;
+    qml6_gl_overlay->renderer = NULL;
+    GST_OBJECT_UNLOCK (bfilter);
+    return FALSE;
+  }
+
+  /* FIXME: Qml may do async loading and we need to propagate qml errors in that case as well */
+  if (!qml6_gl_overlay->renderer->setQmlScene (qml6_gl_overlay->qml_scene, &error)) {
+    GST_ELEMENT_ERROR (GST_ELEMENT (bfilter), RESOURCE, NOT_FOUND,
+        ("%s", error->message), (NULL));
+    goto fail_renderer;
+    return FALSE;
+  }
+
+  root = qml6_gl_overlay->renderer->rootItem();
+  if (!root) {
+    GST_ELEMENT_ERROR (GST_ELEMENT (bfilter), RESOURCE, NOT_FOUND,
+        ("Qml scene does not have a root item"), (NULL));
+    goto fail_renderer;
+  }
+  GST_OBJECT_UNLOCK (bfilter);
+
+  g_object_notify (G_OBJECT (qml6_gl_overlay), "root-item");
+  g_signal_emit (qml6_gl_overlay, gst_qml6_gl_overlay_signals[SIGNAL_QML_SCENE_INITIALIZED], 0);
+
+  GST_OBJECT_LOCK (bfilter);
+  if (!qml6_gl_overlay->widget) {
+    Qt6GLVideoItem *qt_item = static_cast<Qt6GLVideoItem *>(root->findChild<Qt6GLVideoItem *> ());
+    if (qt_item)
+      qml6_gl_overlay->widget = qt_item->getInterface();
+  }
+  GST_OBJECT_UNLOCK (bfilter);
+
+  return TRUE;
+
+fail_renderer:
+  {
+    qml6_gl_overlay->renderer->cleanup();
+    delete qml6_gl_overlay->renderer;
+    qml6_gl_overlay->renderer = NULL;
+    GST_OBJECT_UNLOCK (bfilter);
+    return FALSE;
+  }
+}
+
+static void
+gst_qml6_gl_overlay_gl_stop (GstGLBaseFilter * bfilter)
+{
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (bfilter);
+  GstQt6QuickRenderer *renderer = NULL;
+
+  /* notify before actually destroying anything */
+  GST_OBJECT_LOCK (qml6_gl_overlay);
+  if (qml6_gl_overlay->renderer)
+    renderer = qml6_gl_overlay->renderer;
+  qml6_gl_overlay->renderer = NULL;
+  GST_OBJECT_UNLOCK (qml6_gl_overlay);
+
+  g_signal_emit (qml6_gl_overlay, gst_qml6_gl_overlay_signals[SIGNAL_QML_SCENE_DESTROYED], 0);
+  g_object_notify (G_OBJECT (qml6_gl_overlay), "root-item");
+
+  if (qml6_gl_overlay->widget)
+    qml6_gl_overlay->widget->setBuffer (NULL);
+
+  if (renderer) {
+    renderer->cleanup();
+    delete renderer;
+  }
+
+  GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (bfilter);
+}
+
+static gboolean
+gst_qml6_gl_overlay_gl_set_caps (GstGLBaseFilter * bfilter, GstCaps * in_caps,
+    GstCaps * out_caps)
+{
+  GstGLFilter *filter = GST_GL_FILTER (bfilter);
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (bfilter);
+
+  if (!GST_GL_BASE_FILTER_CLASS (parent_class)->gl_set_caps (bfilter, in_caps, out_caps))
+    return FALSE;
+
+  qml6_gl_overlay->renderer->setSize (GST_VIDEO_INFO_WIDTH (&filter->out_info),
+      GST_VIDEO_INFO_HEIGHT (&filter->out_info));
+
+  return TRUE;
+}
+
+static GstCaps *
+gst_qml6_overlay_transform_internal_caps (GstGLFilter * filter,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
+{
+  GstCaps *tmp = GST_GL_FILTER_CLASS (parent_class)->transform_internal_caps (filter, direction, caps, filter_caps);
+  int i, n;
+
+  n = gst_caps_get_size (tmp);
+  for (i = 0; i < n; i++) {
+    GstStructure *s = gst_caps_get_structure (tmp, i);
+
+    gst_structure_remove_fields (s, "format", "colorimetry", "chroma-site",
+        "texture-target", NULL);
+  }
+
+  return tmp;
+}
+
+static GstFlowReturn
+gst_qml6_gl_overlay_prepare_output_buffer (GstBaseTransform * btrans,
+    GstBuffer * buffer, GstBuffer ** outbuf)
+{
+  GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (btrans);
+  GstGLBaseFilter *bfilter = GST_GL_BASE_FILTER (btrans);
+  GstGLFilter *filter = GST_GL_FILTER (btrans);
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (btrans);
+  GstGLMemory *out_mem;
+  GstGLSyncMeta *sync_meta;
+
+  if (gst_buffer_n_memory (buffer) <= 0) {
+    GST_ELEMENT_ERROR (btrans, RESOURCE, NOT_FOUND,
+        (NULL), ("Buffer must have a memory object"));
+    return GST_FLOW_ERROR;
+  }
+
+  if (qml6_gl_overlay->widget) {
+    GstMemory *mem;
+    GstGLMemory *gl_mem;
+
+    qml6_gl_overlay->widget->setCaps (bfilter->in_caps);
+
+    mem = gst_buffer_peek_memory (buffer, 0);
+    if (!gst_is_gl_memory (mem)) {
+      GST_ELEMENT_ERROR (btrans, RESOURCE, NOT_FOUND,
+          (NULL), ("Input memory must be a GstGLMemory"));
+      return GST_FLOW_ERROR;
+    }
+    gl_mem = (GstGLMemory *) mem;
+    if (!gst_gl_context_can_share (gl_mem->mem.context, bfilter->context)) {
+      GST_WARNING_OBJECT (bfilter, "Cannot use the current input texture "
+          "(input buffer GL context %" GST_PTR_FORMAT " cannot share "
+          "resources with the configured OpenGL context %" GST_PTR_FORMAT ")",
+          gl_mem->mem.context, bfilter->context);
+    } else {
+      qml6_gl_overlay->widget->setBuffer (buffer);
+    }
+  }
+
+  /* XXX: is this the correct ts to drive the animation */
+  out_mem = qml6_gl_overlay->renderer->generateOutput (GST_BUFFER_PTS (buffer));
+  if (!out_mem) {
+    GST_ERROR_OBJECT (qml6_gl_overlay, "Failed to generate output");
+    return GST_FLOW_ERROR;
+  }
+
+  *outbuf = gst_buffer_new ();
+  gst_buffer_append_memory (*outbuf, (GstMemory *) out_mem);
+  gst_buffer_add_video_meta (*outbuf, (GstVideoFrameFlags) 0,
+      GST_VIDEO_INFO_FORMAT (&filter->out_info),
+      GST_VIDEO_INFO_WIDTH (&filter->in_info),
+      GST_VIDEO_INFO_HEIGHT (&filter->out_info));
+
+  sync_meta = gst_buffer_add_gl_sync_meta (bfilter->context, *outbuf);
+  gst_gl_sync_meta_set_sync_point (sync_meta, bfilter->context);
+
+  bclass->copy_metadata (btrans, buffer, *outbuf);
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_qml6_gl_overlay_transform (GstBaseTransform * btrans, GstBuffer * inbuf,
+    GstBuffer * outbuf)
+{
+  return GST_FLOW_OK;
+}
+
+static GstStateChangeReturn
+gst_qml6_gl_overlay_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstQml6GLOverlay *qml6_gl_overlay = GST_QML6_GL_OVERLAY (element);
+  GstGLBaseFilter *filter = GST_GL_BASE_FILTER (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  GST_DEBUG_OBJECT (filter, "changing state: %s => %s",
+      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY: {
+      QGuiApplication *app;
+      GstGLDisplay *display = NULL;
+
+      app = static_cast<QGuiApplication *> (QCoreApplication::instance ());
+      if (!app) {
+        GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
+            ("%s", "Failed to connect to Qt"),
+            ("%s", "Could not retrieve QGuiApplication instance"));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      display = gst_qml6_get_gl_display (FALSE);
+
+      if (display != filter->display)
+        /* always propagate. The application may need to choose between window
+         * system display connections */
+        gst_gl_element_propagate_display_context (GST_ELEMENT (qml6_gl_overlay), display);
+      gst_object_unref (display);
+      break;
+    }
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    return ret;
+
+  switch (transition) {
+    default:
+      break;
+  }
+
+  return ret;
+
+}
diff --git a/ext/qt6/gstqml6gloverlay.h b/ext/qt6/gstqml6gloverlay.h
new file mode 100644
index 0000000000000000000000000000000000000000..704706775052cb9837b9104bec79f652b27d2d98
--- /dev/null
+++ b/ext/qt6/gstqml6gloverlay.h
@@ -0,0 +1,76 @@
+/*
+ * GStreamer
+ * Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_QML6_GL_OVERLAY_H__
+#define __GST_QML6_GL_OVERLAY_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/gl/gl.h>
+#include "qt6glrenderer.h"
+#include "qt6glitem.h"
+
+typedef struct _GstQml6GLOverlay GstQml6GLOverlay;
+typedef struct _GstQml6GLOverlayClass GstQml6GLOverlayClass;
+typedef struct _GstQml6GLOverlayPrivate GstQml6GLOverlayPrivate;
+
+G_BEGIN_DECLS
+
+GType gst_qml6_gl_overlay_get_type (void);
+#define GST_TYPE_QML6_GL_OVERLAY            (gst_qml6_gl_overlay_get_type())
+#define GST_QML6_GL_OVERLAY(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QML6_GL_OVERLAY,GstQml6GLOverlay))
+#define GST_QML6_GL_OVERLAY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QML6_GL_OVERLAY,GstQml6GLOverlayClass))
+#define GST_IS_QML6_GL_OVERLAY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QML6_GL_OVERLAY))
+#define GST_IS_QML6_GL_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QML6_GL_OVERLAY))
+#define GST_QML6_GL_OVERLAY_CAST(obj)       ((GstQml6GLOverlay*)(obj))
+
+/**
+ * GstQml6GLOverlay:
+ *
+ * Opaque #GstQml6GLOverlay object
+ */
+struct _GstQml6GLOverlay
+{
+  /* <private> */
+  GstGLFilter           parent;
+
+  gchar                *qml_scene;
+
+  GstQt6QuickRenderer     *renderer;
+
+  QSharedPointer<Qt6GLVideoItemInterface> widget;
+};
+
+/**
+ * GstQml6GLOverlayClass:
+ *
+ * The #GstQml6GLOverlayClass struct only contains private data
+ */
+struct _GstQml6GLOverlayClass
+{
+  /* <private> */
+  GstGLFilterClass parent_class;
+};
+
+GstQml6GLOverlay *    gst_qml6_gl_overlay_new (void);
+
+G_END_DECLS
+
+#endif /* __GST_QML6_GL_OVERLAY_H__ */
diff --git a/ext/qt6/gstqml6glsink.cc b/ext/qt6/gstqml6glsink.cc
index cff6277955c1634daac620e44de0237ae8427af6..068d343a3387935e4324a7b13bdc5be8f3d5b69b 100644
--- a/ext/qt6/gstqml6glsink.cc
+++ b/ext/qt6/gstqml6glsink.cc
@@ -43,29 +43,30 @@
  * #GstGLDisplay object and as Qt cannot currently share an existing window
  * system display connection, GStreamer must use the window system display
  * connection provided by Qt.  This window system display connection can be
- * retrieved by either a qmlglsink element or a qmlgloverlay element. The
- * recommended usage is to have either element (qmlglsink or qmlgloverlay)
- * be the first to propagate the #GstGLDisplay for the entire pipeline to use by
- * setting either element to the READY element state before any other OpenGL
- * element in the pipeline.
+ * retrieved by either a `qml6glsink` element, a `qml6gloverlay` element, or a
+ * `qml6glmixer` element. The recommended usage is to have either element
+ * (`qml6glsink`, or `qml6gloverlay`, or `qml6glmixer`) be the first to
+ * propagate the #GstGLDisplay for the entire pipeline to use by setting either
+ * element to the READY element state before any other OpenGL element in the
+ * pipeline.
  *
- * In a dynamically adding qmlglsink (or qmlgloverlay) to a pipeline case,
- * there are some considerations for ensuring that the window system display
- * and OpenGL contexts are compatible with Qt.  When the qmlgloverlay (or
- * qmlglsink) element is added and brought up to READY, it will propagate it's
- * own #GstGLDisplay using the #GstContext mechanism regardless of any existing
- * #GstGLDisplay used by the pipeline previously.  In order for the new
- * #GstGLDisplay to be used, the application must then set the provided
- * #GstGLDisplay containing #GstContext on the pipeline.  This may effectively
- * cause each OpenGL element to replace the window system display and also the
- * OpenGL context it is using.  As such this process may take a significant
- * amount of time and resources as objects are recreated in the new OpenGL
- * context.
+ * In the dynamically adding `qml6glsink` (or `qml6gloverlay`, or `qml6glmixer`)
+ * to a pipeline case, there are some considerations for ensuring that the
+ * window system display and OpenGL contexts are compatible with Qt.  When the
+ * `qml6gloverlay` (or `qml6glsink`, or `qml6glmixer`) element is added and
+ * brought up to READY, it will propagate it's own #GstGLDisplay using the
+ * #GstContext mechanism regardless of any existing #GstGLDisplay used by the
+ * pipeline previously.  In order for the new #GstGLDisplay to be used, the
+ * application must then set the provided #GstGLDisplay containing #GstContext
+ * on the pipeline.  This may effectively cause each OpenGL element to replace
+ * the window system display and also the OpenGL context it is using.  As such
+ * this process may take a significant amount of time and resources as objects
+ * are recreated in the new OpenGL context.
  *
- * All instances of qmlglsink and qmlgloverlay will return the exact same
- * #GstGLDisplay object while the pipeline is running regardless of whether
- * any qmlglsink or qmlgloverlay elements are added or removed from the
- * pipeline.
+ * All instances of `qml6glsink`, `qml6gloverlay`, and `qml6glmixer` will
+ * return the exact same #GstGLDisplay object while the pipeline is running
+ * regardless of whether any `qml6glsink` or `qml6gloverlay` elements are
+ * added or removed from the pipeline.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -108,7 +109,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
-    "format = (string) { RGB, RGBA }, "
+    "format = (string) { RGBA, BGRA, RGB, YV12 }, "
     "width = " GST_VIDEO_SIZE_RANGE ", "
     "height = " GST_VIDEO_SIZE_RANGE ", "
     "framerate = " GST_VIDEO_FPS_RANGE ", "
diff --git a/ext/qt6/gstqml6glsrc.cc b/ext/qt6/gstqml6glsrc.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dbd76844bf2ab03cc8b5bb52ae099e9a628f1c62
--- /dev/null
+++ b/ext/qt6/gstqml6glsrc.cc
@@ -0,0 +1,599 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:qml6glsrc
+ *
+ * Since: 1.24
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstqt6elements.h"
+#include "gstqml6glsrc.h"
+#include <QtGui/QGuiApplication>
+
+#define GST_CAT_DEFAULT gst_debug_qml6_gl_src
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+#define DEFAULT_IS_LIVE TRUE
+
+static void gst_qml6_gl_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_qml6_gl_src_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static void gst_qml6_gl_src_finalize (GObject * object);
+
+static gboolean gst_qml6_gl_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
+static GstCaps *gst_qml6_gl_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter);
+static gboolean gst_qml6_gl_src_query (GstBaseSrc * bsrc, GstQuery * query);
+
+static gboolean gst_qml6_gl_src_decide_allocation (GstBaseSrc * bsrc,
+    GstQuery * query);
+static GstFlowReturn gst_qml6_gl_src_create (GstPushSrc * psrc, GstBuffer ** buffer);
+static gboolean gst_qml6_gl_src_unlock(GstBaseSrc * bsrc);
+static gboolean gst_qml6_gl_src_unlock_stop (GstBaseSrc * bsrc);
+static GstStateChangeReturn gst_qml6_gl_src_change_state (GstElement * element,
+    GstStateChange transition);
+static gboolean gst_qml6_gl_src_start (GstBaseSrc * basesrc);
+static gboolean gst_qml6_gl_src_stop (GstBaseSrc * basesrc);
+
+static GstStaticPadTemplate gst_qml6_gl_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
+        "format = (string) RGBA, "
+        "width = " GST_VIDEO_SIZE_RANGE ", "
+        "height = " GST_VIDEO_SIZE_RANGE ", "
+        "framerate = " GST_VIDEO_FPS_RANGE ", "
+        "texture-target = (string) 2D"));
+
+enum
+{
+  ARG_0,
+  PROP_WINDOW,
+  PROP_DEFAULT_FBO
+};
+
+#define gst_qml6_gl_src_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstQml6GLSrc, gst_qml6_gl_src,
+    GST_TYPE_PUSH_SRC, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
+        "qml6glsrc", 0, "Qt6 Qml Video Src"));
+GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qml6glsrc, "qml6glsrc",
+    GST_RANK_NONE, GST_TYPE_QML6_GL_SRC, qt6_element_init (plugin));
+
+static const gfloat vertical_flip_matrix[] = {
+  1.0f, 0.0f, 0.0f, 0.0f,
+  0.0f, -1.0f, 0.0f, 0.0f,
+  0.0f, 0.0f, 1.0f, 0.0f,
+  0.0f, 1.0f, 0.0f, 1.0f,
+};
+
+static void
+gst_qml6_gl_src_class_init (GstQml6GLSrcClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstElementClass *gstelement_class = (GstElementClass *) klass;
+  GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
+  GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass;
+
+  gobject_class->set_property = gst_qml6_gl_src_set_property;
+  gobject_class->get_property = gst_qml6_gl_src_get_property;
+  gobject_class->finalize = gst_qml6_gl_src_finalize;
+
+  gst_element_class_set_metadata (gstelement_class, "Qt Video Source",
+      "Source/Video", "A video src that captures a window from a QML view",
+      "Multimedia Team <shmmmw@freescale.com>");
+
+  g_object_class_install_property (gobject_class, PROP_WINDOW,
+      g_param_spec_pointer ("window", "QQuickWindow",
+          "The QQuickWindow to place in the object hierarchy",
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+  g_object_class_install_property (gobject_class, PROP_DEFAULT_FBO,
+      g_param_spec_boolean ("use-default-fbo",
+          "Whether to use default FBO",
+          "When set it will not create a new FBO for the QML render thread",
+          FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_qml6_gl_src_template));
+
+  gstelement_class->change_state = gst_qml6_gl_src_change_state;
+  gstbasesrc_class->set_caps = gst_qml6_gl_src_setcaps;
+  gstbasesrc_class->get_caps = gst_qml6_gl_src_get_caps;
+  gstbasesrc_class->query = gst_qml6_gl_src_query;
+  gstbasesrc_class->start = gst_qml6_gl_src_start;
+  gstbasesrc_class->stop = gst_qml6_gl_src_stop;
+  gstbasesrc_class->decide_allocation = gst_qml6_gl_src_decide_allocation;
+  gstbasesrc_class->unlock = gst_qml6_gl_src_unlock;
+  gstbasesrc_class->unlock_stop = gst_qml6_gl_src_unlock_stop;
+
+  gstpushsrc_class->create = gst_qml6_gl_src_create;
+}
+
+static void
+gst_qml6_gl_src_init (GstQml6GLSrc * src)
+{
+  gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
+  gst_base_src_set_live (GST_BASE_SRC (src), DEFAULT_IS_LIVE);
+  src->default_fbo = FALSE;
+  src->pending_image_orientation = TRUE;
+}
+
+static void
+gst_qml6_gl_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (object);
+
+  switch (prop_id) {
+    case PROP_WINDOW:{
+      qt_src->qwindow =
+          static_cast < QQuickWindow * >(g_value_get_pointer (value));
+
+      if (qt_src->window) {
+        delete qt_src->window;
+        qt_src->window = NULL;
+      }
+
+      if (qt_src->qwindow)
+        qt_src->window = new Qt6GLWindow (NULL, qt_src->qwindow);
+
+      break;
+    }
+    case PROP_DEFAULT_FBO:
+      qt_src->default_fbo = g_value_get_boolean (value);
+      if (qt_src->window)
+        qt6_gl_window_use_default_fbo (qt_src->window, qt_src->default_fbo);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qml6_gl_src_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (object);
+
+  switch (prop_id) {
+    case PROP_WINDOW:
+      g_value_set_pointer (value, qt_src->qwindow);
+      break;
+    case PROP_DEFAULT_FBO:
+      g_value_set_boolean (value, qt_src->default_fbo);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_qml6_gl_src_finalize (GObject * object)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (object);
+
+  GST_DEBUG ("qmlglsrc finalize");
+  if (qt_src->context)
+    gst_object_unref (qt_src->context);
+  qt_src->context = NULL;
+
+  if (qt_src->qt_context)
+    gst_object_unref (qt_src->qt_context);
+  qt_src->qt_context = NULL;
+
+  if (qt_src->display)
+    gst_object_unref (qt_src->display);
+  qt_src->display = NULL;
+
+  if (qt_src->window)
+    delete qt_src->window;
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_qml6_gl_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (bsrc);
+
+  GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
+
+  if (!gst_video_info_from_caps (&qt_src->v_info, caps))
+    return FALSE;
+
+  return TRUE;
+}
+
+static GstCaps *
+gst_qml6_gl_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
+{
+  GstCaps *caps = NULL, *temp = NULL;
+  GstPadTemplate *pad_template;
+  GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (bsrc);
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (bsrc);
+  guint i;
+  gint width, height;
+
+  if (qt_src->window) {
+    qt_src->window->getGeometry (&width, &height);
+  }
+
+  pad_template =
+      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
+  if (pad_template != NULL)
+    caps = gst_pad_template_get_caps (pad_template);
+
+  if (qt_src->window) {
+    temp = gst_caps_copy (caps);
+    guint n_caps = gst_caps_get_size (caps);
+
+    for (i = 0; i < n_caps; i++) {
+      GstStructure *s = gst_caps_get_structure (temp, i);
+      gst_structure_set (s, "width", G_TYPE_INT, width, NULL);
+      gst_structure_set (s, "height", G_TYPE_INT, height, NULL);
+      /* because the framerate is unknown */
+      gst_structure_set (s, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
+      gst_structure_set (s, "pixel-aspect-ratio",
+          GST_TYPE_FRACTION, 1, 1, NULL);
+    }
+
+    gst_caps_unref (caps);
+    caps = temp;
+  }
+
+  if (filter) {
+    GstCaps *intersection;
+
+    intersection =
+        gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (caps);
+    caps = intersection;
+  }
+
+  return caps;
+}
+
+static gboolean
+gst_qml6_gl_src_query (GstBaseSrc * bsrc, GstQuery * query)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (bsrc);
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_CONTEXT:
+    {
+      if (!qt6_gl_window_is_scenegraph_initialized (qt_src->window))
+        return FALSE;
+
+      if (!qt_src->display && !qt_src->qt_context) {
+        if (!qt_src->display)
+          qt_src->display = qt6_gl_window_get_display (qt_src->window);
+        if (!qt_src->qt_context)
+          qt_src->qt_context = qt6_gl_window_get_qt_context (qt_src->window);
+        if (!qt_src->context)
+          qt_src->context = qt6_gl_window_get_context (qt_src->window);
+      }
+
+      if (gst_gl_handle_context_query ((GstElement *) qt_src, query,
+          qt_src->display, qt_src->context, qt_src->qt_context))
+        return TRUE;
+
+      /* fallthrough */
+    }
+    default:
+      res = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
+      break;
+  }
+
+  return res;
+}
+
+static gboolean
+_find_local_gl_context (GstQml6GLSrc * qt_src)
+{
+  if (gst_gl_query_local_gl_context (GST_ELEMENT (qt_src), GST_PAD_SRC,
+      &qt_src->context))
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean
+gst_qml6_gl_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
+{
+  GstBufferPool *pool = NULL;
+  GstStructure *config;
+  GstCaps *caps;
+  guint min, max, size, n, i;
+  gboolean update_pool, update_allocator;
+  GstAllocator *allocator;
+  GstAllocationParams params;
+  GstGLVideoAllocationParams *glparams;
+  GstVideoInfo vinfo;
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (bsrc);
+
+  if (gst_query_find_allocation_meta (query,
+          GST_VIDEO_AFFINE_TRANSFORMATION_META_API_TYPE, NULL)) {
+    qt_src->downstream_supports_affine_meta = TRUE;
+  } else {
+    qt_src->downstream_supports_affine_meta = FALSE;
+  }
+
+  gst_query_parse_allocation (query, &caps, NULL);
+  if (!caps)
+    return FALSE;
+
+  gst_video_info_from_caps (&vinfo, caps);
+
+  n = gst_query_get_n_allocation_pools (query);
+  if (n > 0) {
+    update_pool = TRUE;
+    for (i = 0; i < n; i++) {
+      gst_query_parse_nth_allocation_pool (query, i, &pool, &size, &min, &max);
+
+      if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) {
+        if (pool)
+          gst_object_unref (pool);
+        pool = NULL;
+      }
+    }
+  }
+
+  if (!pool) {
+    size = vinfo.size;
+    min = max = 0;
+    update_pool = FALSE;
+  }
+
+  if (!qt_src->context && !_find_local_gl_context (qt_src))
+    return FALSE;
+
+  if (!qt6_gl_window_set_context (qt_src->window, qt_src->context))
+    return FALSE;
+
+  if (!pool) {
+    if (!qt_src->context || !GST_IS_GL_CONTEXT (qt_src->context))
+      return FALSE;
+
+    pool = gst_gl_buffer_pool_new (qt_src->context);
+    GST_INFO_OBJECT (qt_src, "No pool, create one ourself %p", pool);
+  }
+
+  config = gst_buffer_pool_get_config (pool);
+
+  gst_buffer_pool_config_set_params (config, caps, size, min, max);
+  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
+  if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL))
+    gst_buffer_pool_config_add_option (config,
+        GST_BUFFER_POOL_OPTION_GL_SYNC_META);
+
+  if (gst_query_get_n_allocation_params (query) > 0) {
+    gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
+    gst_buffer_pool_config_set_allocator (config, allocator, &params);
+    GST_INFO_OBJECT (qt_src, "got allocator %p", allocator);
+    update_allocator = TRUE;
+  } else {
+    allocator = NULL;
+    gst_allocation_params_init (&params);
+    update_allocator = FALSE;
+  }
+
+  glparams =
+      gst_gl_video_allocation_params_new (qt_src->context, &params, &vinfo, 0,
+      NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA);
+  gst_buffer_pool_config_set_gl_allocation_params (config,
+      (GstGLAllocationParams *) glparams);
+  gst_gl_allocation_params_free ((GstGLAllocationParams *) glparams);
+
+  if (!gst_buffer_pool_set_config (pool, config))
+    GST_WARNING_OBJECT (qt_src, "Failed to set buffer pool config");
+
+  if (update_allocator)
+    gst_query_set_nth_allocation_param (query, 0, allocator, &params);
+  else
+    gst_query_add_allocation_param (query, allocator, &params);
+  if (allocator)
+    gst_object_unref (allocator);
+
+  if (update_pool)
+    gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
+  else
+    gst_query_add_allocation_pool (query, pool, size, min, max);
+  gst_object_unref (pool);
+
+  GST_INFO_OBJECT (qt_src, "successfully decide_allocation");
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_qml6_gl_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (psrc);
+  GstCaps *updated_caps = NULL;
+  GstGLContext* context = qt_src->context;
+  GstGLSyncMeta *sync_meta;
+
+  *buffer = qt6_gl_window_take_buffer (qt_src->window, &updated_caps);
+  GST_DEBUG_OBJECT (qt_src, "produced buffer %p", *buffer);
+  if (!*buffer)
+    return GST_FLOW_FLUSHING;
+
+  if (updated_caps) {
+    GST_DEBUG_OBJECT (qt_src, "new_caps %" GST_PTR_FORMAT, updated_caps);
+    gst_base_src_set_caps (GST_BASE_SRC (qt_src), updated_caps);
+  }
+  gst_clear_caps (&updated_caps);
+
+  sync_meta = gst_buffer_get_gl_sync_meta(*buffer);
+  if (sync_meta)
+      gst_gl_sync_meta_wait(sync_meta, context);
+
+  if (!qt_src->downstream_supports_affine_meta) {
+    if (qt_src->pending_image_orientation) {
+      /* let downstream know the image orientation is vertical filp */
+      GstTagList *image_orientation_tag =
+          gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION, "flip-rotate-180", NULL);
+
+      gst_pad_push_event (GST_BASE_SRC_PAD (psrc),
+          gst_event_new_tag (image_orientation_tag));
+
+      qt_src->pending_image_orientation = FALSE;
+    }
+  } else {
+    GstVideoAffineTransformationMeta *trans_meta;
+    trans_meta = gst_buffer_add_video_affine_transformation_meta (*buffer);
+    gst_video_affine_transformation_meta_apply_matrix (trans_meta,
+        vertical_flip_matrix);
+  }
+
+  GST_DEBUG_OBJECT (qt_src, "buffer create done %p", *buffer);
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_qml6_gl_src_unlock (GstBaseSrc * bsrc)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (bsrc);
+
+  if (!qt_src->window)
+    return TRUE;
+
+  qt6_gl_window_unlock (qt_src->window);
+
+  return TRUE;
+}
+
+static gboolean
+gst_qml6_gl_src_unlock_stop (GstBaseSrc * bsrc)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (bsrc);
+
+  if (!qt_src->window)
+    return TRUE;
+
+  qt6_gl_window_unlock_stop (qt_src->window);
+
+  return TRUE;
+}
+
+static GstStateChangeReturn
+gst_qml6_gl_src_change_state (GstElement * element, GstStateChange transition)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  QGuiApplication *app;
+
+  GST_DEBUG ("changing state: %s => %s",
+      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      app = static_cast < QGuiApplication * >(QCoreApplication::instance ());
+      if (!app) {
+        GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
+            ("%s", "Failed to connect to Qt"),
+            ("%s", "Could not retrieve QGuiApplication instance"));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      if (!qt_src->window) {
+        GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
+            ("%s", "Required property \'window\' not set"), (NULL));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      if (!qt6_gl_window_is_scenegraph_initialized (qt_src->window)) {
+        GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
+            ("%s", "Could not initialize window system"), (NULL));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      qt6_gl_window_use_default_fbo (qt_src->window, qt_src->default_fbo);
+
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      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_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static gboolean
+gst_qml6_gl_src_start (GstBaseSrc * basesrc)
+{
+  GstQml6GLSrc *qt_src = GST_QML6_GL_SRC (basesrc);
+
+  /* already has get OpenGL configuration from qt */
+  if (qt_src->display && qt_src->qt_context)
+    return TRUE;
+
+  if (!qt6_gl_window_is_scenegraph_initialized (qt_src->window))
+    return FALSE;
+
+  qt_src->display = qt6_gl_window_get_display (qt_src->window);
+  qt_src->qt_context = qt6_gl_window_get_qt_context (qt_src->window);
+  qt_src->context = qt6_gl_window_get_context (qt_src->window);
+
+  if (!qt_src->display || !qt_src->qt_context) {
+    GST_ERROR_OBJECT (qt_src,
+        "Could not retrieve window system OpenGL configuration");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (qt_src, "Got qt display %p and qt gl context %p",
+      qt_src->display, qt_src->qt_context);
+  return TRUE;
+}
+
+static gboolean
+gst_qml6_gl_src_stop (GstBaseSrc * basesrc)
+{
+  return TRUE;
+}
diff --git a/ext/qt6/gstqml6glsrc.h b/ext/qt6/gstqml6glsrc.h
new file mode 100644
index 0000000000000000000000000000000000000000..96ad503f179cd1adfd273330d1c691acce32bcb7
--- /dev/null
+++ b/ext/qt6/gstqml6glsrc.h
@@ -0,0 +1,62 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_QML6_GL_SRC_H__
+#define __GST_QML6_GL_SRC_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstpushsrc.h>
+#include <gst/video/video.h>
+#include <gst/gl/gl.h>
+#include "qt6glwindow.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_QML6_GL_SRC (gst_qml6_gl_src_get_type())
+G_DECLARE_FINAL_TYPE (GstQml6GLSrc, gst_qml6_gl_src, GST, QML6_GL_SRC, GstPushSrc)
+#define GST_QML6_GL_SRC_CAST(obj) ((GstQml6GLSrc*)(obj))
+
+/**
+ * GstQml6GLSrc:
+ *
+ * Opaque #GstQml6GLSrc object
+ */
+struct _GstQml6GLSrc
+{
+  /* <private> */
+  GstPushSrc            parent;
+
+  QQuickWindow         *qwindow;
+  Qt6GLWindow          *window;
+
+  GstVideoInfo          v_info;
+
+  GstGLDisplay         *display;
+  GstGLContext         *context;
+  GstGLContext         *qt_context;
+
+  gboolean              default_fbo;
+  gboolean              downstream_supports_affine_meta;
+  gboolean              pending_image_orientation;
+};
+
+G_END_DECLS
+
+#endif /* __GST_QML6_GL_SRC_H__ */
diff --git a/ext/qt6/gstqsg6glnode.cc b/ext/qt6/gstqsg6glnode.cc
deleted file mode 100644
index f36cb2a7d44770938ff9d406473d1db2820878e1..0000000000000000000000000000000000000000
--- a/ext/qt6/gstqsg6glnode.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "gstqsg6glnode.h"
-
-#include <QtQuick/QSGTextureProvider>
-#include <QtQuick/QSGSimpleTextureNode>
-#include <QtQuick/QQuickWindow>
-#include <QtQuick/QSGTexture>
-
-#define GST_CAT_DEFAULT gst_qsg_texture_debug
-GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
-
-GstQSG6OpenGLNode::GstQSG6OpenGLNode(QQuickItem * item)
-{
-  static gsize _debug;
-
-  if (g_once_init_enter (&_debug)) {
-    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtqsgtexture", 0,
-        "Qt Scenegraph Texture");
-    g_once_init_leave (&_debug, 1);
-  }
-
-  gst_video_info_init (&this->v_info);
-
-  this->buffer_ = NULL;
-  this->sync_buffer_ = gst_buffer_new ();
-  this->dummy_tex_ = nullptr;
-  // TODO; handle windowChanged?
-  this->window_ = item->window();
-}
-
-GstQSG6OpenGLNode::~GstQSG6OpenGLNode()
-{
-  gst_buffer_replace (&this->buffer_, NULL);
-  gst_buffer_replace (&this->sync_buffer_, NULL);
-  this->buffer_was_bound = FALSE;
-  delete this->dummy_tex_;
-  this->dummy_tex_ = nullptr;
-}
-
-QSGTexture *
-GstQSG6OpenGLNode::texture() const
-{
-  return QSGSimpleTextureNode::texture();
-}
-
-/* only called from the streaming thread with scene graph thread blocked */
-void
-GstQSG6OpenGLNode::setCaps (GstCaps * caps)
-{
-  GST_LOG ("%p setCaps %" GST_PTR_FORMAT, this, caps);
-
-  if (caps)
-    gst_video_info_from_caps (&this->v_info, caps);
-  else
-    gst_video_info_init (&this->v_info);
-}
-
-/* only called from the streaming thread with scene graph thread blocked */
-GstBuffer *
-GstQSG6OpenGLNode::getBuffer ()
-{
-  GstBuffer *buffer = NULL;
-
-  if (this->buffer_)
-    buffer = gst_buffer_ref (this->buffer_);
-
-  return buffer;
-}
-
-/* only called from the streaming thread with scene graph thread blocked */
-void
-GstQSG6OpenGLNode::setBuffer (GstBuffer * buffer)
-{
-  GstGLContext *qt_context = NULL;
-  gboolean buffer_changed;
-
-  GST_LOG ("%p setBuffer %" GST_PTR_FORMAT, this, buffer);
-  /* FIXME: update more state here */
-  buffer_changed = gst_buffer_replace (&this->buffer_, buffer);
-
-  if (buffer_changed) {
-    GstGLContext *context;
-    GstGLSyncMeta *sync_meta;
-    GstMemory *mem;
-    guint tex_id;
-    QQuickWindow::CreateTextureOptions options = QQuickWindow::TextureHasAlphaChannel;
-    QSGTexture *texture = nullptr;
-    QSize texSize;
-
-    qt_context = gst_gl_context_get_current();
-    if (!qt_context)
-      goto use_dummy_tex;
-
-    if (!this->buffer_)
-      goto use_dummy_tex;
-    if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN)
-      goto use_dummy_tex;
-
-    this->mem_ = gst_buffer_peek_memory (this->buffer_, 0);
-    if (!this->mem_)
-      goto use_dummy_tex;
-
-    /* FIXME: should really lock the memory to prevent write access */
-    if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_,
-          (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
-      g_assert_not_reached ();
-      goto use_dummy_tex;
-    }
-
-    mem = gst_buffer_peek_memory (this->buffer_, 0);
-    g_assert (gst_is_gl_memory (mem));
-
-    context = ((GstGLBaseMemory *)mem)->context;
-
-    sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_);
-    if (!sync_meta)
-      sync_meta = gst_buffer_add_gl_sync_meta (context, this->sync_buffer_);
-
-    gst_gl_sync_meta_set_sync_point (sync_meta, context);
-
-    gst_gl_sync_meta_wait (sync_meta, qt_context);
-
-    tex_id = *(guint *) this->v_frame.data[0];
-    GST_LOG ("%p binding Qt texture %u", this, tex_id);
-
-    texSize = QSize(GST_VIDEO_FRAME_WIDTH (&this->v_frame), GST_VIDEO_FRAME_HEIGHT (&this->v_frame));
-    // XXX: ideally, we would like to subclass the relevant texture object
-    // ourselves but this is good enough for now
-    texture = QNativeInterface::QSGOpenGLTexture::fromNative(tex_id, this->window_, texSize, options);
-
-    setTexture(texture);
-    setOwnsTexture(true);
-    markDirty(QSGNode::DirtyMaterial);
-
-    gst_video_frame_unmap (&this->v_frame);
-
-    /* Texture was successfully bound, so we do not need
-     * to use the dummy texture */
-  }
-
-  if (!texture()) {
-use_dummy_tex:
-    /* Create dummy texture if not already present. */
-    if (this->dummy_tex_ == nullptr) {
-      /* Make this a black 64x64 pixel RGBA texture.
-       * This size and format is supported pretty much everywhere, so these
-       * are a safe pick. (64 pixel sidelength must be supported according
-       * to the GLES2 spec, table 6.18.)
-       * Set min/mag filters to GL_LINEAR to make sure no mipmapping is used. */
-      const int tex_sidelength = 64;
-      QImage image(tex_sidelength, tex_sidelength, QImage::Format_ARGB32);
-      image.fill(QColor(0, 0, 0, 255));
-
-      this->dummy_tex_ = this->window_->createTextureFromImage(image);
-    }
-
-    g_assert (this->dummy_tex_ != nullptr);
-
-    if (texture() != this->dummy_tex_) {
-      setTexture(this->dummy_tex_);
-      setOwnsTexture(false);
-      markDirty(QSGNode::DirtyMaterial);
-    }
-
-    GST_LOG ("%p binding fallback dummy Qt texture %p", this, this->dummy_tex_);
-  }
-}
diff --git a/ext/qt6/gstqsg6glnode.h b/ext/qt6/gstqsg6glnode.h
deleted file mode 100644
index 0428fa5dea188e3f3c74519a0836e90a4fa66b78..0000000000000000000000000000000000000000
--- a/ext/qt6/gstqsg6glnode.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-#include <gst/gst.h>
-#include <gst/gl/gl.h>
-
-#include "gstqt6gl.h"
-#include <QtQuick/QQuickItem>
-#include <QtQuick/QSGTexture>
-#include <QtQuick/QSGTextureProvider>
-#include <QtQuick/QSGSimpleTextureNode>
-#include <QtGui/QOpenGLFunctions>
-
-class GstQSG6OpenGLNode : public QSGTextureProvider, public QSGSimpleTextureNode, protected QOpenGLFunctions
-{
-  Q_OBJECT
-
-public:
-  GstQSG6OpenGLNode(QQuickItem *item);
-  ~GstQSG6OpenGLNode();
-
-  QSGTexture *texture() const override;
-
-  void setCaps(GstCaps *caps);
-  void setBuffer(GstBuffer *buffer);
-  GstBuffer *getBuffer();
-
-  void updateQSGTexture();
-
-private:
-  QQuickWindow *window_;
-  GstBuffer * buffer_;
-  gboolean buffer_was_bound;
-  GstBuffer * sync_buffer_;
-  GstMemory * mem_;
-  QSGTexture *dummy_tex_;
-  GstVideoInfo v_info;
-  GstVideoFrame v_frame;
-};
diff --git a/ext/qt6/gstqsg6material.cc b/ext/qt6/gstqsg6material.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9e64d4ab46988bbc67b0c33922b61ee4bcb3faf4
--- /dev/null
+++ b/ext/qt6/gstqsg6material.cc
@@ -0,0 +1,649 @@
+/*
+ * GStreamer
+ * Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <vector>
+#include <stdio.h>
+
+#include <gst/video/video.h>
+#include <gst/gl/gl.h>
+#include <gst/gl/gstglfuncs.h>
+#include "gstqsg6material.h"
+#include <private/qrhi_p.h>
+
+#define GST_CAT_DEFAULT gst_qsg_texture_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+/* matrix colour conversion code from vkvideoconvert.c */
+typedef struct
+{
+  double dm[4][4];
+} Matrix4;
+
+static void
+matrix_debug (const Matrix4 * s)
+{
+  GST_DEBUG ("[%f %f %f %f]", s->dm[0][0], s->dm[0][1], s->dm[0][2],
+      s->dm[0][3]);
+  GST_DEBUG ("[%f %f %f %f]", s->dm[1][0], s->dm[1][1], s->dm[1][2],
+      s->dm[1][3]);
+  GST_DEBUG ("[%f %f %f %f]", s->dm[2][0], s->dm[2][1], s->dm[2][2],
+      s->dm[2][3]);
+  GST_DEBUG ("[%f %f %f %f]", s->dm[3][0], s->dm[3][1], s->dm[3][2],
+      s->dm[3][3]);
+}
+
+static void
+matrix_to_float (const Matrix4 * m, float *ret)
+{
+  int i, j;
+
+  for (i = 0; i < 4; i++) {
+    for (j = 0; j < 4; j++) {
+      ret[j * 4 + i] = m->dm[i][j];
+    }
+  }
+}
+
+static void
+matrix_set_identity (Matrix4 * m)
+{
+  int i, j;
+
+  for (i = 0; i < 4; i++) {
+    for (j = 0; j < 4; j++) {
+      m->dm[i][j] = (i == j);
+    }
+  }
+}
+
+static void
+matrix_copy (Matrix4 * d, const Matrix4 * s)
+{
+  gint i, j;
+
+  for (i = 0; i < 4; i++)
+    for (j = 0; j < 4; j++)
+      d->dm[i][j] = s->dm[i][j];
+}
+
+/* Perform 4x4 matrix multiplication:
+ *  - @dst@ = @a@ * @b@
+ *  - @dst@ may be a pointer to @a@ andor @b@
+ */
+static void
+matrix_multiply (Matrix4 * dst, Matrix4 * a, Matrix4 * b)
+{
+  Matrix4 tmp;
+  int i, j, k;
+
+  for (i = 0; i < 4; i++) {
+    for (j = 0; j < 4; j++) {
+      double x = 0;
+      for (k = 0; k < 4; k++) {
+        x += a->dm[i][k] * b->dm[k][j];
+      }
+      tmp.dm[i][j] = x;
+    }
+  }
+  matrix_copy (dst, &tmp);
+}
+
+static void
+matrix_offset_components (Matrix4 * m, double a1, double a2, double a3)
+{
+  Matrix4 a;
+
+  matrix_set_identity (&a);
+  a.dm[0][3] = a1;
+  a.dm[1][3] = a2;
+  a.dm[2][3] = a3;
+  matrix_debug (&a);
+  matrix_multiply (m, &a, m);
+}
+
+static void
+matrix_scale_components (Matrix4 * m, double a1, double a2, double a3)
+{
+  Matrix4 a;
+
+  matrix_set_identity (&a);
+  a.dm[0][0] = a1;
+  a.dm[1][1] = a2;
+  a.dm[2][2] = a3;
+  matrix_multiply (m, &a, m);
+}
+
+static void
+matrix_YCbCr_to_RGB (Matrix4 * m, double Kr, double Kb)
+{
+  double Kg = 1.0 - Kr - Kb;
+  Matrix4 k = {
+    {
+          {1., 0., 2 * (1 - Kr), 0.},
+          {1., -2 * Kb * (1 - Kb) / Kg, -2 * Kr * (1 - Kr) / Kg, 0.},
+          {1., 2 * (1 - Kb), 0., 0.},
+          {0., 0., 0., 1.},
+        }
+  };
+
+  matrix_multiply (m, &k, m);
+}
+
+static void
+convert_to_RGB (GstVideoInfo *info, Matrix4 * m)
+{
+  {
+    const GstVideoFormatInfo *uinfo;
+    gint offset[4], scale[4], depth[4];
+    guint i;
+
+    uinfo = gst_video_format_get_info (GST_VIDEO_INFO_FORMAT (info));
+
+    /* bring color components to [0..1.0] range */
+    gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
+        scale);
+
+    for (i = 0; i < uinfo->n_components; i++)
+      depth[i] = (1 << uinfo->depth[i]) - 1;
+
+    matrix_offset_components (m, -offset[0] / (float) depth[0],
+        -offset[1] / (float) depth[1], -offset[2] / (float) depth[2]);
+    matrix_scale_components (m, depth[0] / ((float) scale[0]),
+        depth[1] / ((float) scale[1]), depth[2] / ((float) scale[2]));
+    GST_DEBUG ("to RGB scale/offset matrix");
+    matrix_debug (m);
+  }
+
+  if (GST_VIDEO_INFO_IS_YUV (info)) {
+    gdouble Kr, Kb;
+
+    if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
+      matrix_YCbCr_to_RGB (m, Kr, Kb);
+    GST_DEBUG ("to RGB matrix");
+    matrix_debug (m);
+  }
+}
+
+class GstQSGTexture : public QSGTexture {
+public:
+  GstQSGTexture(QRhiTexture *);
+  ~GstQSGTexture();
+
+  qint64 comparisonKey() const override;
+  bool hasAlphaChannel() const override;
+  bool hasMipmaps() const override { return false; };
+  bool isAtlasTexture() const override { return false; };
+  QSize textureSize() const override;
+
+  QRhiTexture *rhiTexture() const override;
+
+private:
+  QRhiTexture *m_texture;
+  bool m_has_alpha;
+};
+
+GstQSGTexture::GstQSGTexture(QRhiTexture * texture)
+  : m_texture(texture)
+{
+  switch (texture->format()) {
+    case QRhiTexture::RGBA8:
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
+    case QRhiTexture::RGB10A2:
+#endif
+    case QRhiTexture::RGBA16F:
+    case QRhiTexture::RGBA32F:
+      this->m_has_alpha = true;
+      break;
+    default:
+      this->m_has_alpha = false;
+  }
+}
+
+GstQSGTexture::~GstQSGTexture()
+{
+  if (m_texture) {
+    m_texture->deleteLater();
+    m_texture = nullptr;
+  }
+}
+
+qint64
+GstQSGTexture::comparisonKey() const
+{
+  if (this->m_texture)
+      return qint64(qintptr(this->m_texture));
+
+  return qint64(qintptr(this));
+}
+
+bool
+GstQSGTexture::hasAlphaChannel() const
+{
+  return m_has_alpha;
+}
+
+QSize
+GstQSGTexture::textureSize() const
+{
+  // XXX: currently unused
+  return QSize(0, 0);
+}
+
+QRhiTexture *
+GstQSGTexture::rhiTexture() const
+{
+  return m_texture;
+}
+
+class GstQSGMaterialShader : public QSGMaterialShader {
+public:
+  GstQSGMaterialShader(GstVideoFormat v_format);
+  ~GstQSGMaterialShader();
+
+  bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+  void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *) override;
+
+private:
+  GstVideoFormat v_format;
+  QSGTexture *m_textures[GST_VIDEO_MAX_PLANES];
+};
+
+GstQSGMaterialShader::GstQSGMaterialShader(GstVideoFormat v_format)
+  : v_format(v_format)
+{
+  setShaderFileName(VertexStage, ":/org/freedesktop/gstreamer/qml6/vertex.vert.qsb");
+
+  switch (v_format) {
+    case GST_VIDEO_FORMAT_RGBA:
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_RGB:
+      setShaderFileName(FragmentStage, ":/org/freedesktop/gstreamer/qml6/RGBA.frag.qsb");
+      break;
+    case GST_VIDEO_FORMAT_YV12:
+      setShaderFileName(FragmentStage, ":/org/freedesktop/gstreamer/qml6/YUV_TRIPLANAR.frag.qsb");
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  m_textures[0] = nullptr;
+  m_textures[1] = nullptr;
+  m_textures[2] = nullptr;
+  m_textures[3] = nullptr;
+}
+
+GstQSGMaterialShader::~GstQSGMaterialShader()
+{
+  for (int i = 0; i < 4; i++) {
+    if (m_textures[i]) {
+      delete m_textures[i];
+      m_textures[i] = nullptr;
+    }
+  }
+}
+
+bool
+GstQSGMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+  const GstVideoFormatInfo *finfo = gst_video_format_get_info (v_format);
+  bool changed = false;
+  QByteArray *buf = state.uniformData();
+  Q_ASSERT(buf->size() >= 84);
+
+  GST_TRACE ("%p new material %p old material %p", this, newMaterial, oldMaterial);
+
+  if (state.isMatrixDirty()) {
+    const QMatrix4x4 m = state.combinedMatrix();
+    memcpy(buf->data(), m.constData(), 64);
+    changed = true;
+  }
+
+  if (state.isOpacityDirty()) {
+    const float opacity = state.opacity();
+    memcpy(buf->data() + 144, &opacity, 4);
+    changed = true;
+  }
+
+  auto *mat = static_cast<GstQSGMaterial *>(newMaterial);
+  if (oldMaterial != newMaterial || mat->uniforms.dirty) {
+    memcpy(buf->data() + 64, &mat->uniforms.input_swizzle, 4 * sizeof (int));
+    memcpy(buf->data() + 80, mat->uniforms.color_matrix.constData(), 64);
+    mat->uniforms.dirty = false;
+    changed = true;
+  }
+
+  for (guint i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
+    if (this->m_textures[i]) {
+      delete this->m_textures[i];
+      this->m_textures[i] = nullptr;
+    }
+    if (i < finfo->n_planes)
+      this->m_textures[i] = mat->bind(this, state.rhi(), state.resourceUpdateBatch(), i, v_format);
+  }
+
+  return changed;
+}
+
+void
+GstQSGMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+    QSGMaterial *newMaterial, QSGMaterial *)
+{
+  *texture = this->m_textures[binding - 1];
+  GST_TRACE ("%p binding:%d texture %p", this, binding, *texture);
+}
+
+#define DEFINE_MATERIAL(format) \
+class G_PASTE(GstQSGMaterial_,format) : public GstQSGMaterial { \
+public: \
+  G_PASTE(GstQSGMaterial_,format)(); \
+  ~G_PASTE(GstQSGMaterial_,format)(); \
+  QSGMaterialType *type() const override { static QSGMaterialType type; return &type; }; \
+}; \
+G_PASTE(GstQSGMaterial_,format)::G_PASTE(GstQSGMaterial_,format)() {} \
+G_PASTE(GstQSGMaterial_,format)::~G_PASTE(GstQSGMaterial_,format)() {}
+
+DEFINE_MATERIAL(RGBA_SWIZZLE);
+DEFINE_MATERIAL(YUV_TRIPLANAR);
+
+GstQSGMaterial *
+GstQSGMaterial::new_for_format(GstVideoFormat format)
+{
+  const GstVideoFormatInfo *finfo = gst_video_format_get_info (format);
+
+  if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo) && finfo->n_planes == 1) {
+    return static_cast<GstQSGMaterial *>(new GstQSGMaterial_RGBA_SWIZZLE());
+  }
+
+  switch (format) {
+    case GST_VIDEO_FORMAT_YV12:
+      return static_cast<GstQSGMaterial *>(new GstQSGMaterial_YUV_TRIPLANAR());
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+GstQSGMaterial::GstQSGMaterial ()
+{
+  static gsize _debug;
+
+  if (g_once_init_enter (&_debug)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtqsg6material", 0,
+        "Qt6 Scenegraph Material");
+    g_once_init_leave (&_debug, 1);
+  }
+
+  g_weak_ref_init (&this->qt_context_ref_, NULL);
+  gst_video_info_init (&this->v_info);
+  memset (&this->v_frame, 0, sizeof (this->v_frame));
+
+  this->buffer_ = NULL;
+  this->buffer_was_bound = false;
+  this->sync_buffer_ = gst_buffer_new ();
+
+  this->uniforms.dirty = true;
+}
+
+GstQSGMaterial::~GstQSGMaterial ()
+{
+  g_weak_ref_clear (&this->qt_context_ref_);
+  gst_buffer_replace (&this->buffer_, NULL);
+  gst_buffer_replace (&this->sync_buffer_, NULL);
+  this->buffer_was_bound = false;
+
+  if (this->v_frame.buffer) {
+    gst_video_frame_unmap (&this->v_frame);
+    memset (&this->v_frame, 0, sizeof (this->v_frame));
+  }
+}
+
+bool
+GstQSGMaterial::compatibleWith(GstVideoInfo * v_info)
+{
+  if (GST_VIDEO_INFO_FORMAT (&this->v_info) != GST_VIDEO_INFO_FORMAT (v_info))
+    return false;
+
+  return true;
+}
+
+QSGMaterialShader *
+GstQSGMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
+{
+  GstVideoFormat v_format = GST_VIDEO_INFO_FORMAT (&this->v_info);
+
+  return new GstQSGMaterialShader(v_format);
+}
+
+/* only called from the streaming thread with scene graph thread blocked */
+void
+GstQSGMaterial::setCaps (GstCaps * caps)
+{
+  GST_LOG ("%p setCaps %" GST_PTR_FORMAT, this, caps);
+
+  gst_video_info_from_caps (&this->v_info, caps);
+}
+
+/* only called from the streaming thread with scene graph thread blocked */
+gboolean
+GstQSGMaterial::setBuffer (GstBuffer * buffer)
+{
+  GstGLContext *qt_context = gst_gl_context_get_current ();
+
+  GST_LOG ("%p setBuffer %" GST_PTR_FORMAT " with qt context %" GST_PTR_FORMAT,
+      this, buffer, qt_context);
+  /* FIXME: update more state here */
+
+  g_weak_ref_set (&this->qt_context_ref_, qt_context);
+
+  if (!gst_buffer_replace (&this->buffer_, buffer))
+    return FALSE;
+
+  this->buffer_was_bound = false;
+
+  if (this->v_frame.buffer) {
+    gst_video_frame_unmap (&this->v_frame);
+    memset (&this->v_frame, 0, sizeof (this->v_frame));
+  }
+
+  if (this->buffer_) {
+    if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_,
+          (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
+      g_assert_not_reached ();
+      return FALSE;
+    }
+    gst_gl_video_format_swizzle(GST_VIDEO_INFO_FORMAT (&this->v_info), this->uniforms.input_swizzle);
+
+    Matrix4 m;
+    float matrix_data[16] = { 0.0, };
+
+    matrix_set_identity (&m);
+    convert_to_RGB (&this->v_info, &m);
+    matrix_debug (&m);
+    matrix_to_float (&m, matrix_data);
+
+    this->uniforms.color_matrix = QMatrix4x4(matrix_data);
+    this->uniforms.dirty = true;
+  }
+
+  return TRUE;
+}
+
+/* only called from the streaming thread with scene graph thread blocked */
+GstBuffer *
+GstQSGMaterial::getBuffer (bool * was_bound)
+{
+  GstBuffer *buffer = NULL;
+
+  if (this->buffer_)
+    buffer = gst_buffer_ref (this->buffer_);
+  if (was_bound)
+    *was_bound = this->buffer_was_bound;
+
+  return buffer;
+}
+
+void
+GstQSGMaterial::setFiltering(QSGTexture::Filtering filtering)
+{
+  m_filtering = filtering;
+}
+
+static QRhiTexture::Format
+video_format_to_rhi_format (GstVideoFormat format, guint plane)
+{
+  switch (format) {
+    case GST_VIDEO_FORMAT_RGBA:
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_RGB:
+      return QRhiTexture::RGBA8;
+    case GST_VIDEO_FORMAT_YV12:
+      return QRhiTexture::R8;
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+static int
+video_format_to_texel_size (GstVideoFormat format, guint plane)
+{
+  switch (format) {
+    case GST_VIDEO_FORMAT_RGBA:
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_RGB:
+      return 4;
+    case GST_VIDEO_FORMAT_YV12:
+      return 1;
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+QSGTexture *
+GstQSGMaterial::bind(GstQSGMaterialShader *shader, QRhi * rhi, QRhiResourceUpdateBatch *res_updates, guint plane, GstVideoFormat v_format)
+{
+  GstGLContext *qt_context = NULL, *context;
+  GstMemory *mem;
+  GstGLMemory *gl_mem;
+  GstGLSyncMeta *sync_meta;
+  gboolean use_dummy_tex = TRUE;
+  guint tex_id;
+  GstQSGTexture *ret;
+  QRhiTexture *rhi_tex;
+  QSize tex_size;
+
+  if (!this->buffer_)
+    goto out;
+  if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN)
+    goto out;
+
+  qt_context = GST_GL_CONTEXT (g_weak_ref_get (&this->qt_context_ref_));
+  if (!qt_context)
+    goto out;
+
+  GST_DEBUG ("%p attempting to bind with context %" GST_PTR_FORMAT, this, qt_context);
+
+  mem = gst_buffer_peek_memory (this->buffer_, plane);
+  g_assert (gst_is_gl_memory (mem));
+  gl_mem = (GstGLMemory *) mem;
+  context = ((GstGLBaseMemory *)mem)->context;
+
+  /* Texture was successfully bound, so we do not need
+   * to use the dummy texture */
+  use_dummy_tex = FALSE;
+
+  this->buffer_was_bound = true;
+  tex_id = *(guint *) this->v_frame.data[plane];
+
+  tex_size = QSize(gst_gl_memory_get_texture_width(gl_mem), gst_gl_memory_get_texture_height (gl_mem));
+
+  rhi_tex = rhi->newTexture (video_format_to_rhi_format (v_format, plane), tex_size, 1, {});
+  rhi_tex->createFrom({(guint64) tex_id, 0});
+
+  sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_);
+  if (!sync_meta)
+    sync_meta = gst_buffer_add_gl_sync_meta (context, this->sync_buffer_);
+
+  gst_gl_sync_meta_set_sync_point (sync_meta, context);
+
+  gst_gl_sync_meta_wait (sync_meta, qt_context);
+
+  GST_LOG ("%p binding GL texture %u for plane %d", this, tex_id, plane);
+
+out:
+  if (G_UNLIKELY (use_dummy_tex)) {
+    /* Create dummy texture if not already present.
+     * Use the Qt RHI functions instead of the GstGL ones.
+     */
+
+    /* Make this a black 64x64 pixel RGBA texture.
+     * This size and format is supported pretty much everywhere, so these
+     * are a safe pick. (64 pixel sidelength must be supported according
+     * to the GLES2 spec, table 6.18.) */
+    const int tex_sidelength = 64;
+
+    rhi_tex = rhi->newTexture (video_format_to_rhi_format (v_format, plane), QSize(tex_sidelength, tex_sidelength), 1, {});
+    g_assert (rhi_tex->create());
+
+    int ts = video_format_to_texel_size (v_format, plane);
+    QByteArray dummy_data (tex_sidelength * tex_sidelength * ts, 0);
+    char *data = dummy_data.data();
+
+    switch (v_format) {
+      case GST_VIDEO_FORMAT_RGBA:
+      case GST_VIDEO_FORMAT_BGRA:
+      case GST_VIDEO_FORMAT_RGB:
+        for (gsize j = 0; j < tex_sidelength; j++) {
+          for (gsize k = 0; k < tex_sidelength; k++) {
+            data[(j * tex_sidelength + k) * ts + 3] = 0xFF; // opaque
+          }
+        }
+        break;
+      case GST_VIDEO_FORMAT_YV12:
+        if (plane == 1 || plane == 2) {
+          for (gsize j = 0; j < tex_sidelength; j++) {
+            for (gsize k = 0; k < tex_sidelength; k++) {
+              data[(j * tex_sidelength + k) * ts + 0] = 0x7F;
+            }
+          }
+        }
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+
+    QRhiTextureSubresourceUploadDescription sub_desc(dummy_data);
+    QRhiTextureUploadEntry entry(0, 0, sub_desc);
+    QRhiTextureUploadDescription desc(entry);
+    res_updates->uploadTexture(rhi_tex, desc);
+
+    GST_LOG ("%p binding for plane %d fallback dummy Qt texture", this, plane);
+  }
+
+  ret = new GstQSGTexture(rhi_tex);
+  ret->setFiltering(m_filtering);
+
+  gst_clear_object (&qt_context);
+
+  return static_cast<QSGTexture *>(ret);
+}
diff --git a/ext/qt6/gstqsg6material.h b/ext/qt6/gstqsg6material.h
new file mode 100644
index 0000000000000000000000000000000000000000..42a2a87768d5e882c40b948be5b34fea90815d88
--- /dev/null
+++ b/ext/qt6/gstqsg6material.h
@@ -0,0 +1,75 @@
+/*
+ * GStreamer
+ * Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_QSG6_MATERIAL_H__
+#define __GST_QSG6_MATERIAL_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/gl/gl.h>
+
+#include "gstqt6gl.h"
+#include <QtQuick/QSGMaterial>
+#include <QtQuick/QSGMaterialShader>
+#include <QtGui/QOpenGLFunctions>
+#include <QtQuick/QSGTexture>
+
+class QRhi;
+class QRhiResourceUpdateBatch;
+class GstQSGMaterialShader;
+
+class GstQSGMaterial : public QSGMaterial
+{
+protected:
+    GstQSGMaterial();
+    ~GstQSGMaterial();
+public:
+    static GstQSGMaterial *new_for_format (GstVideoFormat format);
+
+    void setCaps (GstCaps * caps);
+    gboolean setBuffer (GstBuffer * buffer);
+    GstBuffer * getBuffer (bool * was_bound);
+    bool compatibleWith(GstVideoInfo *v_info);
+
+    void setFiltering(QSGTexture::Filtering);
+
+    QSGTexture * bind(GstQSGMaterialShader *, QRhi *, QRhiResourceUpdateBatch *, guint binding, GstVideoFormat);
+
+    /* QSGMaterial */
+    QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
+
+    struct {
+        int input_swizzle[4];
+        QMatrix4x4 color_matrix;
+        bool dirty;
+    } uniforms;
+
+private:
+    GstBuffer * buffer_;
+    bool buffer_was_bound;
+    GWeakRef qt_context_ref_;
+    GstBuffer * sync_buffer_;
+    GstMemory * mem_;
+    GstVideoInfo v_info;
+    GstVideoFrame v_frame;
+    QSGTexture::Filtering m_filtering;
+};
+
+#endif /* __GST_QSG6_MATERIAL_H__ */
diff --git a/ext/qt6/gstqt6elements.h b/ext/qt6/gstqt6elements.h
index 7b18455ba911108f8f30fd3e872ee28c028785fa..c1a7955a1adb6317fb13ff0fcd5cec907c9c03ee 100644
--- a/ext/qt6/gstqt6elements.h
+++ b/ext/qt6/gstqt6elements.h
@@ -27,6 +27,9 @@ G_BEGIN_DECLS
 void qt6_element_init (GstPlugin * plugin);
 
 GST_ELEMENT_REGISTER_DECLARE (qml6glsink);
+GST_ELEMENT_REGISTER_DECLARE (qml6glsrc);
+GST_ELEMENT_REGISTER_DECLARE (qml6glmixer);
+GST_ELEMENT_REGISTER_DECLARE (qml6gloverlay);
 
 G_END_DECLS
 
diff --git a/ext/qt6/gstqt6glutility.cc b/ext/qt6/gstqt6glutility.cc
index 11daec37f8d8ca4436d579513d6042acb3e7d89d..6802cf16d287b9035507733c9677a15716a5f981 100644
--- a/ext/qt6/gstqt6glutility.cc
+++ b/ext/qt6/gstqt6glutility.cc
@@ -254,7 +254,6 @@ gst_qml6_get_gl_wrapcontext (GstGLDisplay * display,
     gst_gl_display_filter_gl_api (display, gst_gl_context_get_gl_api (*wrap_glcontext));
     gst_gl_context_activate (*wrap_glcontext, FALSE);
   }
-#if 0
 #if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32)
   g_return_val_if_fail (context != NULL, FALSE);
 
@@ -288,72 +287,100 @@ gst_qml6_get_gl_wrapcontext (GstGLDisplay * display,
       return FALSE;
 
   } G_STMT_END;
-#endif
 #endif
   return TRUE;
 }
-#if 0
-QVariant
+
+QOpenGLContext *
 qt_opengl_native_context_from_gst_gl_context (GstGLContext * context)
 {
-    guintptr handle;
-    GstGLPlatform platform;
+  guintptr handle;
+  GstGLPlatform platform;
+  QOpenGLContext *ret = NULL;
+
+  handle = gst_gl_context_get_gl_context (context);
+  platform = gst_gl_context_get_gl_platform (context);
 
-    handle = gst_gl_context_get_gl_context (context);
-    platform = gst_gl_context_get_gl_platform (context);
+  /* this is required as Qt doesn't allow retrieving the relevant native
+   * interface unless the underlying context has been created */
+  QOpenGLContext *qt_gl_context = new QOpenGLContext();
+  qt_gl_context->create();
 
 #if GST_GL_HAVE_WINDOW_X11 && defined (HAVE_QT_X11)
-    if (platform == GST_GL_PLATFORM_GLX) {
-        GstGLDisplay *display = gst_gl_context_get_display (context);
-        GstGLWindow *window = gst_gl_context_get_window (context);
-        Display *xdisplay = (Display *) gst_gl_display_get_handle (display);
-        Window win = gst_gl_window_get_window_handle (window);
-        gst_object_unref (window);
-        gst_object_unref (display);
-        return QVariant::fromValue(QGLXNativeContext((GLXContext) handle, xdisplay, win));
+  if (!ret && platform == GST_GL_PLATFORM_GLX) {
+    auto glx = qt_gl_context->nativeInterface<QNativeInterface::QGLXContext>();
+    if (!glx) {
+      GST_WARNING ("Retriving GLX context interface from Qt failed");
+    } else {
+      GstGLDisplay *display = gst_gl_context_get_display (context);
+      GstGLWindow *window = gst_gl_context_get_window (context);
+      gst_object_unref (window);
+      gst_object_unref (display);
+      ret = glx->fromNative((GLXContext) handle);
     }
+  }
 #endif
 #if GST_GL_HAVE_PLATFORM_EGL && (defined (HAVE_QT_WAYLAND) || defined (HAVE_QT_EGLFS) || defined (HAVE_QT_ANDROID))
-    if (platform == GST_GL_PLATFORM_EGL) {
-        EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
-        GstGLDisplay *display = gst_gl_context_get_display (context);
-        GstGLDisplayEGL *display_egl = gst_gl_display_egl_from_gl_display (display);
+  if (!ret && platform == GST_GL_PLATFORM_EGL) {
+    auto egl = qt_gl_context->nativeInterface<QNativeInterface::QEGLContext>();
+    if (!egl) {
+      GST_WARNING ("Retriving EGL context interface from Qt failed");
+    } else {
+      EGLDisplay egl_display = EGL_DEFAULT_DISPLAY;
+      GstGLDisplay *display = gst_gl_context_get_display (context);
+      GstGLDisplayEGL *display_egl = gst_gl_display_egl_from_gl_display (display);
 #if GST_GL_HAVE_WINDOW_WAYLAND && defined (HAVE_QT_WAYLAND)
-        if (gst_gl_display_get_handle_type (display) == GST_GL_DISPLAY_TYPE_WAYLAND) {
-#if 1
-            g_warning ("Qt does not support wrapping native OpenGL contexts "
-                "on wayland. See https://bugreports.qt.io/browse/QTBUG-82528");
-            gst_object_unref (display_egl);
-            gst_object_unref (display);
-            return QVariant::fromValue(nullptr);
+      if (gst_gl_display_get_handle_type (display) == GST_GL_DISPLAY_TYPE_WAYLAND) {
+#if 0
+        g_warning ("Qt does not support wrapping native OpenGL contexts "
+            "on wayland. See https://bugreports.qt.io/browse/QTBUG-82528");
+        gst_object_unref (display_egl);
+        gst_object_unref (display);
+        return NULL;
 #else
-            if (display_egl)
-                egl_display = (EGLDisplay) gst_gl_display_get_handle ((GstGLDisplay *) display_egl);
+        if (display_egl)
+          egl_display = (EGLDisplay) gst_gl_display_get_handle ((GstGLDisplay *) display_egl);
 #endif
-        }
+      }
 #endif
-        gst_object_unref (display_egl);
-        gst_object_unref (display);
-        return QVariant::fromValue(QEGLNativeContext((EGLContext) handle, egl_display));
+      gst_object_unref (display_egl);
+      gst_object_unref (display);
+      GST_ERROR ("creating native context from context %p and display %p", (void *) handle, egl_display);
+      ret = egl->fromNative((EGLContext) handle, egl_display);
+      GST_ERROR ("created native context %p", ret);
     }
+  }
 #endif
 #if GST_GL_HAVE_WINDOW_WIN32 && GST_GL_HAVE_PLATFORM_WGL && defined (HAVE_QT_WIN32)
-    if (platform == GST_GL_PLATFORM_WGL) {
-        GstGLWindow *window = gst_gl_context_get_window (context);
-        guintptr hwnd = gst_gl_window_get_window_handle (window);
-        gst_object_unref (window);
-        return QVariant::fromValue(QWGLNativeContext((HGLRC) handle, (HWND) hwnd));
+  if (!ret && platform == GST_GL_PLATFORM_WGL) {
+    auto wgl = qt_gl_context->nativeInterface<QNativeInterface::QWGLContext>();
+    if (!wgl) {
+      GST_WARNING ("Retriving WGL context interface from Qt failed");
+    } else {
+      GstGLWindow *window = gst_gl_context_get_window (context);
+      guintptr hwnd = gst_gl_window_get_window_handle (window);
+      gst_object_unref (window);
+      ret = wgl->fromNative((HGLRC) handle, (HWND) hwnd);
     }
+  }
 #endif
-    {
-      gchar *platform_s = gst_gl_platform_to_string (platform);
-      g_warning ("Unimplemented configuration!  This means either:\n"
-          "1. The qmlgl plugin was built without support for your platform.\n"
-          "2. The necessary code to convert from a GstGLContext to Qt's "
-          "native context type for \'%s\' currently does not exist.",
-          platform_s);
-      g_free (platform_s);
-    }
-    return QVariant::fromValue(nullptr);
+  if (!ret) {
+    gchar *platform_s = gst_gl_platform_to_string (platform);
+    g_warning ("Unimplemented configuration!  This means either:\n"
+        "1. Qt6 wasn't built with support for \'%s\'\n"
+        "2. The qmlgl plugin was built without support for your platform.\n"
+        "3. The necessary code to convert from a GstGLContext to Qt's "
+        "native context type for \'%s\' currently does not exist."
+        "4. Qt failed to wrap an existing native context.",
+        platform_s, platform_s);
+    g_free (platform_s);
+  }
+
+  qt_gl_context->doneCurrent();
+  delete qt_gl_context;
+
+  gst_gl_context_activate (context, FALSE);
+  gst_gl_context_activate (context, TRUE);
+
+  return ret;
 }
-#endif
diff --git a/ext/qt6/gstqt6glutility.h b/ext/qt6/gstqt6glutility.h
index ba436230da8513104126f6d79c8d50b57d8a9258..a3a5561306b68bc1ca1e5f80cf33ebc0494760d4 100644
--- a/ext/qt6/gstqt6glutility.h
+++ b/ext/qt6/gstqt6glutility.h
@@ -26,6 +26,7 @@
 
 #include <QVariant>
 #include <QRunnable>
+#include <QOpenGLContext>
 
 G_BEGIN_DECLS
 
@@ -46,10 +47,6 @@ gboolean       gst_qml6_get_gl_wrapcontext (GstGLDisplay * display,
 
 G_END_DECLS
 
-#if 0
-#if defined(__cplusplus)
-QVariant       qt_opengl_native_context_from_gst_gl_context     (GstGLContext * context);
-#endif
-#endif
+QOpenGLContext *        qt_opengl_native_context_from_gst_gl_context     (GstGLContext * context);
 
 #endif /* __QML6_GL_UTILS_H__ */
diff --git a/ext/qt6/meson.build b/ext/qt6/meson.build
index 150b0b9f222feac336228a8349a777568c52611e..2b1ab1d8711290cb66ee0319157c782a665602e6 100644
--- a/ext/qt6/meson.build
+++ b/ext/qt6/meson.build
@@ -1,19 +1,34 @@
 sources = [
   'gstplugin.cc',
   'gstqt6element.cc',
-  'gstqsg6glnode.cc',
+  'gstqsg6material.cc',
   'gstqt6glutility.cc',
   'gstqml6glsink.cc',
+  'gstqml6glsrc.cc',
+  'gstqml6glmixer.cc',
+  'gstqml6gloverlay.cc',
   'qt6glitem.cc',
+  'qt6glwindow.cc',
+  'qt6glrenderer.cc',
 ]
 
 moc_headers = [
   'qt6glitem.h',
-  'gstqsg6glnode.h',
+  'qt6glwindow.h',
+  'qt6glrenderer.h',
+]
+
+shader_sources = [
+  'vertex.vert',
+  'RGBA.frag',
+  'YUV_TRIPLANAR.frag',
 ]
 
 qt6qml_dep = dependency('', required: false)
 qt6_option = get_option('qt6')
+qt6_egl = get_option('qt-egl')
+qt6_wayland = get_option('qt-wayland')
+qt6_x11 = get_option('qt-x11')
 qt6_method = get_option('qt-method')
 
 if qt6_option.disabled()
@@ -32,7 +47,7 @@ if not add_languages('cpp', native: false, required: qt6_option)
 endif
 
 qt6_mod = import('qt6')
-if not qt6_mod.has_tools()
+if not qt6_mod.has_tools(method: qt6_method)
   if qt6_option.enabled()
     error('qt6 qmlglsink plugin is enabled, but qt specific tools were not found')
   endif
@@ -40,11 +55,17 @@ if not qt6_mod.has_tools()
 endif
 
 qt6qml_dep = dependency('qt6', modules : ['Core', 'Gui', 'Qml', 'Quick'],
-                        method: qt6_method, required: qt6_option, static: host_system == 'ios')
+                        method: qt6_method, required: qt6_option, static: host_system == 'ios', private_headers: true)
 if not qt6qml_dep.found()
   subdir_done()
 endif
 
+qt6_bindir = qt6qml_dep.get_variable('bindir', configtool: 'QT_HOST_BINS')
+qsb = find_program('qsb-qt6', 'qsb', dirs: [qt6_bindir], required: qt6_option)
+if not qsb.found()
+  subdir_done()
+endif
+
 optional_deps = []
 qt_defines = []
 have_qpa_include = false
@@ -53,86 +74,113 @@ have_qt_windowing = false
 # Look for the QPA platform native interface header
 qpa_header_path = join_paths(qt6qml_dep.version(), 'QtGui')
 qpa_header = join_paths(qpa_header_path, 'qpa/qplatformnativeinterface.h')
-if cxx.has_header(qpa_header, dependencies : qt6qml_dep)
+need_qpa_include = qt6_option.enabled() and (host_system == 'android' or qt6_wayland.enabled())
+if cxx.has_header(qpa_header, dependencies : qt6qml_dep, required: need_qpa_include)
   qt_defines += '-DHAVE_QT_QPA_HEADER'
   qt_defines += '-DQT_QPA_HEADER=' + '<@0@>'.format(qpa_header)
   have_qpa_include = true
   message('Found QtGui QPA header in ' + qpa_header_path)
 endif
 
-# Try to come up with all the platform/winsys combinations that will work
+## Try to come up with all the platform/winsys combinations that will work
 
-if gst_gl_have_window_x11 and gst_gl_have_platform_glx
-  # FIXME: automagic
+# X11 windowing
+qt6_x11 = qt6_x11 \
+    .require(gstglx11_dep.found(), error_message: 'gstreamer-gl-x11-1.0 is required') \
+    .require(gst_gl_have_window_x11, error_message: 'x11 windowing support in gstreamer-gl is required') \
+    .require(gst_gl_have_platform_glx, error_message: 'glx platform support in gstreamer-gl is required')
+if qt6_x11.allowed()
   qt_defines += ['-DHAVE_QT_X11']
   have_qt_windowing = true
 endif
 
-if gst_gl_have_platform_egl
-  # Embedded linux (e.g. i.MX6) with or without windowing support
+# Wayland windowing
+qt6_wayland = qt6_wayland \
+    .require(gstglwayland_dep.found(), error_message: 'gstreamer-gl-wayland-1.0 is required') \
+    .require(gst_gl_have_window_wayland, error_message: 'wayland windowing support in gstreamer-gl is required') \
+    .require(gst_gl_have_platform_egl, error_message: 'egl platform support in gstreamer-gl is required') \
+    .require(have_qpa_include, error_message: 'QPA platform native interface header is required')
+if qt6_wayland.allowed()
+  qt6waylandextras = dependency('qt6', modules : ['WaylandClient'], method: qt6_method, required: qt6_wayland)
+  if qt6waylandextras.found()
+    optional_deps += [qt6waylandextras, gstglwayland_dep]
+    qt_defines += ['-DHAVE_QT_WAYLAND']
+    have_qt_windowing = true
+  endif
+endif
+
+# EGL windowing for Embedded linux (e.g. i.MX6) with or without windowing
+# support
+qt6_egl = qt6_egl \
+    .require(host_system == 'linux') \
+    .require(gstglegl_dep.found(), error_message: 'gstreamer-gl-egl-1.0 is required') \
+    .require(gst_gl_have_platform_egl, error_message: 'egl platform support in gstreamer-gl is required')
+if qt6_egl.allowed()
   qt_defines += ['-DHAVE_QT_EGLFS']
   optional_deps += gstglegl_dep
   have_qt_windowing = true
-  if have_qpa_include
-    # Wayland windowing
-    if gst_gl_have_window_wayland
-      # FIXME: automagic
-      qt6waylandextras = dependency('qt6', modules : ['WaylandClient'], method: qt6_method, required : false)
-      if qt6waylandextras.found()
-        optional_deps += [qt6waylandextras, gstglwayland_dep]
-        qt_defines += ['-DHAVE_QT_WAYLAND']
-        have_qt_windowing = true
-      endif
+endif
+
+# TODO: Android windowing
+
+# Win32 windowing
+if host_system == 'windows'
+  qt6_win32 = qt6_option \
+      .require(gst_gl_have_window_win32, error_message: 'win32 windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_wgl, error_message: 'wgl platform support in gstreamer-gl is required')
+  if qt6_win32.allowed()
+    # for wglMakeCurrent()
+    opengl32_dep = cc.find_library('opengl32', required : qt6_win32)
+    if opengl32_dep.found()
+      qt_defines += ['-DHAVE_QT_WIN32']
+      optional_deps += opengl32_dep
+      have_qt_windowing = true
     endif
-    # Android windowing
-#    if gst_gl_have_window_android
-      # FIXME: automagic
-#      qt5androidextras = dependency('qt5', modules : ['AndroidExtras'], method: qt6_method, required : false)
-      # for gl functions in QtGui/qopenglfunctions.h
-      # FIXME: automagic
-#      glesv2_dep = cc.find_library('GLESv2', required : false)
-#      if glesv2_dep.found() and qt5androidextras.found()
-#        optional_deps += [qt5androidextras, glesv2_dep]
-#        qt_defines += ['-DHAVE_QT_ANDROID']
-#        have_qt_windowing = true
-        # Needed for C++11 support in Cerbero. People building with Android
-        # in some other way need to add the necessary bits themselves.
-#        optional_deps += dependency('gnustl', required : false)
-#      endif
-#    endif
   endif
 endif
 
-#if gst_gl_have_platform_wgl and gst_gl_have_window_win32
-  # for wglMakeCurrent()
-  # FIXME: automagic
-#  opengl32_dep = cc.find_library('opengl32', required : false)
-#  if opengl32_dep.found()
-#    qt_defines += ['-DHAVE_QT_WIN32']
-#    optional_deps += opengl32_dep
-#    have_qt_windowing = true
-#  endif
-#endif
-
-if gst_gl_have_window_cocoa and gst_gl_have_platform_cgl
-  # FIXME: automagic
-  if host_machine.system() == 'darwin'
+# macOS windowing
+if host_system == 'darwin'
+  qt6_macos = qt6_option \
+      .require(gst_gl_have_window_cocoa, error_message: 'cocoa windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_cgl, error_message: 'cgl platform support in gstreamer-gl is required')
+  if qt6_macos.allowed()
     qt_defines += ['-DHAVE_QT_MAC']
     have_qt_windowing = true
   endif
 endif
 
-if gst_gl_have_window_eagl and gst_gl_have_platform_eagl
-  if host_machine.system() == 'ios'
+# iOS windowing
+if host_system == 'ios'
+  qt6_ios = qt6_option \
+      .require(gst_gl_have_window_eagl, error_message: 'eagl windowing support in gstreamer-gl is required') \
+      .require(gst_gl_have_platform_eagl, error_message: 'eagl platform support in gstreamer-gl is required')
+  if qt6_ios.allowed()
     qt_defines += ['-DHAVE_QT_IOS']
     have_qt_windowing = true
   endif
 endif
 
-if have_qt_windowing
+if qt6_option.require(have_qt_windowing, error_message: 'No windowing, enable one of the qt-* windowing options').allowed()
   # Build it!
-  moc_files = qt6_mod.preprocess(moc_headers : moc_headers)
-  gstqml6gl = library('gstqml6', sources, moc_files,
+  moc_files = qt6_mod.preprocess(moc_headers : moc_headers, method: qt6_method)
+  # TODO: dist backup qsb shaders?
+  shaders = []
+  foreach shader: shader_sources
+    qsb_shader = shader + '.qsb'
+    dist_shader = shader + '-dist.qsb'
+
+    compiled_shader = custom_target(qsb_shader,
+      input: shader,
+      output: qsb_shader,
+      command: [qsb, '--glsl=100 es,120,330', '--batchable', '--output', '@OUTPUT@', '@INPUT@']
+    )
+    shaders += [compiled_shader]
+  endforeach
+  resource_file = configure_file(input: 'resources.qrc', output: 'resources.qrc', copy: true)
+  qresources = qt6_mod.compile_resources(sources: resource_file, method: qt6_method)
+
+  gstqml6gl = library('gstqml6', sources, moc_files, qresources,
     cpp_args : gst_plugins_good_args + qt_defines,
     link_args : noseh_link_args,
     include_directories: [configinc, libsinc],
diff --git a/ext/qt6/qt6glitem.cc b/ext/qt6/qt6glitem.cc
index a88d212fab9b30e0f3db4a2a76152925cfb402ed..2ddb0b94164637293f9b6fac7e780c91b906311e 100644
--- a/ext/qt6/qt6glitem.cc
+++ b/ext/qt6/qt6glitem.cc
@@ -26,14 +26,13 @@
 
 #include <gst/video/video.h>
 #include "qt6glitem.h"
-#include "gstqsg6glnode.h"
 #include "gstqt6glutility.h"
+#include "gstqsg6material.h"
 
 #include <QtCore/QMutexLocker>
 #include <QtCore/QPointer>
 #include <QtGui/QGuiApplication>
 #include <QtQuick/QQuickWindow>
-#include <QtQuick/QSGSimpleTextureNode>
 
 /**
  * SECTION:Qt6GLVideoItem
@@ -74,6 +73,7 @@ struct _Qt6GLVideoItemPrivate
   GstCaps *caps;
   GstVideoInfo new_v_info;
   GstVideoInfo v_info;
+  GstVideoRectangle v_rect;
 
   gboolean initted;
   GstGLDisplay *display;
@@ -88,8 +88,6 @@ struct _Qt6GLVideoItemPrivate
    * FIXME: Ideally we would use fences for this but there seems to be no
    * way to reliably "try wait" on a fence */
   GQueue potentially_unbound_buffers;
-
-  GstQSG6OpenGLNode *m_node;
 };
 
 Qt6GLVideoItem::Qt6GLVideoItem()
@@ -276,30 +274,60 @@ Qt6GLVideoItem::updatePaintNode(QSGNode * oldNode,
     UpdatePaintNodeData * updatePaintNodeData)
 {
   GstBuffer *old_buffer;
+  GstQSGMaterial *tex = nullptr;
+  QSGGeometry *geometry = nullptr;
+  bool was_bound = false;
 
   if (!this->priv->initted)
     return oldNode;
 
-  GstQSG6OpenGLNode *texNode = static_cast<GstQSG6OpenGLNode *> (oldNode);
+  QSGGeometryNode *texNode = static_cast<QSGGeometryNode *> (oldNode);
   GstVideoRectangle src, dst, result;
 
   g_mutex_lock (&this->priv->lock);
 
   GST_TRACE ("%p updatePaintNode", this);
 
+  if (!this->priv->caps) {
+    GST_LOG ("%p no caps yet", this);
+    g_mutex_unlock (&this->priv->lock);
+    return NULL;
+  }
+
   if (gst_gl_context_get_current() == NULL)
     gst_gl_context_activate (this->priv->other_context, TRUE);
 
+  if (texNode) {
+    tex = static_cast<GstQSGMaterial *>(texNode->material());
+    if (tex && !tex->compatibleWith(&this->priv->v_info)) {
+      delete texNode;
+      texNode = nullptr;
+    }
+  }
+
   if (!texNode) {
-    texNode = new GstQSG6OpenGLNode (this);
-    this->priv->m_node = texNode;
+    bool is_smooth = this->smooth ();
+    texNode = new QSGGeometryNode();
+    geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+    texNode->setGeometry(geometry);
+    texNode->setFlag(QSGGeometryNode::OwnsGeometry);
+    tex = GstQSGMaterial::new_for_format(GST_VIDEO_INFO_FORMAT (&this->priv->v_info));
+    tex->setFiltering(is_smooth ? QSGTexture::Filtering::Linear :
+        QSGTexture::Filtering::Nearest);
+    texNode->setMaterial(tex);
+    texNode->setFlag(QSGGeometryNode::OwnsMaterial);
   }
 
-  if ((old_buffer = texNode->getBuffer())) {
+  if ((old_buffer = tex->getBuffer(&was_bound))) {
     if (old_buffer == this->priv->buffer) {
       /* same buffer */
       gst_buffer_unref (old_buffer);
+    } else if (!was_bound) {
+      GST_TRACE ("old buffer %p was not bound yet, unreffing", old_buffer);
+      gst_buffer_unref (old_buffer);
     } else {
+      texNode->markDirty(QSGNode::DirtyMaterial);
+
       GstBuffer *tmp_buffer;
 
       GST_TRACE ("old buffer %p was bound, queueing up for later", old_buffer);
@@ -323,8 +351,8 @@ Qt6GLVideoItem::updatePaintNode(QSGNode * oldNode,
     old_buffer = NULL;
   }
 
-  texNode->setCaps (this->priv->caps);
-  texNode->setBuffer (this->priv->buffer);
+  tex->setCaps (this->priv->caps);
+  tex->setBuffer (this->priv->buffer);
 
   if (this->priv->force_aspect_ratio && this->priv->caps) {
     src.w = this->priv->display_width;
@@ -343,7 +371,15 @@ Qt6GLVideoItem::updatePaintNode(QSGNode * oldNode,
     result.h = boundingRect().height();
   }
 
-  texNode->setRect (QRectF (result.x, result.y, result.w, result.h));
+  geometry = texNode->geometry();
+  QRectF rect(result.x, result.y, result.w, result.h);
+  QRectF sourceRect(0, 0, 1, 1);
+  QSGGeometry::updateTexturedRectGeometry(geometry, rect, sourceRect);
+  if(priv->v_rect.x != result.x || priv->v_rect.y != result.y ||
+     priv->v_rect.w != result.w || priv->v_rect.h != result.h) {
+    texNode->markDirty(QSGNode::DirtyGeometry);
+    priv->v_rect = result;
+  }
 
   g_mutex_unlock (&this->priv->lock);
 
@@ -710,7 +746,6 @@ Qt6GLVideoItem::onSceneGraphInitialized ()
 void
 Qt6GLVideoItem::onSceneGraphInvalidated ()
 {
-  this->priv->m_node = nullptr;
   GST_FIXME ("%p scene graph invalidated", this);
 }
 
@@ -787,13 +822,11 @@ Qt6GLVideoItem::handleWindowChanged (QQuickWindow * win)
     this->priv->qt_context = NULL;
     this->priv->initted = FALSE;
   }
-  this->priv->m_node = nullptr;
 }
 
 void
 Qt6GLVideoItem::releaseResources()
 {
-  this->priv->m_node = nullptr;
 }
 
 gboolean
diff --git a/ext/qt6/qt6glrenderer.cc b/ext/qt6/qt6glrenderer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..20b803cdb7c73f39ec075d208dde37b03200aa26
--- /dev/null
+++ b/ext/qt6/qt6glrenderer.cc
@@ -0,0 +1,766 @@
+#include <QObject>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QWindow>
+#include <QQuickRenderControl>
+#include <QQuickWindow>
+#include <QQuickGraphicsDevice>
+#include <QQuickItem>
+#include <QQuickRenderTarget>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QAnimationDriver>
+#include <QCoreApplication>
+#include <QEventLoop>
+
+#include <gst/gl/gl.h>
+#include "gstqt6gl.h"
+
+#include "qt6glrenderer.h"
+#include "gstqt6glutility.h"
+
+#define GST_CAT_DEFAULT gst_qt6_gl_renderer_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+static void
+init_debug (void)
+{
+  static gsize _debug;
+
+  if (g_once_init_enter (&_debug)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qt6glrenderer", 0,
+        "Qt6 OpenGL Renderer");
+    g_once_init_leave (&_debug, 1);
+  }
+}
+
+/* Needs to be based on QWindow otherwise (at least) windows and nvidia
+ * proprietary on linux does not work
+ * We also need to override the size handling to get the correct output size
+ */
+class GstQt6BackingSurface : public QWindow
+{
+public:
+    GstQt6BackingSurface();
+    ~GstQt6BackingSurface();
+
+    void setSize (int width, int height);
+    QSize size() const override;
+
+private:
+    QSize m_size;
+};
+
+GstQt6BackingSurface::GstQt6BackingSurface()
+    : m_size(QSize())
+{
+    /* we do OpenGL things so need an OpenGL surface */
+    setSurfaceType(QSurface::OpenGLSurface);
+}
+
+GstQt6BackingSurface::~GstQt6BackingSurface()
+{
+}
+
+QSize GstQt6BackingSurface::size () const
+{
+    return m_size;
+}
+
+void GstQt6BackingSurface::setSize (int width, int height)
+{
+    m_size = QSize (width, height);
+}
+
+class GstQt6AnimationDriver : public QAnimationDriver
+{
+public:
+    GstQt6AnimationDriver();
+
+    void setNextTime(qint64 ms);
+    void advance() override;
+    qint64 elapsed() const override;
+private:
+    qint64 m_elapsed;
+    qint64 m_next;
+};
+
+GstQt6AnimationDriver::GstQt6AnimationDriver()
+    : m_elapsed(0),
+      m_next(0)
+{
+}
+
+void GstQt6AnimationDriver::advance()
+{
+    m_elapsed = m_next;
+    advanceAnimation();
+}
+
+qint64 GstQt6AnimationDriver::elapsed() const
+{
+    return m_elapsed;
+}
+
+void GstQt6AnimationDriver::setNextTime(qint64 ms)
+{
+    m_next = ms;
+}
+
+typedef enum
+{
+  STATE_ERROR = -1,
+  STATE_NEW = 0,
+  STATE_WAITING_FOR_WINDOW,
+  STATE_WINDOW_CREATED,
+  STATE_READY,
+} SharedRenderDataState;
+
+struct SharedRenderData
+{
+  int refcount;
+  SharedRenderDataState state;
+  GMutex lock;
+  GCond cond;
+  GstQt6AnimationDriver *m_animationDriver;
+  QOpenGLContext *m_context;
+  GstQt6BackingSurface *m_surface;
+  QThread *m_renderThread;
+};
+
+static struct SharedRenderData *
+shared_render_data_new (void)
+{
+  struct SharedRenderData *ret = g_new0 (struct SharedRenderData, 1);
+
+  g_atomic_int_set (&ret->refcount, 1);
+  g_mutex_init (&ret->lock);
+
+  return ret;
+}
+
+static void
+shared_render_data_free (struct SharedRenderData * data)
+{
+  GST_DEBUG ("%p freeing shared render data", data);
+
+  g_mutex_clear (&data->lock);
+
+  if (data->m_animationDriver) {
+    data->m_animationDriver->uninstall();
+    delete data->m_animationDriver;
+  }
+  data->m_animationDriver = nullptr;
+  if (data->m_context) {
+    if (QOpenGLContext::currentContext() == data->m_context)
+      data->m_context->doneCurrent();
+    delete data->m_context;
+  }
+  data->m_context = nullptr;
+  if (data->m_surface)
+    data->m_surface->deleteLater();
+  data->m_surface = nullptr;
+}
+
+static struct SharedRenderData *
+shared_render_data_ref (struct SharedRenderData * data)
+{
+  GST_TRACE ("%p reffing shared render data", data);
+  g_atomic_int_inc (&data->refcount);
+  return data;
+}
+
+static void
+shared_render_data_unref (struct SharedRenderData * data)
+{
+  GST_TRACE ("%p unreffing shared render data", data);
+  if (g_atomic_int_dec_and_test (&data->refcount))
+    shared_render_data_free (data);
+}
+
+void
+GstQt6QuickRenderer::deactivateContext ()
+{
+}
+
+void
+GstQt6QuickRenderer::activateContext ()
+{
+}
+
+struct FBOUserData
+{
+  GstGLContext * context;
+  QOpenGLFramebufferObject * fbo;
+};
+
+GstQt6QuickRenderer::GstQt6QuickRenderer()
+    : gl_context(NULL),
+      m_quickWindow(nullptr),
+      m_renderControl(nullptr),
+      m_qmlEngine(nullptr),
+      m_qmlComponent(nullptr),
+      m_rootItem(nullptr),
+      gl_allocator(NULL),
+      gl_params(NULL),
+      gl_mem(NULL),
+      m_sharedRenderData(NULL)
+{
+  init_debug ();
+}
+
+static gpointer
+dup_shared_render_data (gpointer data, gpointer user_data)
+{
+  struct SharedRenderData *render_data = (struct SharedRenderData *) data;
+
+  if (render_data)
+    return shared_render_data_ref (render_data);
+
+  return NULL;
+}
+
+class CreateSurfaceEvent : public QEvent
+{
+public:
+  CreateSurfaceEvent (CreateSurfaceWorker * worker)
+      : QEvent(CreateSurfaceEvent::type())
+  {
+    m_worker = worker;
+  }
+
+  ~CreateSurfaceEvent()
+  {
+    GST_TRACE ("%p destroying create surface event", this);
+    delete m_worker;
+  }
+
+  static QEvent::Type type()
+  {
+    if (customEventType == QEvent::None) {
+      int generatedType = QEvent::registerEventType();
+      customEventType = static_cast<QEvent::Type>(generatedType);
+    }
+    return customEventType;
+  }
+
+private:
+  static QEvent::Type customEventType;
+  CreateSurfaceWorker *m_worker;
+};
+
+QEvent::Type CreateSurfaceEvent::customEventType = QEvent::None;
+
+
+CreateSurfaceWorker::CreateSurfaceWorker (struct SharedRenderData * rdata)
+{
+  m_sharedRenderData = shared_render_data_ref (rdata);
+}
+
+CreateSurfaceWorker::~CreateSurfaceWorker ()
+{
+  shared_render_data_unref (m_sharedRenderData);
+}
+
+bool CreateSurfaceWorker::event(QEvent * ev)
+{
+    if (ev->type() == CreateSurfaceEvent::type()) {
+        GST_TRACE ("%p creating surface", m_sharedRenderData);
+        /* create the window surface in the main thread */
+        g_mutex_lock (&m_sharedRenderData->lock);
+        m_sharedRenderData->m_surface = new GstQt6BackingSurface;
+        m_sharedRenderData->m_surface->create();
+        GST_TRACE ("%p created surface %p", m_sharedRenderData,
+            m_sharedRenderData->m_surface);
+        g_cond_broadcast (&m_sharedRenderData->cond);
+        g_mutex_unlock (&m_sharedRenderData->lock);
+    }
+
+    return QObject::event(ev);
+}
+
+bool GstQt6QuickRenderer::init (GstGLContext * context, GError ** error)
+{
+    g_return_val_if_fail (GST_IS_GL_CONTEXT (context), false);
+    g_return_val_if_fail (gst_gl_context_get_current () == context, false);
+
+    QOpenGLContext *qt_native_context = qt_opengl_native_context_from_gst_gl_context (context);
+
+    if (!qt_native_context) {
+        g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+            "Could not convert from the provided GstGLContext to a Qt "
+            "native context");
+        return false;
+    }
+
+    struct SharedRenderData *render_data = NULL, *old_render_data;
+    do {
+        if (render_data)
+            shared_render_data_unref (render_data);
+
+        old_render_data = render_data = (struct SharedRenderData *)
+                g_object_dup_data (G_OBJECT (context),
+                "qt.gl.render.shared.data", dup_shared_render_data, NULL);
+        if (!render_data)
+            render_data = shared_render_data_new ();
+    } while (old_render_data != render_data
+            && !g_object_replace_data (G_OBJECT (context),
+                "qt.gl.render.shared.data", old_render_data, render_data,
+                NULL, NULL));
+    m_sharedRenderData = render_data;
+    GST_TRACE ("%p retrieved shared render data %p", this, m_sharedRenderData);
+
+    g_mutex_lock (&m_sharedRenderData->lock);
+    if (m_sharedRenderData->state == STATE_ERROR) {
+        g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+            "In an error state from a previous attempt");
+        g_mutex_unlock (&m_sharedRenderData->lock);
+        return false;
+    }
+
+    if (m_sharedRenderData->state != STATE_READY) {
+        /* this state handling and locking is so that two qtglrenderer's will
+         * not attempt to create an OpenGL context without freeing the previous
+         * OpenGL context and cause a leak.  It also only allows one
+         * CreateSurfaceEvent() to be posted to the main thread
+         * (QCoreApplication::instance()->thread()) while still allowing
+         * multiple waiters to wait for the window to be created */
+        if (m_sharedRenderData->state == STATE_NEW) {
+            QCoreApplication *app = QCoreApplication::instance ();
+
+            if (!app) {
+                g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+                    "Could not retrieve QCoreApplication instance");
+                m_sharedRenderData->state = STATE_ERROR;
+                g_mutex_unlock (&m_sharedRenderData->lock);
+                return false;
+            }
+
+            m_sharedRenderData->m_renderThread = QThread::currentThread();
+            m_sharedRenderData->m_context = qt_native_context;
+            GST_TRACE ("%p new QOpenGLContext %p", this, m_sharedRenderData->m_context);
+
+            CreateSurfaceWorker *w = new CreateSurfaceWorker (m_sharedRenderData);
+            GST_TRACE ("%p posting create surface event to main thread with "
+                "worker %p", this, w);
+            w->moveToThread (app->thread());
+            app->postEvent (w, new CreateSurfaceEvent (w));
+            m_sharedRenderData->state = STATE_WAITING_FOR_WINDOW;
+        }
+
+        if (m_sharedRenderData->state == STATE_WAITING_FOR_WINDOW) {
+            gint64 end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+            while (!m_sharedRenderData->m_surface) {
+                /* XXX: This might deadlock with the main thread if the
+                 * QCoreApplication is not running and will not be able to
+                 * execute.  We only wait for 5 seconds until a better
+                 * approach can be found here */
+                if (!g_cond_wait_until (&m_sharedRenderData->cond,
+                        &m_sharedRenderData->lock, end_time)) {
+                    g_set_error (error, GST_RESOURCE_ERROR,
+                        GST_RESOURCE_ERROR_NOT_FOUND,
+                        "Could not create Qt window within 5 seconds");
+                    m_sharedRenderData->state = STATE_ERROR;
+                    g_mutex_unlock (&m_sharedRenderData->lock);
+                    return false;
+                }
+            }
+
+            GST_TRACE ("%p surface successfully created", this);
+            m_sharedRenderData->state = STATE_WINDOW_CREATED;
+        }
+
+        if (m_sharedRenderData->state == STATE_WINDOW_CREATED) {
+            /* Qt does some things that may require the OpenGL context current
+             * in ->create() so that it has the necessry information to create
+             * the QOpenGLContext from the native handle. This may fail if the
+             * OpenGL context is already current in another thread so we need
+             * to deactivate the context from GStreamer's thread before asking
+             * Qt to create the QOpenGLContext with ->create().
+             */
+            gst_gl_context_activate (context, FALSE);
+            //m_sharedRenderData->m_context->create();
+            //m_sharedRenderData->m_context->doneCurrent();
+
+            if (!m_sharedRenderData->m_context->makeCurrent(m_sharedRenderData->m_surface)) {
+                g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+                    "Could not make Qt OpenGL context current");
+                /* try to keep the same OpenGL context state */
+                gst_gl_context_activate (context, TRUE);
+                m_sharedRenderData->state = STATE_ERROR;
+                g_mutex_unlock (&m_sharedRenderData->lock);
+                return false;
+            }
+
+            if (!gst_gl_context_activate (context, TRUE)) {
+                g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
+                    "Could not make GStreamer OpenGL context current again");
+                m_sharedRenderData->state = STATE_ERROR;
+                g_mutex_unlock (&m_sharedRenderData->lock);
+                return false;
+            }
+            m_sharedRenderData->state = STATE_READY;
+        }
+    }
+
+    m_renderControl = new QQuickRenderControl();
+    /* Create a QQuickWindow that is associated with our render control. Note that this
+     * window never gets created or shown, meaning that it will never get an underlying
+     * native (platform) window.
+     */
+    m_quickWindow = new QQuickWindow(m_renderControl);
+    m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(qt_native_context));
+    /* after QQuickWindow creation as QQuickRenderControl requires it */
+    m_renderControl->prepareThread (m_sharedRenderData->m_renderThread);
+    g_mutex_unlock (&m_sharedRenderData->lock);
+
+    /* Create a QML engine. */
+    m_qmlEngine = new QQmlEngine;
+    if (!m_qmlEngine->incubationController())
+        m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
+
+    /* TODO: use buffer pool */
+    gl_context = static_cast<GstGLContext*>(gst_object_ref (context));
+    gl_allocator = (GstGLBaseMemoryAllocator *) gst_gl_memory_allocator_get_default (gl_context);
+    gl_params = (GstGLAllocationParams *)
+        gst_gl_video_allocation_params_new (gl_context,
+            NULL, &this->v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA8);
+
+    /* This is a gross hack relying on the internals of Qt and GStreamer
+     * however it's the only way to remove this warning on shutdown of all
+     * resources.
+     *
+     * GLib-CRITICAL **: 17:35:24.988: g_main_context_pop_thread_default: assertion 'g_queue_peek_head (stack) == context' failed
+     *
+     * The reason is that libgstgl has a GMainContext that it pushes as the
+     * thread default context.  Then later, Qt pushes a thread default main
+     * context.  The detruction order of the GMainContext's is reversed as
+     * GStreamer will explicitly pop the thread default main context however
+     * Qt pops when the thread is about to be destroyed.  GMainContext is
+     * unhappy with the ordering of the pops.
+     */
+    GMainContext *gst_main_context = g_main_context_ref_thread_default ();
+
+    /* make Qt allocate and push a thread-default GMainContext if it is
+     * going to */
+    QEventLoop loop;
+    if (loop.processEvents())
+        GST_LOG ("pending QEvents processed");
+
+    GMainContext *qt_main_context = g_main_context_ref_thread_default ();
+
+    if (qt_main_context == gst_main_context) {
+        g_main_context_unref (qt_main_context);
+        g_main_context_unref (gst_main_context);
+    } else {
+        /* We flip the order of the GMainContext's so that the destruction
+         * order can be preserved. */
+        g_main_context_pop_thread_default (qt_main_context);
+        g_main_context_pop_thread_default (gst_main_context);
+        g_main_context_push_thread_default (qt_main_context);
+        g_main_context_push_thread_default (gst_main_context);
+        g_main_context_unref (qt_main_context);
+        g_main_context_unref (gst_main_context);
+    }
+
+    return true;
+}
+
+GstQt6QuickRenderer::~GstQt6QuickRenderer()
+{
+    gst_gl_allocation_params_free (gl_params);
+    gst_clear_object (&gl_allocator);
+}
+
+void GstQt6QuickRenderer::stopGL ()
+{
+    QOpenGLContext *current_qt_context = QOpenGLContext::currentContext();
+
+    GST_DEBUG ("%p stop QOpenGLContext current: %p stored: %p", this,
+        current_qt_context, m_sharedRenderData->m_context);
+    /* Invalidating the renderer will cause Qt6 to clear the current qt-tracked OpenGL context.
+     * We however may be using the QOpenGLContext for multiple qml6gloverlay
+     * elements so need to recurrent it */
+    if (current_qt_context)
+      g_assert (current_qt_context == m_sharedRenderData->m_context);
+    else
+      m_sharedRenderData->m_context->makeCurrent(m_sharedRenderData->m_surface);
+
+    if (m_renderControl)
+        m_renderControl->invalidate();
+
+    GST_ERROR ("%p %p", this, QOpenGLContext::currentContext());
+
+    QEventLoop loop;
+    if (loop.processEvents())
+        GST_LOG ("%p pending QEvents processed", this);
+}
+
+void GstQt6QuickRenderer::stopAfterGL ()
+{
+    GST_DEBUG ("%p stop QOpenGLContext curent: %p stored: %p", this,
+        QOpenGLContext::currentContext(), m_sharedRenderData->m_context);
+    g_assert (QOpenGLContext::currentContext() == nullptr);
+
+    if (!m_sharedRenderData->m_context->makeCurrent(m_sharedRenderData->m_surface))
+      g_warn_if_reached();
+
+    if (m_sharedRenderData)
+        shared_render_data_unref (m_sharedRenderData);
+    m_sharedRenderData = NULL;
+
+    /* XXX: reset the OpenGL context and drawable as Qt may have clobbered it.
+     * Fixes any attempt to access OpenGL after shutting down qmlgloverlay. */
+    gst_gl_context_activate (gl_context, FALSE);
+    gst_gl_context_activate (gl_context, TRUE);
+}
+
+void GstQt6QuickRenderer::cleanup()
+{
+    if (gl_context)
+        gst_gl_context_thread_add (gl_context,
+            (GstGLContextThreadFunc) GstQt6QuickRenderer::stop_c, this);
+
+    /* Delete the render control first since it will free the scenegraph resources.
+     * Destroy the QQuickWindow only afterwards. */
+    if (m_renderControl)
+        delete m_renderControl;
+    m_renderControl = nullptr;
+
+    if (m_qmlComponent)
+        delete m_qmlComponent;
+    m_qmlComponent = nullptr;
+    if (m_quickWindow)
+        delete m_quickWindow;
+    m_quickWindow = nullptr;
+    if (m_qmlEngine)
+        delete m_qmlEngine;
+    m_qmlEngine = nullptr;
+    if (m_rootItem)
+        delete m_rootItem;
+    m_rootItem = nullptr;
+
+    if (gl_context)
+        gst_gl_context_thread_add (gl_context,
+            (GstGLContextThreadFunc) GstQt6QuickRenderer::stop_after_c, this);
+
+    gst_clear_object (&gl_context);
+}
+
+static QSize
+gl_params_get_QSize(GstGLAllocationParams * gl_params)
+{
+    GstGLVideoAllocationParams * gl_vid_params = (GstGLVideoAllocationParams *) gl_params;
+
+    if (!gl_vid_params)
+        return QSize (0, 0);
+
+    return QSize(GST_VIDEO_INFO_WIDTH (gl_vid_params->v_info), GST_VIDEO_INFO_HEIGHT(gl_vid_params->v_info));
+}
+
+void
+GstQt6QuickRenderer::renderGstGL ()
+{
+//    const GstGLFuncs *gl = gl_context->gl_vtable;
+
+    GST_TRACE ("%p current QOpenGLContext %p", this,
+        QOpenGLContext::currentContext());
+
+    m_sharedRenderData->m_animationDriver->advance();
+
+    QEventLoop loop;
+    if (loop.processEvents())
+        GST_LOG ("pending QEvents processed");
+
+    loop.exit();
+
+   if (gl_params && gl_params_get_QSize(gl_params) != m_sharedRenderData->m_surface->size()) {
+        gst_gl_allocation_params_free(gl_params);
+        gl_params = NULL;
+    }
+
+    if (!gl_params)
+        gl_params = (GstGLAllocationParams *)
+            gst_gl_video_allocation_params_new (gl_context,
+                NULL, &this->v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA8);
+    
+
+    gl_mem = (GstGLMemory *) gst_gl_base_memory_alloc (gl_allocator, gl_params);
+    m_quickWindow->setRenderTarget(QQuickRenderTarget::fromOpenGLTexture(gst_gl_memory_get_texture_id (gl_mem), gl_params_get_QSize(gl_params)));
+
+    m_renderControl->beginFrame();
+    if (m_renderControl->sync())
+        GST_LOG ("sync successful");
+
+    m_renderControl->render();
+    m_renderControl->endFrame();
+
+    /* Qt doesn't seem to reset this, breaking glimagesink */
+//    if (gl->DrawBuffer)
+//      gl->DrawBuffer (GL_BACK);
+}
+
+GstGLMemory *GstQt6QuickRenderer::generateOutput(GstClockTime input_ns)
+{
+    m_sharedRenderData->m_animationDriver->setNextTime(input_ns / GST_MSECOND);
+
+    /* run an event loop to update any changed values for rendering */
+    QEventLoop loop;
+    if (loop.processEvents())
+        GST_LOG ("pending QEvents processed");
+
+    GST_LOG ("generating output for time %" GST_TIME_FORMAT " ms: %"
+        G_GUINT64_FORMAT, GST_TIME_ARGS (input_ns), input_ns / GST_MSECOND);
+
+    m_quickWindow->update();
+
+    /* Polishing happens on the gui thread. */
+    m_renderControl->polishItems();
+
+    /* TODO: an async version could be used instead */
+    gst_gl_context_thread_add (gl_context,
+            (GstGLContextThreadFunc) GstQt6QuickRenderer::render_gst_gl_c, this);
+
+    GstGLMemory *tmp = gl_mem;
+    gl_mem = NULL;
+
+    return tmp;
+}
+
+void GstQt6QuickRenderer::initializeGstGL ()
+{
+    GST_TRACE ("current QOpenGLContext %p", QOpenGLContext::currentContext());
+    if (!m_sharedRenderData->m_context->makeCurrent(m_sharedRenderData->m_surface)) {
+        m_errorString = "Failed to make Qt's wrapped OpenGL context current";
+        return;
+    }
+    GST_INFO ("current QOpenGLContext %p", QOpenGLContext::currentContext());
+
+    /* XXX: Avoid an assertion inside QSGDefaultRenderContext::initialize()
+     * from an unused (in this scenario) property when using multiple
+     * QQuickRenderControl's with the same QOpenGLContext.
+     *
+     * First noticed with Qt 5.15.  Idea from:
+     * https://forum.qt.io/topic/55888/is-it-impossible-that-2-qquickrendercontrol-use-same-qopenglcontext/2
+     *
+     * ASSERT: "!m_gl->property(QSG_RENDERCONTEXT_PROPERTY).isValid()" in file /path/to/qt5/qtdeclarative/src/quick/scenegraph/qsgdefaultrendercontext.cpp, line 121
+     */
+    //m_sharedRenderData->m_context->setProperty("_q_sgrendercontext", QVariant());
+
+    m_renderControl->initialize();
+
+    /* 1. QAnimationDriver's are thread-specific
+     * 2. QAnimationDriver controls the 'animation time' that the Qml scene is
+     *    rendered at
+     */
+    /* FIXME: what happens with multiple qmlgloverlay elements?  Do we need a
+     * shared animation driver? */
+    g_mutex_lock (&m_sharedRenderData->lock);
+    if (m_sharedRenderData->m_animationDriver == nullptr) {
+        m_sharedRenderData->m_animationDriver = new GstQt6AnimationDriver;
+        m_sharedRenderData->m_animationDriver->install();
+    }
+    g_mutex_unlock (&m_sharedRenderData->lock);
+    /* XXX: reset the OpenGL context drawable as Qt may have clobbered it.
+     * Fixes glimagesink output where Qt replaces the Surface to use in its
+     * own MakeCurrent call.  Qt does this on it's OpenGL initialisation
+     * the the rendering engine. */
+    gst_gl_context_activate (gl_context, FALSE);
+    gst_gl_context_activate (gl_context, TRUE);
+}
+
+void GstQt6QuickRenderer::initializeQml()
+{
+    disconnect(m_qmlComponent, &QQmlComponent::statusChanged, this,
+            &GstQt6QuickRenderer::initializeQml);
+
+    if (m_qmlComponent->isError()) {
+        const QList<QQmlError> errorList = m_qmlComponent->errors();
+        for (const QQmlError &error : errorList)
+            m_errorString += error.toString();
+        return;
+    }
+
+    QObject *rootObject = m_qmlComponent->create();
+    if (m_qmlComponent->isError()) {
+        const QList<QQmlError> errorList = m_qmlComponent->errors();
+        for (const QQmlError &error : errorList)
+            m_errorString += error.toString();
+        delete rootObject;
+        return;
+    }
+
+    m_rootItem = qobject_cast<QQuickItem *>(rootObject);
+    if (!m_rootItem) {
+        m_errorString += "root QML item is not a QQuickItem";
+        delete rootObject;
+        return;
+    }
+
+    /* The root item is ready. Associate it with the window. */
+    m_rootItem->setParentItem(m_quickWindow->contentItem());
+
+    /* Update item and rendering related geometries. */
+    updateSizes();
+
+    /* Initialize the render control and our OpenGL resources. */
+    gst_gl_context_thread_add (gl_context,
+            (GstGLContextThreadFunc) GstQt6QuickRenderer::initialize_gst_gl_c, this);
+}
+
+void GstQt6QuickRenderer::updateSizes()
+{
+    GstQt6BackingSurface *surface =
+            static_cast<GstQt6BackingSurface *>(m_sharedRenderData->m_surface);
+    /* Behave like SizeRootObjectToView. */
+    QSize size = surface->size();
+
+    m_rootItem->setWidth(size.width());
+    m_rootItem->setHeight(size.height());
+
+    m_quickWindow->setGeometry(0, 0, size.width(), size.height());
+
+    gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, size.width(),
+        size.height());
+    GstGLVideoAllocationParams *params = (GstGLVideoAllocationParams *) (gl_params);
+    gst_video_info_set_format (params->v_info, GST_VIDEO_FORMAT_RGBA, size.width(),
+        size.height());
+}
+
+void GstQt6QuickRenderer::setSize(int w, int h)
+{
+    static_cast<GstQt6BackingSurface *>(m_sharedRenderData->m_surface)->setSize(w, h);
+    updateSizes();
+}
+
+bool GstQt6QuickRenderer::setQmlScene (const gchar * scene, GError ** error)
+{
+    /* replacing the scene is not supported */
+    g_return_val_if_fail (m_qmlComponent == NULL, false);
+
+    m_errorString = "";
+
+    m_qmlComponent = new QQmlComponent(m_qmlEngine);
+    /* XXX: do we need to provide a propper base name? */
+    m_qmlComponent->setData(QByteArray (scene), QUrl(""));
+    if (m_qmlComponent->isLoading())
+        /* TODO: handle async properly */
+        connect(m_qmlComponent, &QQmlComponent::statusChanged, this,
+                &GstQt6QuickRenderer::initializeQml);
+    else
+        initializeQml();
+
+    if (m_errorString != "") {
+        QByteArray string = m_errorString.toUtf8();
+        g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
+            "%s", string.constData());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+QQuickItem * GstQt6QuickRenderer::rootItem() const
+{
+    return m_rootItem;
+}
diff --git a/ext/qt6/qt6glrenderer.h b/ext/qt6/qt6glrenderer.h
new file mode 100644
index 0000000000000000000000000000000000000000..0d36a6611737aac806b12d2af6c956bbf705ffed
--- /dev/null
+++ b/ext/qt6/qt6glrenderer.h
@@ -0,0 +1,124 @@
+/*
+ * GStreamer
+ * Copyright (C) 2022 Matthew Waters <matthew@cenricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_QT6_GL_RENDER_H__
+#define __GST_QT6_GL_RENDER_H__
+
+#include <QThread>
+#include <QMutex>
+
+#include <gst/gl/gl.h>
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLContext)
+QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject)
+QT_FORWARD_DECLARE_CLASS(QQuickRenderControl)
+QT_FORWARD_DECLARE_CLASS(QQuickWindow)
+QT_FORWARD_DECLARE_CLASS(QQmlEngine)
+QT_FORWARD_DECLARE_CLASS(QQmlComponent)
+QT_FORWARD_DECLARE_CLASS(QQuickItem)
+QT_FORWARD_DECLARE_CLASS(GstAnimationDriver)
+QT_FORWARD_DECLARE_CLASS(GstBackingSurface)
+
+class GstQt6QuickRenderer : public QObject
+{
+    Q_OBJECT
+
+public:
+    GstQt6QuickRenderer();
+    ~GstQt6QuickRenderer();
+
+    /* initialize the GStreamer/Qt integration.  On failure returns false
+     * and fills @error.
+     * Must be called with @context not wrapped and current in the current
+     * thread  */
+    bool init (GstGLContext * context, GError ** error);
+
+    /* set the qml scene.  returns false and fills @error on failure */
+    bool setQmlScene (const gchar * scene, GError ** error);
+
+    void setSize(int w, int h);
+
+    GstGLMemory *generateOutput(GstClockTime input_ns);
+
+    /* cleanup any resources.  Any use of this object after calling this
+     * function may result in undefined behaviour */
+    void cleanup();
+
+    /* retrieve the rootItem from the qml scene.  Only valid after
+     * setQmlScene() has been successfully called */
+    QQuickItem *rootItem() const;
+
+private slots:
+    void initializeQml();
+
+private:
+    void init();
+    void ensureFbo();
+
+    void updateSizes();
+
+    static void render_gst_gl_c (GstGLContext * context, GstQt6QuickRenderer * self) { self->renderGstGL (); }
+    void renderGstGL ();
+
+    static void initialize_gst_gl_c (GstGLContext * context, GstQt6QuickRenderer * self) { self->initializeGstGL (); }
+    void initializeGstGL ();
+
+    static void stop_c (GstGLContext * context, GstQt6QuickRenderer * self) { self->stopGL (); }
+    void stopGL ();
+    static void stop_after_c (GstGLContext * context, GstQt6QuickRenderer * self) { self->stopAfterGL (); }
+    void stopAfterGL ();
+
+    static void activate_context_c (GstGLContext * context, GstQt6QuickRenderer * self) { self->activateContext (); }
+    void activateContext ();
+
+    static void deactivate_context_c (GstGLContext * context, GstQt6QuickRenderer * self) { self->deactivateContext (); }
+    void deactivateContext ();
+
+    GstGLContext *gl_context;
+    QQuickWindow *m_quickWindow;
+    QQuickRenderControl *m_renderControl;
+    QQmlEngine *m_qmlEngine;
+    QQmlComponent *m_qmlComponent;
+    QQuickItem *m_rootItem;
+
+    GstGLBaseMemoryAllocator *gl_allocator;
+    GstGLAllocationParams *gl_params;
+    GstVideoInfo v_info;
+    GstGLMemory *gl_mem;
+
+    QString m_errorString;
+    struct SharedRenderData *m_sharedRenderData;
+};
+
+class CreateSurfaceWorker : public QObject
+{
+  Q_OBJECT
+
+public:
+  CreateSurfaceWorker (struct SharedRenderData * rdata);
+  ~CreateSurfaceWorker ();
+
+  bool event(QEvent *ev) override;
+
+private:
+  struct SharedRenderData *m_sharedRenderData;
+};
+
+#endif /* __GST_QT6_GL_RENDER_H__ */
diff --git a/ext/qt6/qt6glwindow.cc b/ext/qt6/qt6glwindow.cc
new file mode 100644
index 0000000000000000000000000000000000000000..661187c6eb44a2a701cbdc74f4ecfe202c7e884f
--- /dev/null
+++ b/ext/qt6/qt6glwindow.cc
@@ -0,0 +1,494 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <gst/video/video.h>
+#include <gst/gl/gstglfuncs.h>
+#include "qt6glwindow.h"
+#include "gstqt6glutility.h"
+
+#include <QtCore/QDateTime>
+#include <QtGui/QGuiApplication>
+#include <QtQuick/QQuickWindow>
+#include <QQuickRenderTarget>
+
+/* compatibility definitions... */
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+/**
+ * SECTION:
+ *
+ * #Qt6GLWindow is an #QQuickWindow that grab QtQuick view to GStreamer OpenGL video buffers.
+ */
+
+GST_DEBUG_CATEGORY_STATIC (qt6_gl_window_debug);
+#define GST_CAT_DEFAULT qt6_gl_window_debug
+
+struct _Qt6GLWindowPrivate
+{
+  GMutex lock;
+  GCond update_cond;
+
+  GstBuffer *buffer;
+  GstVideoInfo v_info;
+  GstVideoFrame mapped_frame;
+  GstGLBaseMemoryAllocator *gl_allocator;
+  GstGLAllocationParams *gl_params;
+  GLenum internal_format;
+
+  gboolean initted;
+  gboolean updated;
+  gboolean quit;
+  gboolean result;
+  gboolean useDefaultFbo;
+
+  GstGLDisplay *display;
+  GstGLContext *other_context;
+  GstGLContext *context;
+
+  guint fbo;
+
+  gboolean new_caps;
+  GstBuffer *produced_buffer;
+};
+
+Qt6GLWindow::Qt6GLWindow (QWindow * parent, QQuickWindow *src)
+  : QQuickWindow( parent ), source (src)
+{
+  QGuiApplication *app = static_cast<QGuiApplication *> (QCoreApplication::instance ());
+  static gsize _debug;
+
+  g_assert (app != NULL);
+
+  if (g_once_init_enter (&_debug)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qt6glwindow", 0, "Qt6 GL QuickWindow");
+    g_once_init_leave (&_debug, 1);
+  }
+
+  this->priv = g_new0 (Qt6GLWindowPrivate, 1);
+
+  g_mutex_init (&this->priv->lock);
+  g_cond_init (&this->priv->update_cond);
+
+  this->priv->display = gst_qml6_get_gl_display(FALSE);
+  this->priv->result = TRUE;
+  this->priv->internal_format = GL_RGBA;
+
+  connect (source, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()), Qt::DirectConnection);
+  connect (source, SIGNAL(afterFrameEnd()), this, SLOT(afterFrameEnd()), Qt::DirectConnection);
+  if (source->isSceneGraphInitialized())
+    source->scheduleRenderJob(new RenderJob(std::bind(&Qt6GLWindow::onSceneGraphInitialized, this)), QQuickWindow::BeforeSynchronizingStage);
+  else
+    connect (source, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection);
+
+  connect (source, SIGNAL(sceneGraphInvalidated()), this, SLOT(onSceneGraphInvalidated()), Qt::DirectConnection);
+
+  GST_DEBUG ("%p init Qt Window", this->priv->display);
+}
+
+Qt6GLWindow::~Qt6GLWindow()
+{
+  GST_DEBUG ("deinit Qt Window");
+  g_mutex_clear (&this->priv->lock);
+  g_cond_clear (&this->priv->update_cond);
+  gst_clear_object (&this->priv->other_context);
+  gst_clear_buffer (&this->priv->buffer);
+  gst_clear_buffer (&this->priv->produced_buffer);
+  gst_clear_object (&this->priv->display);
+  gst_clear_object (&this->priv->context);
+  gst_clear_object (&this->priv->gl_allocator);
+
+  if (this->priv->gl_params)
+    gst_gl_allocation_params_free (this->priv->gl_params);
+  this->priv->gl_params = NULL;
+
+  g_free (this->priv);
+  this->priv = NULL;
+}
+
+void
+Qt6GLWindow::beforeRendering()
+{
+  g_mutex_lock (&this->priv->lock);
+
+  if (!this->priv->context) {
+    GST_LOG ("no GStreamer GL context set yet, skipping frame");
+    g_mutex_unlock (&this->priv->lock);
+    return;
+  }
+
+  QSize size = source->size();
+
+  if (!this->priv->gl_allocator)
+    this->priv->gl_allocator =
+        (GstGLBaseMemoryAllocator *) gst_gl_memory_allocator_get_default (this->priv->context);
+
+  if (GST_VIDEO_INFO_WIDTH (&this->priv->v_info) != size.width()
+      || GST_VIDEO_INFO_HEIGHT (&this->priv->v_info) != size.height()) {
+    this->priv->new_caps = TRUE;
+
+    gst_video_info_set_format (&this->priv->v_info, GST_VIDEO_FORMAT_RGBA,
+        size.width(), size.height());
+
+    if (this->priv->gl_params) {
+      GstGLVideoAllocationParams *gl_vid_params = (GstGLVideoAllocationParams *) this->priv->gl_params;
+      if (GST_VIDEO_INFO_WIDTH (gl_vid_params->v_info) != source->width()
+            || GST_VIDEO_INFO_HEIGHT (gl_vid_params->v_info) != source->height())
+        this->priv->gl_params = NULL;
+      gst_clear_buffer (&this->priv->buffer);
+    }
+  }
+
+  if (!this->priv->gl_params) {
+    this->priv->gl_params = (GstGLAllocationParams *)
+        gst_gl_video_allocation_params_new (this->priv->context, NULL,
+        &this->priv->v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA);
+  }
+
+  if (!this->priv->buffer) {
+    GstGLMemory *gl_mem =
+        (GstGLMemory *) gst_gl_base_memory_alloc (this->priv->gl_allocator,
+        this->priv->gl_params);
+    this->priv->buffer = gst_buffer_new ();
+    gst_buffer_append_memory (this->priv->buffer, (GstMemory *) gl_mem);
+  }
+
+  if (!gst_video_frame_map (&this->priv->mapped_frame, &this->priv->v_info,
+        this->priv->buffer, (GstMapFlags) (GST_MAP_WRITE | GST_MAP_GL))) {
+    GST_WARNING ("failed map video frame");
+    gst_clear_buffer (&this->priv->buffer);
+    return;
+  }
+
+  if (!this->priv->useDefaultFbo) {
+    guint tex_id = *(guint *) this->priv->mapped_frame.data[0];
+
+    source->setRenderTarget(QQuickRenderTarget::fromOpenGLTexture(tex_id, source->size()));
+  } else if (this->priv->useDefaultFbo) {
+    GST_DEBUG ("use default fbo for render target");
+    source->setRenderTarget(QQuickRenderTarget());
+  }
+
+  g_mutex_unlock (&this->priv->lock);
+}
+
+void
+Qt6GLWindow::afterFrameEnd()
+{
+  gboolean ret;
+  guint width, height;
+  const GstGLFuncs *gl;
+  GstGLSyncMeta *sync_meta;
+  GLenum fbo_target;
+
+  g_mutex_lock (&this->priv->lock);
+
+  if (!this->priv->buffer) {
+    GST_LOG ("no buffer created in beforeRendering(), skipping");
+    g_mutex_unlock (&this->priv->lock);
+    return;
+  }
+
+  width = GST_VIDEO_INFO_WIDTH (&this->priv->v_info);
+  height = GST_VIDEO_INFO_HEIGHT (&this->priv->v_info);
+
+  gst_gl_context_activate (this->priv->other_context, TRUE);
+  gl = this->priv->other_context->gl_vtable;
+
+  fbo_target = gl->BlitFramebuffer ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER;
+
+  if (!this->priv->useDefaultFbo) {
+    ret = TRUE;
+  } else {
+
+    gl->BindFramebuffer (fbo_target, 0);
+
+    ret = gst_gl_context_check_framebuffer_status (this->priv->other_context, fbo_target);
+    if (!ret) {
+      GST_ERROR ("FBO errors");
+      goto errors;
+    }
+
+    guint dst_tex = *(guint *) this->priv->mapped_frame.data[0];
+    gl->BindTexture (GL_TEXTURE_2D, dst_tex);
+    if (gl->BlitFramebuffer) {
+      gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, this->priv->fbo);
+      gl->FramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                GL_TEXTURE_2D, dst_tex, 0);
+
+      ret = gst_gl_context_check_framebuffer_status (this->priv->other_context, GL_DRAW_FRAMEBUFFER);
+      if (!ret) {
+        GST_ERROR ("FBO errors");
+        goto errors;
+      }
+      gl->ReadBuffer (GL_BACK);
+      gl->BlitFramebuffer (0, 0, width, height,
+          0, 0, width, height,
+          GL_COLOR_BUFFER_BIT, GL_LINEAR);
+    } else {
+      gl->CopyTexImage2D (GL_TEXTURE_2D, 0, this->priv->internal_format, 0, 0, width, height, 0);
+
+      GLenum err = gl->GetError ();
+      if (err && this->priv->internal_format == GL_RGBA) {
+        this->priv->internal_format = GL_RGB;
+        GST_WARNING ("Falling back to GL_RGB (opaque) when copying QML texture.");
+        gl->CopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, 0, 0, width, height, 0);
+        err = gl->GetError ();
+      }
+
+      if (err) {
+        GST_ERROR ("CopyTexImage2D() failed with error: 0x%X", err);
+        ret = FALSE;
+        goto errors;
+      }
+    }
+  }
+
+  gst_video_frame_unmap (&this->priv->mapped_frame);
+  gl->BindFramebuffer (fbo_target, 0);
+
+  if (gl->BlitFramebuffer)
+      gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, 0);
+
+  if (this->priv->context) {
+    sync_meta = gst_buffer_get_gl_sync_meta (this->priv->buffer);
+    if (!sync_meta) {
+      sync_meta = gst_buffer_add_gl_sync_meta (this->priv->context, this->priv->buffer);
+    }
+    gst_gl_sync_meta_set_sync_point (sync_meta, this->priv->other_context);
+  }
+
+  GST_DEBUG ("rendering finished");
+
+done:
+  gst_gl_context_activate (this->priv->other_context, FALSE);
+
+  this->priv->result = ret;
+  gst_clear_buffer (&this->priv->produced_buffer);
+  this->priv->produced_buffer = this->priv->buffer;
+  this->priv->buffer = NULL;
+  this->priv->updated = TRUE;
+  g_cond_signal (&this->priv->update_cond);
+  g_mutex_unlock (&this->priv->lock);
+  return;
+
+errors:
+  gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
+  gst_video_frame_unmap (&this->priv->mapped_frame);
+  goto done;
+}
+
+void
+Qt6GLWindow::onSceneGraphInitialized()
+{
+  QSGRendererInterface *renderer = source->rendererInterface();
+  if (!renderer)
+    return;
+
+  if (renderer->graphicsApi() != QSGRendererInterface::GraphicsApi::OpenGL) {
+    GST_WARNING ("%p scene graph initialized with a non-OpenGL renderer interface", this);
+    return;
+  }
+
+  this->priv->initted = gst_qml6_get_gl_wrapcontext (this->priv->display,
+      &this->priv->other_context, &this->priv->context);
+  this->priv->internal_format = GL_RGBA;
+
+  if (this->priv->initted && this->priv->other_context) {
+    const GstGLFuncs *gl;
+
+    gst_gl_context_activate (this->priv->other_context, TRUE);
+    gl = this->priv->other_context->gl_vtable;
+
+    gl->GenFramebuffers (1, &this->priv->fbo);
+
+    gst_gl_context_activate (this->priv->other_context, FALSE);
+  }
+
+  GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this,
+      this->priv->other_context);
+}
+
+void
+Qt6GLWindow::onSceneGraphInvalidated()
+{
+  GST_DEBUG ("scene graph invalidated");
+
+  if (this->priv->fbo && this->priv->other_context) {
+    const GstGLFuncs *gl;
+
+    gst_gl_context_activate (this->priv->other_context, TRUE);
+    gl = this->priv->other_context->gl_vtable;
+
+    gl->DeleteFramebuffers (1, &this->priv->fbo);
+
+    gst_gl_context_activate (this->priv->other_context, FALSE);
+  }
+
+  gst_clear_buffer (&this->priv->buffer);
+  gst_clear_buffer (&this->priv->produced_buffer);
+}
+
+bool
+Qt6GLWindow::getGeometry(int * width, int * height)
+{
+  if (width == NULL || height == NULL)
+    return FALSE;
+
+  *width = this->source->width();
+  *height = this->source->height();
+
+  return TRUE;
+}
+
+GstGLContext *
+qt6_gl_window_get_qt_context (Qt6GLWindow * qt6_gl_window)
+{
+  g_return_val_if_fail (qt6_gl_window != NULL, NULL);
+
+  if (!qt6_gl_window->priv->other_context)
+    return NULL;
+
+  return (GstGLContext *) gst_object_ref (qt6_gl_window->priv->other_context);
+}
+
+GstGLDisplay *
+qt6_gl_window_get_display (Qt6GLWindow * qt6_gl_window)
+{
+  g_return_val_if_fail (qt6_gl_window != NULL, NULL);
+
+  if (!qt6_gl_window->priv->display)
+    return NULL;
+
+  return (GstGLDisplay *) gst_object_ref (qt6_gl_window->priv->display);
+}
+
+GstGLContext *
+qt6_gl_window_get_context (Qt6GLWindow * qt6_gl_window)
+{
+  g_return_val_if_fail (qt6_gl_window != NULL, NULL);
+
+  if (!qt6_gl_window->priv->context)
+    return NULL;
+
+  return (GstGLContext *) gst_object_ref (qt6_gl_window->priv->context);
+}
+
+gboolean
+qt6_gl_window_set_context (Qt6GLWindow * qt6_gl_window, GstGLContext * context)
+{
+  g_return_val_if_fail (qt6_gl_window != NULL, FALSE);
+
+  if (qt6_gl_window->priv->context && qt6_gl_window->priv->context != context)
+    return FALSE;
+
+  gst_object_replace ((GstObject **) &qt6_gl_window->priv->context, (GstObject *) context);
+
+  return TRUE;
+}
+
+gboolean
+qt6_gl_window_is_scenegraph_initialized (Qt6GLWindow * qt6_gl_window)
+{
+  g_return_val_if_fail (qt6_gl_window != NULL, FALSE);
+
+  return qt6_gl_window->priv->initted;
+}
+
+GstBuffer *
+qt6_gl_window_take_buffer (Qt6GLWindow * qt6_gl_window, GstCaps ** updated_caps)
+{
+  g_return_val_if_fail (qt6_gl_window != NULL, FALSE);
+  g_return_val_if_fail (qt6_gl_window->priv->initted, FALSE);
+  GstBuffer *ret;
+
+  g_mutex_lock (&qt6_gl_window->priv->lock);
+
+  if (qt6_gl_window->priv->quit){
+    GST_DEBUG("about to quit, drop this buffer");
+    g_mutex_unlock (&qt6_gl_window->priv->lock);
+    return NULL;
+  }
+
+  while (!qt6_gl_window->priv->produced_buffer && qt6_gl_window->priv->result)
+    g_cond_wait (&qt6_gl_window->priv->update_cond, &qt6_gl_window->priv->lock);
+
+  ret = qt6_gl_window->priv->produced_buffer;
+  qt6_gl_window->priv->produced_buffer = NULL;
+
+  if (qt6_gl_window->priv->new_caps) {
+    *updated_caps = gst_video_info_to_caps (&qt6_gl_window->priv->v_info);
+    gst_caps_set_features (*updated_caps, 0,
+        gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));
+    qt6_gl_window->priv->new_caps = FALSE;
+  }
+
+  g_mutex_unlock (&qt6_gl_window->priv->lock);
+
+  return ret;
+}
+
+void
+qt6_gl_window_use_default_fbo (Qt6GLWindow * qt6_gl_window, gboolean useDefaultFbo)
+{
+  g_return_if_fail (qt6_gl_window != NULL);
+
+  g_mutex_lock (&qt6_gl_window->priv->lock);
+
+  GST_DEBUG ("set to use default fbo %d", useDefaultFbo);
+  qt6_gl_window->priv->useDefaultFbo = useDefaultFbo;
+
+  g_mutex_unlock (&qt6_gl_window->priv->lock);
+}
+
+void
+qt6_gl_window_unlock(Qt6GLWindow* qt6_gl_window)
+{
+  g_mutex_lock(&qt6_gl_window->priv->lock);
+
+  GST_DEBUG("unlock window");
+  qt6_gl_window->priv->result = FALSE;
+  g_cond_signal(&qt6_gl_window->priv->update_cond);
+
+  g_mutex_unlock(&qt6_gl_window->priv->lock);
+}
+
+void
+qt6_gl_window_unlock_stop(Qt6GLWindow* qt6_gl_window)
+{
+  g_mutex_lock(&qt6_gl_window->priv->lock);
+
+  GST_DEBUG("unlock stop window");
+  qt6_gl_window->priv->result = TRUE;
+  g_cond_signal(&qt6_gl_window->priv->update_cond);
+
+  g_mutex_unlock(&qt6_gl_window->priv->lock);
+}
diff --git a/ext/qt6/qt6glwindow.h b/ext/qt6/qt6glwindow.h
new file mode 100644
index 0000000000000000000000000000000000000000..3c0fc14d978b75946d597318a0b2c5ce2651c543
--- /dev/null
+++ b/ext/qt6/qt6glwindow.h
@@ -0,0 +1,69 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __QT6_GL_WINDOW_H__
+#define __QT6_GL_WINDOW_H__
+
+#include <gst/gst.h>
+#include <gst/gl/gl.h>
+
+#include "gstqt6gl.h"
+#include <QtQuick/QQuickWindow>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+
+typedef struct _Qt6GLWindowPrivate Qt6GLWindowPrivate;
+
+class Qt6GLWindow : public QQuickWindow, protected QOpenGLFunctions
+{
+    Q_OBJECT
+public:
+    Qt6GLWindow (QWindow * parent = NULL, QQuickWindow *source = NULL);
+    ~Qt6GLWindow ();
+    bool getGeometry (int * width, int * height);
+
+    /* private for C interface ... */
+    Qt6GLWindowPrivate *priv;
+
+private Q_SLOTS:
+    void beforeRendering ();
+    void afterFrameEnd ();
+    void onSceneGraphInitialized ();
+    void onSceneGraphInvalidated ();
+
+private:
+    QQuickWindow * source;
+};
+
+extern "C"
+{
+GstBuffer *     qt6_gl_window_take_buffer (Qt6GLWindow * qt6_window, GstCaps ** updated_caps);
+GstGLContext *  qt6_gl_window_get_qt_context (Qt6GLWindow * qt6_window);
+GstGLContext *  qt6_gl_window_get_context (Qt6GLWindow * qt6_window);
+gboolean        qt6_gl_window_set_context (Qt6GLWindow * qt6_window, GstGLContext * context);
+GstGLDisplay *  qt6_gl_window_get_display (Qt6GLWindow * qt6_window);
+gboolean        qt6_gl_window_is_scenegraph_initialized (Qt6GLWindow * qt6_window);
+void            qt6_gl_window_use_default_fbo (Qt6GLWindow * qt6_window, gboolean useDefaultFbo);
+void            qt6_gl_window_unlock(Qt6GLWindow* qt6_window);
+void            qt6_gl_window_unlock_stop(Qt6GLWindow* qt6_window);
+}
+
+#endif /* __QT6_GL_WINDOW_H__ */
diff --git a/ext/qt6/resources.qrc b/ext/qt6/resources.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..7a01e20f68f6f7a7425e210a06faacb2c3b5d770
--- /dev/null
+++ b/ext/qt6/resources.qrc
@@ -0,0 +1,7 @@
+<RCC>
+    <qresource prefix="/org/freedesktop/gstreamer/qml6">
+        <file>vertex.vert.qsb</file>
+        <file>RGBA.frag.qsb</file>
+        <file>YUV_TRIPLANAR.frag.qsb</file>
+    </qresource>
+</RCC>
diff --git a/ext/qt6/vertex.vert b/ext/qt6/vertex.vert
new file mode 100644
index 0000000000000000000000000000000000000000..f67117731db209eda6804cccffe556cb41a689e4
--- /dev/null
+++ b/ext/qt6/vertex.vert
@@ -0,0 +1,23 @@
+#version 440
+
+layout(location = 0) in vec4 aVertex;
+layout(location = 1) in vec2 aTexCoord;
+
+layout(location = 0) out vec2 vTexCoord;
+
+layout(std140, binding = 0) uniform buf {
+  mat4 qt_Matrix;
+  ivec4 swizzle;
+  mat4 color_matrix;
+  float qt_Opacity;
+} ubuf;
+
+out gl_PerVertex {
+  vec4 gl_Position;
+};
+
+void main()
+{
+  gl_Position = ubuf.qt_Matrix * aVertex;
+  vTexCoord = aTexCoord;
+}
diff --git a/ext/shout2/gstshout2.c b/ext/shout2/gstshout2.c
index 2564408ecf20038a91f1cebaa9db427313486f7f..ae14324e83ccd4ea0e51d181f9b97611373d03bb 100644
--- a/ext/shout2/gstshout2.c
+++ b/ext/shout2/gstshout2.c
@@ -45,6 +45,8 @@
 
 #include <glib/gi18n-lib.h>
 
+#include <gst/glib-compat-private.h>
+
 #ifndef HAVE_SHOUT_2_4_6_OR_NEWER
 #define shout_set_metadata_utf8 shout_set_metadata
 #endif
@@ -88,7 +90,7 @@ enum
 #define DEFAULT_PUBLIC       FALSE
 #define DEFAULT_STREAMNAME   ""
 #define DEFAULT_DESCRIPTION  ""
-#define DEFAULT_USERAGENT    "GStreamer " PACKAGE_VERSION
+#define DEFAULT_USERAGENT    "GStreamer {VERSION}"
 #define DEFAULT_GENRE        ""
 #define DEFAULT_MOUNT        ""
 #define DEFAULT_URL          ""
@@ -235,6 +237,9 @@ gst_shout2send_class_init (GstShout2sendClass * klass)
    *
    * User agent of the source
    *
+   * If the string contains `{VERSION}` that will be replaced with the
+   * GStreamer version at runtime (since GStreamer 1.24).
+   *
    * Since: 1.22
    **/
 
@@ -486,6 +491,8 @@ gst_shout2send_event (GstBaseSink * sink, GstEvent * event)
     }
   }
 
+  gst_event_unref (event);
+
   return ret;
 }
 
@@ -574,10 +581,14 @@ gst_shout2send_start (GstBaseSink * basesink)
     goto set_failed;
 
   cur_prop = "agent";
-  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->user_agent);
-  if (shout_set_agent (sink->conn, sink->user_agent) != SHOUTERR_SUCCESS) {
+  GString *user_agent = g_string_new (sink->user_agent);
+  g_string_replace (user_agent, "{VERSION}", PACKAGE_VERSION, 0);
+  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, user_agent->str);
+  if (shout_set_agent (sink->conn, user_agent->str) != SHOUTERR_SUCCESS) {
+    g_string_free (user_agent, TRUE);
     goto set_failed;
   }
+  g_string_free (user_agent, TRUE);
 
   return TRUE;
 
diff --git a/ext/soup/gstsoupelement.c b/ext/soup/gstsoupelement.c
index bd1ad3501bfaf3fad650935c4c29f8d6342f4e95..d99caf90898f161bda793a242c6d13c605be7261 100644
--- a/ext/soup/gstsoupelement.c
+++ b/ext/soup/gstsoupelement.c
@@ -56,7 +56,7 @@ soup_element_init (GstPlugin * plugin)
 
     g_once_init_leave (&res, TRUE);
   }
-#ifndef STATIC_SOUP
+#ifndef LINK_SOUP
   if (!gst_soup_load_library ()) {
     GST_WARNING ("Failed to load libsoup library");
     return FALSE;
diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c
index aa5097967a2605f705d62dc91aca87ab7c28914d..32d2e2039b623662825ce65e1368fd98070237c6 100644
--- a/ext/soup/gstsouphttpsrc.c
+++ b/ext/soup/gstsouphttpsrc.c
@@ -84,6 +84,8 @@
 
 #include <gst/tag/tag.h>
 
+#include <gst/glib-compat-private.h>
+
 /* this is a simple wrapper class around SoupSession; it exists in order to
  * have a refcountable owner for the actual SoupSession + the thread it runs
  * in and its main loop (we cannot inverse the ownership hierarchy, because
@@ -94,6 +96,7 @@
 
 #define GST_TYPE_SOUP_SESSION (gst_soup_session_get_type())
 #define GST_SOUP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SOUP_SESSION, GstSoupSession))
+#define gst_soup_session_parent_class session_parent_class
 
 GType gst_soup_session_get_type (void);
 
@@ -137,8 +140,9 @@ gst_soup_session_finalize (GObject * obj)
   GSource *src;
 
   /* handle disposing of failure cases */
-  if (!sess->loop)
-    return;
+  if (!sess->loop) {
+    goto cleanup;
+  }
 
   src = g_idle_source_new ();
 
@@ -150,6 +154,8 @@ gst_soup_session_finalize (GObject * obj)
   g_assert (!g_main_context_is_owner (g_main_loop_get_context (sess->loop)));
   g_thread_join (sess->thread);
   g_main_loop_unref (sess->loop);
+cleanup:
+  G_OBJECT_CLASS (session_parent_class)->finalize (obj);
 }
 
 static void
@@ -198,7 +204,15 @@ enum
   PROP_TLS_INTERACTION,
 };
 
-#define DEFAULT_USER_AGENT           "GStreamer souphttpsrc " PACKAGE_VERSION " "
+enum
+{
+  SIGNAL_ACCEPT_CERTIFICATE,
+  LAST_SIGNAL,
+};
+
+static guint gst_soup_http_src_signals[LAST_SIGNAL] = { 0 };
+
+#define DEFAULT_USER_AGENT           "GStreamer souphttpsrc {VERSION} "
 #define DEFAULT_IRADIO_MODE          TRUE
 #define DEFAULT_SOUP_LOG_LEVEL       SOUP_LOGGER_LOG_HEADERS
 #define DEFAULT_COMPRESS             FALSE
@@ -424,7 +438,8 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
   g_object_class_install_property (gobject_class, PROP_SSL_CA_FILE,
       g_param_spec_string ("ssl-ca-file", "SSL CA File",
           "Location of a SSL anchor CA file to use", DEFAULT_SSL_CA_FILE,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
+          | GST_PARAM_DOC_SHOW_DEFAULT));
 
   /**
    * GstSoupHTTPSrc::ssl-use-system-ca-file:
@@ -440,7 +455,8 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
   g_object_class_install_property (gobject_class, PROP_SSL_USE_SYSTEM_CA_FILE,
       g_param_spec_boolean ("ssl-use-system-ca-file", "Use System CA File",
           "Use system CA file", DEFAULT_SSL_USE_SYSTEM_CA_FILE,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
+          | GST_PARAM_DOC_SHOW_DEFAULT));
 
   /**
    * GstSoupHTTPSrc::tls-database:
@@ -497,6 +513,27 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
           "The HTTP method to use (GET, HEAD, OPTIONS, etc)",
           DEFAULT_SOUP_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstSoupHTTPSrc::accept-certificate:
+   * @souphttpsrc: a #GstSoupHTTPSrc
+   * @peer_cert: the peer's #GTlsCertificate
+   * @errors: the problems with @peer_cert
+   *
+   * This will directly map to #SoupMessage 's "accept-certificate" after
+   * an unacceptable TLS certificate has been received, and only for libsoup 3.x
+   * or above. If "ssl-strict" was set to %FALSE, this signal will not be
+   * emitted.
+   *
+   * Returns: %TRUE to accept the TLS certificate and stop other handlers from
+   * being invoked, or %FALSE to propagate the event further.
+   *
+   * Since: 1.24
+   */
+  gst_soup_http_src_signals[SIGNAL_ACCEPT_CERTIFICATE] =
+      g_signal_new ("accept-certificate", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL,
+      G_TYPE_BOOLEAN, 2, G_TYPE_TLS_CERTIFICATE, G_TYPE_TLS_CERTIFICATE_FLAGS);
+
   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
 
   gst_element_class_set_static_metadata (gstelement_class, "HTTP client source",
@@ -1012,11 +1049,11 @@ thread_func (gpointer user_data)
           NULL);
       g_object_unref (proxy_resolver);
     }
-#if !defined(STATIC_SOUP) || STATIC_SOUP == 2
+#if !defined(LINK_SOUP) || LINK_SOUP == 2
   } else {
     g_object_set (session->session, "ssl-strict", src->ssl_strict, NULL);
     if (src->proxy != NULL) {
-      /* Need #if because there's no proxy->soup_uri when STATIC_SOUP == 3 */
+      /* Need #if because there's no proxy->soup_uri when LINK_SOUP == 3 */
       g_object_set (session->session, "proxy-uri", src->proxy->soup_uri, NULL);
     }
 #endif
@@ -1165,7 +1202,7 @@ gst_soup_http_src_session_open (GstSoupHTTPSrc * src)
     /* now owned by the loop */
     g_main_context_unref (ctx);
 
-    src->session->thread = g_thread_try_new ("souphttpsrc-thread",
+    src->session->thread = g_thread_try_new ("souphttpsrc",
         thread_func, src, NULL);
 
     if (!src->session->thread) {
@@ -1312,6 +1349,7 @@ gst_soup_http_src_accept_certificate_cb (SoupMessage * msg,
     gpointer user_data)
 {
   GstSoupHTTPSrc *src = user_data;
+  gboolean accept = FALSE;
 
   /* Might be from another user of the shared session */
   if (!GST_IS_SOUP_HTTP_SRC (src) || msg != src->msg)
@@ -1321,7 +1359,10 @@ gst_soup_http_src_accept_certificate_cb (SoupMessage * msg,
   if (!src->ssl_strict)
     return TRUE;
 
-  return FALSE;
+  g_signal_emit (src, gst_soup_http_src_signals[SIGNAL_ACCEPT_CERTIFICATE], 0,
+      tls_certificate, tls_errors, &accept);
+
+  return accept;
 }
 
 static void
@@ -1614,7 +1655,7 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src)
   }
 
   /* SOUP_STATUS_IS_TRANSPORT_ERROR was replaced with GError in libsoup-3.0 */
-#if !defined(STATIC_SOUP) || STATIC_SOUP == 2
+#if !defined(LINK_SOUP) || LINK_SOUP == 2
   if (SOUP_STATUS_IS_TRANSPORT_ERROR (status_code)) {
     switch (status_code) {
       case SOUP_STATUS_CANT_RESOLVE:
@@ -1729,22 +1770,16 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
   /* Duplicating the defaults of libsoup here. We don't want to set a
    * User-Agent in the session as each source might have its own User-Agent
    * set */
-  if (!src->user_agent || !*src->user_agent) {
-    gchar *user_agent =
-        g_strdup_printf ("libsoup/%u.%u.%u", _soup_get_major_version (),
-        _soup_get_minor_version (), _soup_get_micro_version ());
-    _soup_message_headers_append (request_headers, "User-Agent", user_agent);
-    g_free (user_agent);
-  } else if (g_str_has_suffix (src->user_agent, " ")) {
-    gchar *user_agent = g_strdup_printf ("%slibsoup/%u.%u.%u", src->user_agent,
-        _soup_get_major_version (),
-        _soup_get_minor_version (), _soup_get_micro_version ());
-    _soup_message_headers_append (request_headers, "User-Agent", user_agent);
-    g_free (user_agent);
-  } else {
-    _soup_message_headers_append (request_headers, "User-Agent",
-        src->user_agent);
+  GString *user_agent = g_string_new (src->user_agent);
+  g_string_replace (user_agent, "{VERSION}", PACKAGE_VERSION, 0);
+  if (user_agent->len == 0 || g_str_has_suffix (user_agent->str, " ")) {
+    g_string_append_printf (user_agent, "libsoup/%u.%u.%u",
+        _soup_get_major_version (), _soup_get_minor_version (),
+        _soup_get_micro_version ());
   }
+  _soup_message_headers_append (request_headers, "User-Agent", user_agent->str);
+  g_string_free (user_agent, TRUE);
+  user_agent = NULL;
 
   if (!src->keep_alive) {
     _soup_message_headers_append (request_headers, "Connection", "close");
@@ -1781,7 +1816,7 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
     /* SOUP_MESSAGE_OVERWRITE_CHUNKS is gone in libsoup-3.0, and
      * soup_message_body_set_accumulate() requires SoupMessageBody, which
      * can only be fetched from SoupServerMessage, not SoupMessage */
-#if !defined(STATIC_SOUP) || STATIC_SOUP == 2
+#if !defined(LINK_SOUP) || LINK_SOUP == 2
     if (gst_soup_loader_get_api_version () == 2)
       flags |= SOUP_MESSAGE_OVERWRITE_CHUNKS;
 #endif
diff --git a/ext/soup/gstsouploader.c b/ext/soup/gstsouploader.c
index b65cfdab5202e97a9bebb24b696b5fcbc7edecf1..9192e4dac5e909c881d8b7642badf5f62df01bb6 100644
--- a/ext/soup/gstsouploader.c
+++ b/ext/soup/gstsouploader.c
@@ -23,13 +23,6 @@
 #include <dlfcn.h>
 #endif
 
-#ifdef G_OS_WIN32
-#include <windows.h>
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-#define GST_WINAPI_ONLY_APP
-#endif
-#endif /* G_OS_WIN32 */
-
 #ifdef BUILDING_ADAPTIVEDEMUX2
 GST_DEBUG_CATEGORY (gst_adaptivedemux_soup_debug);
 #define GST_CAT_DEFAULT gst_adaptivedemux_soup_debug
@@ -39,17 +32,14 @@ GST_DEBUG_CATEGORY (gst_soup_debug);
 #endif
 
 
-#ifndef STATIC_SOUP
+#ifndef LINK_SOUP
 
-/* G_OS_WIN32 is handled separately below */
-#ifdef __APPLE__
-#define LIBSOUP_3_SONAME "libsoup-3.0.0.dylib"
-#define LIBSOUP_2_SONAME "libsoup-2.4.1.dylib"
-#else
-#define LIBSOUP_3_SONAME "libsoup-3.0.so.0"
-#define LIBSOUP_2_SONAME "libsoup-2.4.so.1"
+#if defined(__APPLE__) || defined(G_OS_WIN32)
+#error "dlopen of libsoup is only supported on Linux"
 #endif
 
+#define LIBSOUP_3_SONAME "libsoup-3.0.so.0"
+#define LIBSOUP_2_SONAME "libsoup-2.4.so.1"
 
 #define LOAD_SYMBOL(name) G_STMT_START {                                \
     if (!g_module_symbol (module, G_STRINGIFY (name), (gpointer *) &G_PASTE (vtable->_, name))) { \
@@ -137,6 +127,10 @@ typedef struct _GstSoupVTable
     GAsyncResult * result, GError ** error);
   GInputStream *(*_soup_session_send) (SoupSession * session, SoupMessage * msg,
     GCancellable * cancellable, GError ** error);
+  SoupCookie* (*_soup_cookie_parse) (const char* header, gpointer origin_uri);
+  void (*_soup_cookies_to_request) (GSList* cookies, SoupMessage* msg);
+  void (*_soup_cookies_free) (GSList *cookies);
+
   /* *INDENT-ON* */
 } GstSoupVTable;
 
@@ -183,52 +177,9 @@ gst_soup_load_library (void)
 
     g_clear_pointer (&handle, dlclose);
   }
-#else /* !HAVE_RTLD_NOLOAD */
-
-#ifdef G_OS_WIN32
-
-#define LIBSOUP2_MSVC_DLL "soup-2.4-1.dll"
-#define LIBSOUP3_MSVC_DLL "soup-3.0-0.dll"
-#define LIBSOUP2_MINGW_DLL "libsoup-2.4-1.dll"
-#define LIBSOUP3_MINGW_DLL "libsoup-3.0-0.dll"
-
-  {
-#ifdef _MSC_VER
-    const char *candidates[5] = { LIBSOUP3_MSVC_DLL, LIBSOUP2_MSVC_DLL,
-      LIBSOUP3_MINGW_DLL, LIBSOUP2_MINGW_DLL, 0
-    };
-#else
-    const char *candidates[5] = { LIBSOUP3_MINGW_DLL, LIBSOUP2_MINGW_DLL,
-      LIBSOUP3_MSVC_DLL, LIBSOUP2_MSVC_DLL, 0
-    };
-#endif /* _MSC_VER */
-
-    guint len = g_strv_length ((gchar **) candidates);
-#if !GST_WINAPI_ONLY_APP
-    for (guint i = 0; i < len; i++) {
-      HMODULE phModule;
-      BOOL loaded =
-          GetModuleHandleExA (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
-          candidates[i], &phModule);
-      if (loaded) {
-        GST_DEBUG ("%s is resident. Using it.", candidates[i]);
-        libsoup_sonames[0] = candidates[i];
-        break;
-      }
-    }
-#endif
-    if (libsoup_sonames[0] == NULL) {
-      GST_DEBUG ("No resident libsoup, trying them all");
-      for (guint i = 0; i < len; i++) {
-        libsoup_sonames[i] = candidates[i];
-      }
-    }
-  }
-#else /* !G_OS_WIN32 */
+#else
   libsoup_sonames[0] = LIBSOUP_3_SONAME;
   libsoup_sonames[1] = LIBSOUP_2_SONAME;
-#endif /* G_OS_WIN32 */
-
 #endif /* HAVE_RTLD_NOLOAD */
 
   vtable = &gst_soup_vtable;
@@ -291,6 +242,9 @@ gst_soup_load_library (void)
       LOAD_SYMBOL (soup_session_get_type);
       LOAD_SYMBOL (soup_session_send);
       LOAD_SYMBOL (soup_session_send_finish);
+      LOAD_SYMBOL (soup_cookie_parse);
+      LOAD_SYMBOL (soup_cookies_to_request);
+      LOAD_SYMBOL (soup_cookies_free);
 
       vtable->loaded = TRUE;
       goto beach;
@@ -310,13 +264,13 @@ gst_soup_load_library (void)
   return vtable->loaded;
 }
 
-#endif /* !STATIC_SOUP */
+#endif /* !LINK_SOUP */
 
 guint
 gst_soup_loader_get_api_version (void)
 {
-#ifdef STATIC_SOUP
-  return STATIC_SOUP;
+#ifdef LINK_SOUP
+  return LINK_SOUP;
 #else
   return gst_soup_vtable.lib_version;
 #endif
@@ -339,10 +293,10 @@ _soup_session_new_with_options (const char *optname1, ...)
 SoupLogger *
 _soup_logger_new (SoupLoggerLogLevel level)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   return soup_logger_new (level, -1);
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   return soup_logger_new (level);
 #endif
 #else
@@ -359,7 +313,7 @@ void
 _soup_logger_set_printer (SoupLogger * logger, SoupLoggerPrinter printer,
     gpointer printer_data, GDestroyNotify destroy)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_logger_set_printer (logger, printer, printer_data, destroy);
 #else
   g_assert (gst_soup_vtable._soup_logger_set_printer != NULL);
@@ -371,7 +325,7 @@ _soup_logger_set_printer (SoupLogger * logger, SoupLoggerPrinter printer,
 void
 _soup_session_add_feature (SoupSession * session, SoupSessionFeature * feature)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_session_add_feature (session, feature);
 #else
   g_assert (gst_soup_vtable._soup_session_add_feature != NULL);
@@ -383,8 +337,8 @@ GstSoupUri *
 gst_soup_uri_new (const char *uri_string)
 {
   GstSoupUri *uri = g_new0 (GstSoupUri, 1);
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   uri->soup_uri = soup_uri_new (uri_string);
 #else
   uri->uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL);
@@ -405,48 +359,48 @@ gst_soup_uri_new (const char *uri_string)
 void
 gst_soup_uri_free (GstSoupUri * uri)
 {
-#if (defined(STATIC_SOUP) && STATIC_SOUP == 3) || (!defined(STATIC_SOUP) && GLIB_CHECK_VERSION(2, 66, 0))
+#if (defined(LINK_SOUP) && LINK_SOUP == 3) || (!defined(LINK_SOUP) && GLIB_CHECK_VERSION(2, 66, 0))
   if (uri->uri) {
     g_uri_unref (uri->uri);
   }
 #endif
 
-#if defined(STATIC_SOUP)
-#if STATIC_SOUP == 2
+#if defined(LINK_SOUP)
+#if LINK_SOUP == 2
   if (uri->soup_uri) {
     soup_uri_free (uri->soup_uri);
   }
 #endif
-#else /* !STATIC_SOUP */
+#else /* !LINK_SOUP */
   if (uri->soup_uri) {
     g_assert (gst_soup_vtable._soup_uri_free_2 != NULL);
     gst_soup_vtable._soup_uri_free_2 (uri->soup_uri);
   }
-#endif /* STATIC_SOUP */
+#endif /* LINK_SOUP */
   g_free (uri);
 }
 
 char *
 gst_soup_uri_to_string (GstSoupUri * uri)
 {
-#if (defined(STATIC_SOUP) && STATIC_SOUP == 3) || (!defined(STATIC_SOUP) && GLIB_CHECK_VERSION(2, 66, 0))
+#if (defined(LINK_SOUP) && LINK_SOUP == 3) || (!defined(LINK_SOUP) && GLIB_CHECK_VERSION(2, 66, 0))
   if (uri->uri) {
     return g_uri_to_string_partial (uri->uri, G_URI_HIDE_PASSWORD);
   }
 #endif
 
-#if defined(STATIC_SOUP)
-#if STATIC_SOUP == 2
+#if defined(LINK_SOUP)
+#if LINK_SOUP == 2
   if (uri->soup_uri) {
     return soup_uri_to_string (uri->soup_uri, FALSE);
   }
 #endif
-#else /* !STATIC_SOUP */
+#else /* !LINK_SOUP */
   if (uri->soup_uri) {
     g_assert (gst_soup_vtable._soup_uri_to_string_2 != NULL);
     return gst_soup_vtable._soup_uri_to_string_2 (uri->soup_uri, FALSE);
   }
-#endif /* STATIC_SOUP */
+#endif /* LINK_SOUP */
 
   g_assert_not_reached ();
   return NULL;
@@ -455,12 +409,12 @@ gst_soup_uri_to_string (GstSoupUri * uri)
 char *
 gst_soup_message_uri_to_string (SoupMessage * msg)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   SoupURI *uri = NULL;
   uri = soup_message_get_uri (msg);
   return soup_uri_to_string (uri, FALSE);
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   GUri *uri = NULL;
   uri = soup_message_get_uri (msg);
   return g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD);
@@ -492,7 +446,7 @@ gst_soup_message_uri_to_string (SoupMessage * msg)
 guint
 _soup_get_major_version (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_get_major_version ();
 #else
   g_assert (gst_soup_vtable._soup_get_major_version != NULL);
@@ -503,7 +457,7 @@ _soup_get_major_version (void)
 guint
 _soup_get_minor_version (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_get_minor_version ();
 #else
   g_assert (gst_soup_vtable._soup_get_minor_version != NULL);
@@ -514,7 +468,7 @@ _soup_get_minor_version (void)
 guint
 _soup_get_micro_version (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_get_micro_version ();
 #else
   g_assert (gst_soup_vtable._soup_get_micro_version != NULL);
@@ -526,12 +480,12 @@ void
 _soup_message_set_request_body_from_bytes (SoupMessage * msg,
     const char *content_type, GBytes * bytes)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   gsize size;
   gconstpointer data = g_bytes_get_data (bytes, &size);
   soup_message_body_append (msg->request_body, SOUP_MEMORY_COPY, data, size);
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   soup_message_set_request_body_from_bytes (msg, content_type, bytes);
 #endif
 #else
@@ -554,7 +508,7 @@ _soup_message_set_request_body_from_bytes (SoupMessage * msg,
 GType
 _soup_session_get_type (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_session_get_type ();
 #else
   g_assert (gst_soup_vtable._soup_session_get_type != NULL);
@@ -565,7 +519,7 @@ _soup_session_get_type (void)
 GType
 _soup_logger_log_level_get_type (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_logger_log_level_get_type ();
 #else
   g_assert (gst_soup_vtable._soup_logger_log_level_get_type != NULL);
@@ -576,7 +530,7 @@ _soup_logger_log_level_get_type (void)
 GType
 _soup_content_decoder_get_type (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_content_decoder_get_type ();
 #else
   g_assert (gst_soup_vtable._soup_content_decoder_get_type != NULL);
@@ -587,7 +541,7 @@ _soup_content_decoder_get_type (void)
 GType
 _soup_cookie_jar_get_type (void)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_cookie_jar_get_type ();
 #else
   g_assert (gst_soup_vtable._soup_cookie_jar_get_type != NULL);
@@ -598,7 +552,7 @@ _soup_cookie_jar_get_type (void)
 void
 _soup_session_abort (SoupSession * session)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_session_abort (session);
 #else
   g_assert (gst_soup_vtable._soup_session_abort != NULL);
@@ -609,7 +563,7 @@ _soup_session_abort (SoupSession * session)
 SoupMessage *
 _soup_message_new (const char *method, const char *uri_string)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_message_new (method, uri_string);
 #else
   g_assert (gst_soup_vtable._soup_message_new != NULL);
@@ -620,10 +574,10 @@ _soup_message_new (const char *method, const char *uri_string)
 SoupMessageHeaders *
 _soup_message_get_request_headers (SoupMessage * msg)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   return msg->request_headers;
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   return soup_message_get_request_headers (msg);
 #endif
 #else
@@ -640,10 +594,10 @@ _soup_message_get_request_headers (SoupMessage * msg)
 SoupMessageHeaders *
 _soup_message_get_response_headers (SoupMessage * msg)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   return msg->response_headers;
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   return soup_message_get_response_headers (msg);
 #endif
 #else
@@ -660,7 +614,7 @@ _soup_message_get_response_headers (SoupMessage * msg)
 void
 _soup_message_headers_remove (SoupMessageHeaders * hdrs, const char *name)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_message_headers_remove (hdrs, name);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_remove != NULL);
@@ -672,7 +626,7 @@ void
 _soup_message_headers_append (SoupMessageHeaders * hdrs, const char *name,
     const char *value)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_message_headers_append (hdrs, name, value);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_append != NULL);
@@ -683,7 +637,7 @@ _soup_message_headers_append (SoupMessageHeaders * hdrs, const char *name,
 void
 _soup_message_set_flags (SoupMessage * msg, SoupMessageFlags flags)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_message_set_flags (msg, flags);
 #else
   g_assert (gst_soup_vtable._soup_message_set_flags != NULL);
@@ -694,7 +648,7 @@ _soup_message_set_flags (SoupMessage * msg, SoupMessageFlags flags)
 void
 _soup_session_add_feature_by_type (SoupSession * session, GType feature_type)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_session_add_feature_by_type (session, feature_type);
 #else
   g_assert (gst_soup_vtable._soup_session_add_feature_by_type != NULL);
@@ -706,7 +660,7 @@ void
 _soup_message_headers_foreach (SoupMessageHeaders * hdrs,
     SoupMessageHeadersForeachFunc func, gpointer user_data)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_message_headers_foreach (hdrs, func, user_data);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_foreach != NULL);
@@ -717,7 +671,7 @@ _soup_message_headers_foreach (SoupMessageHeaders * hdrs,
 SoupEncoding
 _soup_message_headers_get_encoding (SoupMessageHeaders * hdrs)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_message_headers_get_encoding (hdrs);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_get_encoding != NULL);
@@ -728,7 +682,7 @@ _soup_message_headers_get_encoding (SoupMessageHeaders * hdrs)
 goffset
 _soup_message_headers_get_content_length (SoupMessageHeaders * hdrs)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_message_headers_get_content_length (hdrs);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_get_content_length != NULL);
@@ -739,10 +693,10 @@ _soup_message_headers_get_content_length (SoupMessageHeaders * hdrs)
 SoupStatus
 _soup_message_get_status (SoupMessage * msg)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   return msg->status_code;
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   return soup_message_get_status (msg);
 #endif
 #else
@@ -759,10 +713,10 @@ _soup_message_get_status (SoupMessage * msg)
 const char *
 _soup_message_get_reason_phrase (SoupMessage * msg)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   return msg->reason_phrase;
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   return soup_message_get_reason_phrase (msg);
 #endif
 #else
@@ -779,7 +733,7 @@ _soup_message_get_reason_phrase (SoupMessage * msg)
 const char *
 _soup_message_headers_get_one (SoupMessageHeaders * hdrs, const char *name)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_message_headers_get_one (hdrs, name);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_get_one != NULL);
@@ -790,7 +744,7 @@ _soup_message_headers_get_one (SoupMessageHeaders * hdrs, const char *name)
 void
 _soup_message_disable_feature (SoupMessage * msg, GType feature_type)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_message_disable_feature (msg, feature_type);
 #else
   g_assert (gst_soup_vtable._soup_message_disable_feature != NULL);
@@ -802,7 +756,7 @@ const char *
 _soup_message_headers_get_content_type (SoupMessageHeaders * hdrs,
     GHashTable ** params)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_message_headers_get_content_type (hdrs, params);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_get_content_type != NULL);
@@ -814,7 +768,7 @@ gboolean
 _soup_message_headers_get_content_range (SoupMessageHeaders * hdrs,
     goffset * start, goffset * end, goffset * total_length)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_message_headers_get_content_range (hdrs, start, end,
       total_length);
 #else
@@ -828,7 +782,7 @@ void
 _soup_message_headers_set_range (SoupMessageHeaders * hdrs, goffset start,
     goffset end)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_message_headers_set_range (hdrs, start, end);
 #else
   g_assert (gst_soup_vtable._soup_message_headers_set_range != NULL);
@@ -840,7 +794,7 @@ void
 _soup_auth_authenticate (SoupAuth * auth, const char *username,
     const char *password)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   soup_auth_authenticate (auth, username, password);
 #else
   g_assert (gst_soup_vtable._soup_auth_authenticate != NULL);
@@ -851,10 +805,10 @@ _soup_auth_authenticate (SoupAuth * auth, const char *username,
 const char *
 _soup_message_get_method (SoupMessage * msg)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   return msg->method;
-#elif STATIC_SOUP == 3
+#elif LINK_SOUP == 3
   return soup_message_get_method (msg);
 #endif
 #else
@@ -873,8 +827,8 @@ _soup_session_send_async (SoupSession * session, SoupMessage * msg,
     GCancellable * cancellable, GAsyncReadyCallback callback,
     gpointer user_data)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 2
+#ifdef LINK_SOUP
+#if LINK_SOUP == 2
   soup_session_send_async (session, msg, cancellable, callback, user_data);
 #else
   soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, cancellable,
@@ -897,7 +851,7 @@ GInputStream *
 _soup_session_send_finish (SoupSession * session,
     GAsyncResult * result, GError ** error)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_session_send_finish (session, result, error);
 #else
   g_assert (gst_soup_vtable._soup_session_send_finish != NULL);
@@ -909,7 +863,7 @@ GInputStream *
 _soup_session_send (SoupSession * session, SoupMessage * msg,
     GCancellable * cancellable, GError ** error)
 {
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
   return soup_session_send (session, msg, cancellable, error);
 #else
   g_assert (gst_soup_vtable._soup_session_send != NULL);
@@ -921,8 +875,8 @@ void
 gst_soup_session_cancel_message (SoupSession * session, SoupMessage * msg,
     GCancellable * cancellable)
 {
-#ifdef STATIC_SOUP
-#if STATIC_SOUP == 3
+#ifdef LINK_SOUP
+#if LINK_SOUP == 3
   g_cancellable_cancel (cancellable);
 #else
   soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
@@ -937,3 +891,37 @@ gst_soup_session_cancel_message (SoupSession * session, SoupMessage * msg,
   }
 #endif
 }
+
+SoupCookie *
+_soup_cookie_parse (const char *header)
+{
+#ifdef LINK_SOUP
+  return soup_cookie_parse (header, NULL);
+#else
+  g_assert (gst_soup_vtable._soup_cookie_parse != NULL);
+  return gst_soup_vtable._soup_cookie_parse (header, NULL);
+#endif
+}
+
+
+void
+_soup_cookies_to_request (GSList * cookies, SoupMessage * msg)
+{
+#ifdef LINK_SOUP
+  soup_cookies_to_request (cookies, msg);
+#else
+  g_assert (gst_soup_vtable._soup_cookies_to_request != NULL);
+  gst_soup_vtable._soup_cookies_to_request (cookies, msg);
+#endif
+}
+
+void
+_soup_cookies_free (GSList * cookies)
+{
+#ifdef LINK_SOUP
+  soup_cookies_free (cookies);
+#else
+  g_assert (gst_soup_vtable._soup_cookies_free != NULL);
+  gst_soup_vtable._soup_cookies_free (cookies);
+#endif
+}
diff --git a/ext/soup/gstsouploader.h b/ext/soup/gstsouploader.h
index 278adff1ac8812a6c29a18445bb74b2bd3c5d559..ccc7445cb4c32297a007d0237ef5fe01a4c3e095 100644
--- a/ext/soup/gstsouploader.h
+++ b/ext/soup/gstsouploader.h
@@ -15,7 +15,7 @@
 #ifndef __GST_SOUP_LOADER_H__
 #define __GST_SOUP_LOADER_H__
 
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
 #include <libsoup/soup.h>
 #else
 #include "stub/soup.h"
@@ -66,6 +66,9 @@
 #define _soup_session_send_async _ad2_soup_session_send_async
 #define _soup_session_send_finish _ad2_soup_session_send_finish
 #define _soup_session_send _ad2_soup_session_send
+#define _soup_cookie_parse _ad2_soup_cookie_parse
+#define _soup_cookies_to_request _ad2_soup_cookies_to_request
+#define _soup_cookies_free _ad2_soup_cookies_free
 #define gst_soup_session_cancel_message ad2_gst_soup_session_cancel_message
 #endif
 
@@ -86,10 +89,10 @@ void _soup_session_add_feature (SoupSession *session,
 void _soup_session_add_feature_by_type (SoupSession *session, GType feature_type);
 
 typedef struct _GstSoupUri {
-#if (defined(STATIC_SOUP) && STATIC_SOUP == 3) || (!defined(STATIC_SOUP) && GLIB_CHECK_VERSION(2, 66, 0))
+#if (defined(LINK_SOUP) && LINK_SOUP == 3) || (!defined(LINK_SOUP) && GLIB_CHECK_VERSION(2, 66, 0))
   GUri *uri;
 #endif
-#if (defined(STATIC_SOUP) && STATIC_SOUP == 2) || !defined(STATIC_SOUP)
+#if (defined(LINK_SOUP) && LINK_SOUP == 2) || !defined(LINK_SOUP)
   SoupURI *soup_uri;
 #endif
 } GstSoupUri;
@@ -167,6 +170,11 @@ GInputStream *_soup_session_send (SoupSession *session, SoupMessage *msg,
 
 void gst_soup_session_cancel_message (SoupSession *session, SoupMessage *msg, GCancellable *cancellable);
 
+SoupCookie *_soup_cookie_parse (const char *header);
+void _soup_cookies_to_request (GSList *cookies, SoupMessage *msg);
+void _soup_cookies_free (GSList *cookies);
+
+
 G_END_DECLS
 
 #endif /* __GST_SOUP_LOADER_H__ */
diff --git a/ext/soup/gstsouputils.h b/ext/soup/gstsouputils.h
index 2ac84a8781730ab00b50b78700b318f8d940785f..6336e3dcb15d80a6f44a611e684100cf1fb53668 100644
--- a/ext/soup/gstsouputils.h
+++ b/ext/soup/gstsouputils.h
@@ -21,7 +21,7 @@
 #include <glib.h>
 #include <gst/gst.h>
 
-#ifdef STATIC_SOUP
+#ifdef LINK_SOUP
 #include <libsoup/soup.h>
 #else
 #include "stub/soup.h"
diff --git a/ext/soup/meson.build b/ext/soup/meson.build
index ea169bf31c364d79e24611d3b6ff7500cc03c252..aaa01dbcf630f50b529e5da302a2822c5d2fa151 100644
--- a/ext/soup/meson.build
+++ b/ext/soup/meson.build
@@ -8,36 +8,46 @@ soup_sources = [
 ]
 
 soup_opt = get_option('soup')
+soup_ver_opt = get_option('soup-version')
 if soup_opt.disabled()
   subdir_done()
 endif
 
 libdl_dep = cc.find_library('dl', required: false)
 
-static_args = []
-static_deps = []
+soup_link_args = []
+soup_link_deps = []
+libsoup2_dep = disabler()
+libsoup3_dep = disabler()
 default_library = get_option('default_library')
-if default_library in ['static', 'both']
-  libsoup2_dep = dependency('libsoup-2.4', version : '>=2.48',
-                            required : false, fallback : ['libsoup', 'libsoup_dep'],
-                            default_options: ['sysprof=disabled'])
-  libsoup3_dep = dependency('libsoup-3.0', required : false,
-                            fallback : ['libsoup3', 'libsoup_dep'])
-  if not libsoup2_dep.found() and not libsoup3_dep.found()
+soup_lookup_dep = get_option('soup-lookup-dep') and host_system == 'linux'
+if host_system != 'linux' or default_library in ['static', 'both'] or soup_lookup_dep
+  if soup_ver_opt in ['auto', '3']
+    libsoup3_dep = dependency('libsoup-3.0', allow_fallback: true,
+                              required: soup_ver_opt == '3' and soup_opt.enabled())
+  endif
+  if soup_ver_opt in ['auto', '2']
+    libsoup2_dep = dependency('libsoup-2.4', version : '>=2.48', allow_fallback: true,
+                              default_options: ['sysprof=disabled'],
+                              required: soup_ver_opt == '2' and soup_opt.enabled())
+  endif
+endif
+
+if host_system != 'linux' or default_library in ['static', 'both']
+  if libsoup3_dep.found()
+    soup_link_deps += libsoup3_dep
+    soup_link_args += '-DLINK_SOUP=3'
+    message('soup plugin: linking to libsoup-3.0')
+  elif libsoup2_dep.found()
+    soup_link_deps += libsoup2_dep
+    soup_link_args += '-DLINK_SOUP=2'
+    message('soup plugin: linking to libsoup-2.4')
+  else
     if soup_opt.enabled()
       error('Either libsoup2 or libsoup3 is needed')
     endif
     subdir_done()
   endif
-  if libsoup3_dep.found()
-    static_deps += libsoup3_dep
-    static_args += '-DSTATIC_SOUP=3'
-    message('soup plugin: using libsoup-3.0 for static build')
-  elif libsoup2_dep.found()
-    static_deps += libsoup2_dep
-    static_args += '-DSTATIC_SOUP=2'
-    message('soup plugin: using libsoup-2.4 for static build')
-  endif
 endif
 
 soup_library_kwargs = {
@@ -50,25 +60,35 @@ soup_library_kwargs = {
 soup_library_deps = [gst_dep, gstbase_dep, gsttag_dep, gmodule_dep, gio_dep, libdl_dep]
 soup_library_c_args = gst_plugins_good_args
 
-if default_library in ['shared', 'both']
-  gstsouphttpsrc_shared = shared_library('gstsoup',
-    c_args : soup_library_c_args,
-    dependencies : soup_library_deps,
-    kwargs: soup_library_kwargs,
-  )
-endif
-
-if default_library in ['static', 'both']
-  gstsouphttpsrc_static = static_library('gstsoup',
-    c_args : soup_library_c_args + static_args,
-    dependencies : soup_library_deps + static_deps,
+if host_system != 'linux'
+  gstsouphttpsrc = library('gstsoup',
+    c_args : soup_library_c_args + soup_link_args,
+    dependencies : soup_library_deps + soup_link_deps,
     kwargs: soup_library_kwargs,
   )
+  gstsouphttpsrc_shared = gstsouphttpsrc
+  gstsouphttpsrc_static = gstsouphttpsrc
+else
+  if default_library in ['static', 'both']
+    gstsouphttpsrc_static = static_library('gstsoup',
+      c_args : soup_library_c_args + soup_link_args,
+      dependencies : soup_library_deps + soup_link_deps,
+      kwargs: soup_library_kwargs,
+    )
+  endif
+  if default_library in ['shared', 'both']
+    gstsouphttpsrc_shared = shared_library('gstsoup',
+      c_args : soup_library_c_args,
+      dependencies : soup_library_deps,
+      kwargs: soup_library_kwargs,
+    )
+  endif
 endif
 
-# Use the static library to generate the .pc file if it's available. The shared
-# library .pc file does not have a Requires: on libsoup-2.4, and we use plugin
-# .pc files to generate dependencies for linking plugins statically.
+# Use the static library to generate the .pc file on Linux if it's available.
+# In that case, the shared library .pc file does not have a Requires: on
+# libsoup-2.4, and we use plugin .pc files to generate dependencies for linking
+# plugins statically.
 if default_library == 'shared'
   pkgconfig.generate(gstsouphttpsrc_shared, install_dir : plugins_pkgconfig_install_dir)
 else
diff --git a/ext/soup/stub/soup.h b/ext/soup/stub/soup.h
index 56ddd6acdfa711c695f61c634654356738914a64..1f1f5c59140d4efbabe662b536195a434d401741 100644
--- a/ext/soup/stub/soup.h
+++ b/ext/soup/stub/soup.h
@@ -209,6 +209,8 @@ static gpointer _SOUP_METHOD_POST;
 #define SOUP_METHOD_HEAD _SOUP_INTERN_METHOD(HEAD)
 #define SOUP_METHOD_POST _SOUP_INTERN_METHOD(POST)
 
+typedef struct _SoupCookie SoupCookie;
+
 G_END_DECLS
 
 #endif /* __GST_SOUP_STUB_H__ */
diff --git a/ext/taglib/meson.build b/ext/taglib/meson.build
index 7226aa3cf865ba5d199f77f75811337c0fba9e2d..cbfcb379923ef3888b6ace68603269a6a4adf2c3 100644
--- a/ext/taglib/meson.build
+++ b/ext/taglib/meson.build
@@ -25,6 +25,7 @@ if taglib_dep.found() and add_languages('cpp', native: false, required: get_opti
     dependencies : [gsttag_dep, taglib_dep],
     install : true,
     install_dir : plugins_install_dir,
+    override_options : ['cpp_std=c++17'],
   )
   plugins += [gsttaglib]
 endif
diff --git a/ext/vpx/gstvp8enc.c b/ext/vpx/gstvp8enc.c
index 93ef43e83b700abeae1f97fe5b7a68cf7cd295c6..f93a23c791ca25b03cecfd0363270d885559ba9b 100644
--- a/ext/vpx/gstvp8enc.c
+++ b/ext/vpx/gstvp8enc.c
@@ -89,11 +89,11 @@ static void
 gst_vp8_enc_user_data_free (GstVP8EncUserData * user_data)
 {
   if (user_data->image)
-    g_slice_free (vpx_image_t, user_data->image);
+    g_free (user_data->image);
 
   g_list_foreach (user_data->invisible, (GFunc) _gst_mini_object_unref0, NULL);
   g_list_free (user_data->invisible);
-  g_slice_free (GstVP8EncUserData, user_data);
+  g_free (user_data);
 }
 
 static vpx_codec_iface_t *gst_vp8_enc_get_algo (GstVPXEnc * enc);
@@ -317,7 +317,7 @@ gst_vp8_enc_process_frame_user_data (GstVPXEnc * enc,
   }
 
   if (user_data->image)
-    g_slice_free (vpx_image_t, user_data->image);
+    g_free (user_data->image);
   user_data->image = NULL;
   return user_data;
 }
@@ -343,7 +343,7 @@ gst_vp8_enc_set_frame_user_data (GstVPXEnc * enc, GstVideoCodecFrame * frame,
     vpx_image_t * image)
 {
   GstVP8EncUserData *user_data;
-  user_data = g_slice_new0 (GstVP8EncUserData);
+  user_data = g_new0 (GstVP8EncUserData, 1);
   user_data->image = image;
   gst_video_codec_frame_set_user_data (frame, user_data,
       (GDestroyNotify) gst_vp8_enc_user_data_free);
@@ -402,9 +402,8 @@ gst_vp8_enc_preflight_buffer (GstVPXEnc * enc,
     gboolean layer_sync, guint layer_id, guint8 tl0picidx)
 {
   GstCustomMeta *meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
-  GstStructure *s = gst_custom_meta_get_structure (meta);
 
-  gst_structure_set (s,
+  gst_structure_set (meta->structure,
       "use-temporal-scaling", G_TYPE_BOOLEAN, (enc->cfg.ts_periodicity != 0),
       "layer-sync", G_TYPE_BOOLEAN, layer_sync,
       "layer-id", G_TYPE_UINT, layer_id,
diff --git a/ext/vpx/gstvp9enc.c b/ext/vpx/gstvp9enc.c
index eb75fa1d0c8633ab5ba461309acf16995fa3698c..e1bdc5a444d5d7de0e181f2f31a02def906e00cc 100644
--- a/ext/vpx/gstvp9enc.c
+++ b/ext/vpx/gstvp9enc.c
@@ -257,8 +257,9 @@ gst_vp9_enc_init (GstVP9Enc * gst_vp9_enc)
       &gst_vpx_enc->cfg, 0);
   if (status != VPX_CODEC_OK) {
     GST_ERROR_OBJECT (gst_vpx_enc,
-        "Failed to get default encoder configuration: %s",
-        gst_vpx_error_name (status));
+        "Failed to get default encoder configuration: %s (details: %s)",
+        gst_vpx_error_name (status),
+        GST_STR_NULL (gst_vpx_enc->encoder.err_detail));
     gst_vpx_enc->have_default_config = FALSE;
   } else {
     gst_vpx_enc->have_default_config = TRUE;
@@ -290,9 +291,8 @@ gst_vp9_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP9E_SET_TILE_COLUMNS,
             gst_vp9_enc->tile_columns);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP9E_SET_TILE_COLUMNS: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP9E_SET_TILE_COLUMNS",
+              status);
         }
       }
       break;
@@ -303,9 +303,8 @@ gst_vp9_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP9E_SET_TILE_ROWS,
             gst_vp9_enc->tile_rows);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP9E_SET_TILE_ROWS: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP9E_SET_TILE_ROWS",
+              status);
         }
       }
       break;
@@ -316,8 +315,8 @@ gst_vp9_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP9E_SET_ROW_MT,
             gst_vp9_enc->row_mt ? 1 : 0);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP9E_SET_ROW_MT: %s", gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP9E_SET_ROW_MT",
+              status);
         }
       }
       break;
@@ -327,9 +326,8 @@ gst_vp9_enc_set_property (GObject * object, guint prop_id,
         status = vpx_codec_control (&gst_vpx_enc->encoder, VP9E_SET_AQ_MODE,
             gst_vp9_enc->aq_mode);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP9E_SET_AQ_MODE: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP9E_SET_AQ_MODE",
+              status);
         }
       }
       break;
@@ -340,9 +338,8 @@ gst_vp9_enc_set_property (GObject * object, guint prop_id,
             VP9E_SET_FRAME_PARALLEL_DECODING,
             gst_vp9_enc->frame_parallel_decoding ? 1 : 0);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP9E_SET_FRAME_PARALLEL_DECODING: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP9E_SET_FRAME_PARALLEL_DECODING", status);
         }
       }
       break;
@@ -468,52 +465,45 @@ gst_vp9_enc_configure_encoder (GstVPXEnc * encoder, GstVideoCodecState * state)
       gst_vp9_get_vpx_colorspace (encoder, &GST_VIDEO_INFO_COLORIMETRY (info),
           GST_VIDEO_INFO_FORMAT (info)));
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP9E_SET_COLOR_SPACE: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_COLOR_SPACE", status);
   }
 
   status = vpx_codec_control (&encoder->encoder, VP9E_SET_COLOR_RANGE,
       gst_vp9_get_vpx_color_range (&GST_VIDEO_INFO_COLORIMETRY (info)));
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP9E_SET_COLOR_RANGE: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_COLOR_RANGE", status);
   }
 
   status =
       vpx_codec_control (&encoder->encoder, VP9E_SET_TILE_COLUMNS,
       vp9enc->tile_columns);
   if (status != VPX_CODEC_OK) {
-    GST_DEBUG_OBJECT (encoder, "Failed to set VP9E_SET_TILE_COLUMNS: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_TILE_COLUMNS", status);
   }
 
   status =
       vpx_codec_control (&encoder->encoder, VP9E_SET_TILE_ROWS,
       vp9enc->tile_rows);
   if (status != VPX_CODEC_OK) {
-    GST_DEBUG_OBJECT (encoder, "Failed to set VP9E_SET_TILE_ROWS: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_TILE_ROWS", status);
   }
   status =
       vpx_codec_control (&encoder->encoder, VP9E_SET_ROW_MT,
       vp9enc->row_mt ? 1 : 0);
   if (status != VPX_CODEC_OK) {
-    GST_DEBUG_OBJECT (encoder,
-        "Failed to set VP9E_SET_ROW_MT: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_ROW_MT", status);
   }
   status =
       vpx_codec_control (&encoder->encoder, VP9E_SET_AQ_MODE, vp9enc->aq_mode);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP9E_SET_AQ_MODE: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_AQ_MODE", status);
   }
   status =
       vpx_codec_control (&encoder->encoder, VP9E_SET_FRAME_PARALLEL_DECODING,
       vp9enc->frame_parallel_decoding ? 1 : 0);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP9E_SET_FRAME_PARALLEL_DECODING: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP9E_SET_FRAME_PARALLEL_DECODING",
+        status);
   }
 
   return TRUE;
@@ -643,7 +633,7 @@ gst_vp9_enc_handle_invisible_frame_buffer (GstVPXEnc * enc, void *user_data,
 static void
 gst_vp9_enc_user_data_free (vpx_image_t * image)
 {
-  g_slice_free (vpx_image_t, image);
+  g_free (image);
 }
 
 static void
diff --git a/ext/vpx/gstvpxcompat.h b/ext/vpx/gstvpxcompat.h
index 1919791a13b388a0789c1f57cc7d0eba0e6d9119..c132d8c36b67586413dcfdc4c00b822ec16b1824 100644
--- a/ext/vpx/gstvpxcompat.h
+++ b/ext/vpx/gstvpxcompat.h
@@ -44,4 +44,20 @@ typedef enum gst_vpx_img_fmt
   GST_VPX_IMG_FMT_I44016 = GST_VPX_IMG_FMT_I440 | GST_VPX_IMG_FMT_HIGHBITDEPTH
 } gst_vpx_img_fmt_t;
 
+#define GST_VPX_ENC_WARN(element, message, status)                      \
+  G_STMT_START {                                                        \
+    GST_WARNING_OBJECT(element, "%s: %s (details: %s)", #message,       \
+                       gst_vpx_error_name(status),                      \
+                       GST_STR_NULL(element->encoder.err_detail));      \
+  }                                                                     \
+  G_STMT_END
+
+#define GST_VPX_DEC_WARN(element, message, status)                      \
+  G_STMT_START {                                                        \
+    GST_WARNING_OBJECT(element, "%s: %s (details: %s)", #message,       \
+                       gst_vpx_error_name(status),                      \
+                       GST_STR_NULL(element->decoder.err_detail));      \
+  }                                                                     \
+  G_STMT_END
+
 G_END_DECLS
diff --git a/ext/vpx/gstvpxdec.c b/ext/vpx/gstvpxdec.c
index d5351b839a4b27fe4121fecb50c4630d620651bd..5e12dfef991872909723aba39cd64b224fdad160 100644
--- a/ext/vpx/gstvpxdec.c
+++ b/ext/vpx/gstvpxdec.c
@@ -600,8 +600,7 @@ gst_vpx_dec_open_codec (GstVPXDec * dec, GstVideoCodecFrame * frame)
   gst_buffer_unmap (frame->input_buffer, &minfo);
 
   if (status != VPX_CODEC_OK) {
-    GST_INFO_OBJECT (dec, "VPX preprocessing error: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_DEC_WARN (dec, "VPX preprocessing error", status);
     return GST_FLOW_CUSTOM_SUCCESS_1;
   }
 
@@ -639,9 +638,12 @@ gst_vpx_dec_open_codec (GstVPXDec * dec, GstVideoCodecFrame * frame)
   status =
       vpx_codec_dec_init (&dec->decoder, vpxclass->codec_algo, &cfg, flags);
   if (status != VPX_CODEC_OK) {
-    GST_ELEMENT_ERROR (dec, LIBRARY, INIT,
-        ("Failed to initialize VP8 decoder"), ("%s",
-            gst_vpx_error_name (status)));
+    GST_ELEMENT_ERROR_WITH_DETAILS (dec, LIBRARY, INIT,
+        ("Failed to initialize VP8 decoder"), ("%s (details: %s)",
+            gst_vpx_error_name (status),
+            GST_STR_NULL (dec->decoder.err_detail)), ("error", G_TYPE_STRING,
+            gst_vpx_error_name (status), "details", G_TYPE_STRING,
+            GST_STR_NULL (dec->decoder.err_detail), NULL));
     return GST_FLOW_ERROR;
   }
 
@@ -654,8 +656,7 @@ gst_vpx_dec_open_codec (GstVPXDec * dec, GstVideoCodecFrame * frame)
 
     status = vpx_codec_control (&dec->decoder, VP8_SET_POSTPROC, &pp_cfg);
     if (status != VPX_CODEC_OK) {
-      GST_WARNING_OBJECT (dec, "Couldn't set postprocessing settings: %s",
-          gst_vpx_error_name (status));
+      GST_VPX_DEC_WARN (dec, "Couldn't set postprocessing settings", status);
     }
   }
   vpx_codec_set_frame_buffer_functions (&dec->decoder,
@@ -725,8 +726,10 @@ gst_vpx_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
   if (status) {
     GstVideoDecoderRequestSyncPointFlags flags = 0;
 
-    GST_VIDEO_DECODER_ERROR (decoder, 1, LIBRARY, ENCODE,
-        ("Failed to decode frame"), ("%s", gst_vpx_error_name (status)), ret);
+    GST_VIDEO_DECODER_ERROR (decoder, 1, STREAM, DECODE,
+        ("Failed to decode frame"), ("%s (details: %s)",
+            gst_vpx_error_name (status),
+            GST_STR_NULL (dec->decoder.err_detail)), ret);
 
     if (gst_video_decoder_get_needs_sync_point (decoder))
       flags |= GST_VIDEO_DECODER_REQUEST_SYNC_POINT_DISCARD_INPUT;
@@ -740,7 +743,7 @@ gst_vpx_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
   if (img) {
     if (vpxclass->get_frame_format (dec, img, &fmt) == FALSE) {
       vpx_img_free (img);
-      GST_ELEMENT_ERROR (decoder, LIBRARY, ENCODE,
+      GST_ELEMENT_ERROR (decoder, STREAM, DECODE,
           ("Failed to decode frame"), ("Unsupported color format %d",
               img->fmt));
       gst_video_codec_frame_unref (frame);
diff --git a/ext/vpx/gstvpxelement.c b/ext/vpx/gstvpxelement.c
index 2550283bf6d595a96ae6a99527abd55b2501c30f..b146dd4b9ab4de09dec4ecf2d3356c565186557b 100644
--- a/ext/vpx/gstvpxelement.c
+++ b/ext/vpx/gstvpxelement.c
@@ -31,9 +31,8 @@ void
 vpx_element_init (GstPlugin * plugin)
 {
   static gsize res = FALSE;
-  static const gchar *tags[] = { NULL };
   if (g_once_init_enter (&res)) {
-    gst_meta_register_custom ("GstVP8Meta", tags, NULL, NULL, NULL);
+    gst_meta_register_custom_simple ("GstVP8Meta");
     g_once_init_leave (&res, TRUE);
   }
 }
diff --git a/ext/vpx/gstvpxenc.c b/ext/vpx/gstvpxenc.c
index 612a7a59f146697b815e3fc5ce5d2cfe7af8624e..1d4de0a04373a16d1af799239e7145a47e2887e8 100644
--- a/ext/vpx/gstvpxenc.c
+++ b/ext/vpx/gstvpxenc.c
@@ -915,7 +915,6 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
   g_return_if_fail (GST_IS_VPX_ENC (object));
   gst_vpx_enc = GST_VPX_ENC (object);
 
-  GST_DEBUG_OBJECT (object, "gst_vpx_enc_set_property");
   g_mutex_lock (&gst_vpx_enc->encoder_lock);
   switch (prop_id) {
     case PROP_RC_END_USAGE:
@@ -1141,9 +1140,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
         status =
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_SCALEMODE, &sm);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_SCALEMODE: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_SCALEMODE",
+              status);
         }
       }
       break;
@@ -1158,9 +1156,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
         status =
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_SCALEMODE, &sm);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_SCALEMODE: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_SCALEMODE",
+              status);
         }
       }
       break;
@@ -1171,8 +1168,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_CPUUSED,
             gst_vpx_enc->cpu_used);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc, "Failed to set VP8E_SET_CPUUSED: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_CPUUSED",
+              status);
         }
       }
       break;
@@ -1183,9 +1180,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_ENABLEAUTOALTREF,
             (gst_vpx_enc->enable_auto_alt_ref ? 1 : 0));
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_ENABLEAUTOALTREF: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP8E_SET_ENABLEAUTOALTREF", status);
         }
       }
       break;
@@ -1196,9 +1192,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder,
             VP8E_SET_NOISE_SENSITIVITY, gst_vpx_enc->noise_sensitivity);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_NOISE_SENSITIVITY: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP8E_SET_NOISE_SENSITIVITY", status);
         }
       }
       break;
@@ -1208,9 +1203,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
         status = vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_SHARPNESS,
             gst_vpx_enc->sharpness);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_SHARPNESS: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_SHARPNESS",
+              status);
         }
       }
       break;
@@ -1221,9 +1215,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_STATIC_THRESHOLD,
             gst_vpx_enc->static_threshold);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_STATIC_THRESHOLD: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP8E_SET_STATIC_THRESHOLD", status);
         }
       }
       break;
@@ -1234,9 +1227,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_TOKEN_PARTITIONS,
             gst_vpx_enc->token_partitions);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_TOKEN_PARTIONS: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP8E_SET_TOKEN_PARTIONS", status);
         }
       }
       break;
@@ -1247,9 +1239,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_ARNR_MAXFRAMES,
             gst_vpx_enc->arnr_maxframes);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_ARNR_MAXFRAMES: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP8E_SET_ARNR_MAXFRAMES", status);
         }
       }
       break;
@@ -1260,9 +1251,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_ARNR_STRENGTH,
             gst_vpx_enc->arnr_strength);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_ARNR_STRENGTH: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_ARNR_STRENGTH",
+              status);
         }
       }
       break;
@@ -1277,8 +1267,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
         status = vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_TUNING,
             gst_vpx_enc->tuning);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_TUNING: %s", gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_TUNING",
+              status);
         }
       }
       break;
@@ -1288,9 +1278,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
         status = vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_CQ_LEVEL,
             gst_vpx_enc->cq_level);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_CQ_LEVEL: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc, "Failed to set VP8E_SET_CQ_LEVEL",
+              status);
         }
       }
       break;
@@ -1301,9 +1290,8 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
             vpx_codec_control (&gst_vpx_enc->encoder,
             VP8E_SET_MAX_INTRA_BITRATE_PCT, gst_vpx_enc->max_intra_bitrate_pct);
         if (status != VPX_CODEC_OK) {
-          GST_WARNING_OBJECT (gst_vpx_enc,
-              "Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT: %s",
-              gst_vpx_error_name (status));
+          GST_VPX_ENC_WARN (gst_vpx_enc,
+              "Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT", status);
         }
       }
       break;
@@ -1327,9 +1315,12 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
         vpx_codec_enc_config_set (&gst_vpx_enc->encoder, &gst_vpx_enc->cfg);
     if (status != VPX_CODEC_OK) {
       g_mutex_unlock (&gst_vpx_enc->encoder_lock);
-      GST_ELEMENT_ERROR (gst_vpx_enc, LIBRARY, INIT,
-          ("Failed to set encoder configuration"), ("%s",
-              gst_vpx_error_name (status)));
+      GST_ELEMENT_ERROR_WITH_DETAILS (gst_vpx_enc, LIBRARY, INIT,
+          ("Failed to set encoder configuration"), ("%s : %s",
+              gst_vpx_error_name (status),
+              GST_STR_NULL (gst_vpx_enc->encoder.err_detail)), ("details",
+              G_TYPE_STRING, GST_STR_NULL (gst_vpx_enc->encoder.err_detail),
+              NULL));
     } else {
       g_mutex_unlock (&gst_vpx_enc->encoder_lock);
     }
@@ -1743,10 +1734,10 @@ gst_vpx_enc_set_format (GstVideoEncoder * video_encoder,
     vpx_codec_destroy (&encoder->encoder);
     encoder->inited = FALSE;
     encoder->multipass_cache_idx++;
-    encoder->last_pts = GST_CLOCK_TIME_NONE;
-    encoder->last_input_duration = GST_CLOCK_TIME_NONE;
   } else {
     g_mutex_lock (&encoder->encoder_lock);
+    encoder->last_pts = GST_CLOCK_TIME_NONE;
+    encoder->last_input_duration = GST_CLOCK_TIME_NONE;
   }
 
   encoder->cfg.g_bit_depth = encoder->cfg.g_input_bit_depth = info->finfo->bits;
@@ -1829,8 +1820,11 @@ gst_vpx_enc_set_format (GstVideoEncoder * video_encoder,
       vpx_codec_enc_init (&encoder->encoder, vpx_enc_class->get_algo (encoder),
       &encoder->cfg, flags);
   if (status != VPX_CODEC_OK) {
-    GST_ELEMENT_ERROR (encoder, LIBRARY, INIT,
-        ("Failed to initialize encoder"), ("%s", gst_vpx_error_name (status)));
+    GST_ELEMENT_ERROR_WITH_DETAILS (encoder, LIBRARY, INIT,
+        ("Failed to initialize encoder"), ("%s : %s",
+            gst_vpx_error_name (status),
+            GST_STR_NULL (encoder->encoder.err_detail)), ("details",
+            G_TYPE_STRING, GST_STR_NULL (encoder->encoder.err_detail), NULL));
     g_mutex_unlock (&encoder->encoder_lock);
     return FALSE;
   }
@@ -1843,8 +1837,7 @@ gst_vpx_enc_set_format (GstVideoEncoder * video_encoder,
 
     status = vpx_codec_control (&encoder->encoder, VP8E_SET_SCALEMODE, &sm);
     if (status != VPX_CODEC_OK) {
-      GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_SCALEMODE: %s",
-          gst_vpx_error_name (status));
+      GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_SCALEMODE", status);
     }
   }
 
@@ -1852,77 +1845,63 @@ gst_vpx_enc_set_format (GstVideoEncoder * video_encoder,
       vpx_codec_control (&encoder->encoder, VP8E_SET_CPUUSED,
       encoder->cpu_used);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_CPUUSED: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_CPUUSED", status);
   }
 
   status =
       vpx_codec_control (&encoder->encoder, VP8E_SET_ENABLEAUTOALTREF,
       (encoder->enable_auto_alt_ref ? 1 : 0));
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_ENABLEAUTOALTREF: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_ENABLEAUTOALTREF",
+        status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_NOISE_SENSITIVITY,
       encoder->noise_sensitivity);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_NOISE_SENSITIVITY: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_NOISE_SENSITIVITY",
+        status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_SHARPNESS,
       encoder->sharpness);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_SHARPNESS: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_SHARPNESS", status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_STATIC_THRESHOLD,
       encoder->static_threshold);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_STATIC_THRESHOLD: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_STATIC_THRESHOLD",
+        status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_TOKEN_PARTITIONS,
       encoder->token_partitions);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_TOKEN_PARTIONS: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_TOKEN_PARTIONS", status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_MAXFRAMES,
       encoder->arnr_maxframes);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_ARNR_MAXFRAMES: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_ARNR_MAXFRAMES", status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_STRENGTH,
       encoder->arnr_strength);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_ARNR_STRENGTH: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_ARNR_STRENGTH", status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_TUNING,
       encoder->tuning);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_TUNING: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_TUNING", status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_CQ_LEVEL,
       encoder->cq_level);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_CQ_LEVEL: %s", gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_CQ_LEVEL", status);
   }
   status = vpx_codec_control (&encoder->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT,
       encoder->max_intra_bitrate_pct);
   if (status != VPX_CODEC_OK) {
-    GST_WARNING_OBJECT (encoder,
-        "Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT: %s",
-        gst_vpx_error_name (status));
+    GST_VPX_ENC_WARN (encoder, "Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT",
+        status);
   }
 
   if (vpx_enc_class->configure_encoder
@@ -2159,8 +2138,9 @@ gst_vpx_enc_drain (GstVideoEncoder * video_encoder)
   g_mutex_unlock (&encoder->encoder_lock);
 
   if (status != 0) {
-    GST_ERROR_OBJECT (encoder, "encode returned %d %s", status,
-        gst_vpx_error_name (status));
+    GST_ERROR_OBJECT (encoder, "encode returned %d %s (details: %s)", status,
+        gst_vpx_error_name (status),
+        GST_STR_NULL (encoder->encoder.err_detail));
     return GST_FLOW_ERROR;
   }
 
@@ -2225,7 +2205,7 @@ gst_vpx_enc_finish (GstVideoEncoder * video_encoder)
 static vpx_image_t *
 gst_vpx_enc_buffer_to_image (GstVPXEnc * enc, GstVideoFrame * frame)
 {
-  vpx_image_t *image = g_slice_new (vpx_image_t);
+  vpx_image_t *image = g_new (vpx_image_t, 1);
 
   memcpy (image, &enc->image, sizeof (*image));
 
@@ -2342,8 +2322,10 @@ gst_vpx_enc_handle_frame (GstVideoEncoder * video_encoder,
   gst_video_frame_unmap (&vframe);
 
   if (status != 0) {
-    GST_ELEMENT_ERROR (encoder, LIBRARY, ENCODE,
-        ("Failed to encode frame"), ("%s", gst_vpx_error_name (status)));
+    GST_ELEMENT_ERROR_WITH_DETAILS (encoder, LIBRARY, ENCODE,
+        ("Failed to encode frame"), ("%s : %s", gst_vpx_error_name (status),
+            GST_STR_NULL (encoder->encoder.err_detail)), ("details",
+            G_TYPE_STRING, GST_STR_NULL (encoder->encoder.err_detail), NULL));
     gst_video_codec_frame_set_user_data (frame, NULL, NULL);
     gst_video_codec_frame_unref (frame);
 
diff --git a/ext/vpx/meson.build b/ext/vpx/meson.build
index e007cafc06db952420d9ec98514cc9375311fd15..9b8b34c67e37395aa9c9161be249f565af4b320b 100644
--- a/ext/vpx/meson.build
+++ b/ext/vpx/meson.build
@@ -18,7 +18,7 @@ vpx_features = [
 ]
 
 vpx_option = get_option('vpx')
-vpx_dep = dependency('vpx', version : '>=1.7.0', required : vpx_option)
+vpx_dep = dependency('vpx', version : '>=1.7.0', required : vpx_option, allow_fallback: true)
 
 if vpx_dep.found()
   vpx_args = []
@@ -31,13 +31,27 @@ if vpx_dep.found()
                      const vpx_codec_iface_t *c = &@1@;
                      return c != 0;
                    }'''.format(header,codec_iface)
-    if cc.links(link_code, dependencies : vpx_dep)
-      vpx_args += extra_define
-      message('libvpx provides @0@ interface (@1@)'.format(f.get(3),f.get(1)))
-      have_vpx_feature = true
+    # Link-time checks are only compatible with externally supplied libraries 
+    if vpx_dep.type_name() == 'internal'
+      vpx_codec = vpx_dep.get_variable(f.get(3).split(' ')[0].to_lower())
+      vpx_feature = vpx_dep.get_variable(f.get(3).to_lower().underscorify())
+      if vpx_feature == 'true' and vpx_codec == 'true'
+        vpx_args += extra_define
+        message('libvpx provides @0@ interface (@1@)'.format(f.get(3),f.get(1)))
+        have_vpx_feature = true
+      else
+        message('libvpx does not provide @0@ interface (@1@)'.format(f.get(3),f.get(1)))
+        have_vpx_feature = false
+      endif
     else
-      message('libvpx does not provide @0@ interface (@1@)'.format(f.get(3),f.get(1)))
-      have_vpx_feature = false
+      if cc.links(link_code, dependencies : vpx_dep)
+        vpx_args += extra_define
+        message('libvpx provides @0@ interface (@1@)'.format(f.get(3),f.get(1)))
+        have_vpx_feature = true
+      else
+        message('libvpx does not provide @0@ interface (@1@)'.format(f.get(3),f.get(1)))
+        have_vpx_feature = false
+      endif
     endif
     set_variable('have_' + f.get(3).to_lower().underscorify(), have_vpx_feature)
   endforeach
diff --git a/ext/vpx/plugin.c b/ext/vpx/plugin.c
index 1887aee70e27ac052cef866b698a5a7c1b9fc897..2c1bc3220cc79857a2faa75939fa8bbb86be1dcd 100644
--- a/ext/vpx/plugin.c
+++ b/ext/vpx/plugin.c
@@ -54,5 +54,5 @@ plugin_init (GstPlugin * plugin)
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
     vpx,
-    "VP8 plugin",
+    "VP8/VP9 video encoding and decoding based on libvpx",
     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/wavpack/gstwavpackcommon.c b/ext/wavpack/gstwavpackcommon.c
index a17c9b2669d6089dcddfc5fd8ef77fd69b72a8de..35a2aabf2ea47a1a59c39b6e44b7df77ba4e4459 100644
--- a/ext/wavpack/gstwavpackcommon.c
+++ b/ext/wavpack/gstwavpackcommon.c
@@ -139,24 +139,24 @@ static const struct
   const GstAudioChannelPosition gst_pos;
 } layout_mapping[] = {
   {
-  0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
-  0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
-  0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
-  0x00008, GST_AUDIO_CHANNEL_POSITION_LFE1}, {
-  0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
-  0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
-  0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
-  0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
-  0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
-  0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
-  0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
-  0x00800, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, {
-  0x01000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
-  0x02000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
-  0x04000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
-  0x08000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, {
-  0x10000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, {
-  0x20000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
+      0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
+      0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
+      0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
+      0x00008, GST_AUDIO_CHANNEL_POSITION_LFE1}, {
+      0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
+      0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
+      0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
+      0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
+      0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
+      0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
+      0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
+      0x00800, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, {
+      0x01000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
+      0x02000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
+      0x04000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
+      0x08000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, {
+      0x10000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, {
+      0x20000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
 };
 
 #define MAX_CHANNEL_POSITIONS G_N_ELEMENTS (layout_mapping)
diff --git a/gst-libs/gst/glib-compat-private.h b/gst-libs/gst/glib-compat-private.h
index 8f37de205ec3c197271174852eb12181ce8f87e6..3d8885100cff176c17c56bc92f266db98eefdcc8 100644
--- a/gst-libs/gst/glib-compat-private.h
+++ b/gst-libs/gst/glib-compat-private.h
@@ -29,6 +29,53 @@ G_BEGIN_DECLS
 
 /* copies */
 
+#if !GLIB_CHECK_VERSION(2,68,0)
+#ifndef g_string_replace
+#define g_string_replace gst_g_string_replace
+#endif
+
+static inline guint
+gst_g_string_replace (GString     *string,
+                      const gchar *find,
+                      const gchar *replace,
+                      guint        limit)
+{
+  gsize f_len, r_len, pos;
+  gchar *cur, *next;
+  guint n = 0;
+
+  g_return_val_if_fail (string != NULL, 0);
+  g_return_val_if_fail (find != NULL, 0);
+  g_return_val_if_fail (replace != NULL, 0);
+
+  f_len = strlen (find);
+  r_len = strlen (replace);
+  cur = string->str;
+
+  while ((next = strstr (cur, find)) != NULL)
+    {
+      pos = next - string->str;
+      g_string_erase (string, pos, f_len);
+      g_string_insert (string, pos, replace);
+      cur = string->str + pos + r_len;
+      n++;
+      /* Only match the empty string once at any given position, to
+       * avoid infinite loops */
+      if (f_len == 0)
+        {
+          if (cur[0] == '\0')
+            break;
+          else
+            cur++;
+        }
+      if (n == limit)
+        break;
+    }
+
+  return n;
+}
+#endif /* GLIB_CHECK_VERSION */
+
 /* adaptations */
 
 G_END_DECLS
diff --git a/gst-plugins-good.doap b/gst-plugins-good.doap
index 6f42463333ece58c4128b0159a45582bf74a0471..2d5c19ef2c480e45ddfa669550e71ad5c91f32e1 100644
--- a/gst-plugins-good.doap
+++ b/gst-plugins-good.doap
@@ -32,6 +32,166 @@ the plug-in code, LGPL or LGPL-compatible for the supporting library).
    </GitRepository>
  </repository>
 
+ <release>
+  <Version>
+   <revision>1.24.12</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2025-01-29</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.12.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.11</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2025-01-06</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.11.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.10</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-12-03</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.10.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.9</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-10-30</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.9.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.8</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-09-19</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.8.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.7</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-08-21</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.7.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.6</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-07-29</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.6.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.5</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-06-20</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.5.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.4</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-05-29</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.4.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.3</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-04-30</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.3.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.2</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-04-09</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.2.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.1</revision>
+   <branch>1.24</branch>
+   <name></name>
+   <created>2024-03-21</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.1.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.24.0</revision>
+   <branch>main</branch>
+   <name></name>
+   <created>2024-03-04</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.24.0.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.23.90</revision>
+   <branch>main</branch>
+   <name></name>
+   <created>2024-02-23</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.23.90.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.23.2</revision>
+   <branch>main</branch>
+   <name></name>
+   <created>2024-02-15</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.23.2.tar.xz" />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
+   <revision>1.23.1</revision>
+   <branch>main</branch>
+   <name></name>
+   <created>2024-02-06</created>
+   <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.23.1.tar.xz" />
+  </Version>
+ </release>
+
  <release>
   <Version>
    <revision>1.22.0</revision>
diff --git a/gst/alpha/gstalpha.c b/gst/alpha/gstalpha.c
index bdd11ca059e2e9349b9b75fdab4484320bd24c99..e9da0a4660ca2949dc4bb45f2e45900f37dde2bd 100644
--- a/gst/alpha/gstalpha.c
+++ b/gst/alpha/gstalpha.c
@@ -271,8 +271,8 @@ gst_alpha_class_init (GstAlphaClass * klass)
   gst_element_class_set_static_metadata (gstelement_class, "Alpha filter",
       "Filter/Effect/Video",
       "Adds an alpha channel to video - uniform or via chroma-keying",
-      "Wim Taymans <wim.taymans@gmail.com>\n"
-      "Edward Hervey <edward.hervey@collabora.co.uk>\n"
+      "Wim Taymans <wim.taymans@gmail.com>, "
+      "Edward Hervey <edward.hervey@collabora.co.uk>, "
       "Jan Schmidt <thaytan@noraisin.net>");
 
   gst_element_class_add_static_pad_template (gstelement_class,
@@ -375,7 +375,7 @@ gst_alpha_set_property (GObject * object, guint prop_id,
     case PROP_PREFER_PASSTHROUGH:{
       gboolean prefer_passthrough = g_value_get_boolean (value);
 
-      reconfigure = ((! !prefer_passthrough) != (! !alpha->prefer_passthrough))
+      reconfigure = ((!!prefer_passthrough) != (!!alpha->prefer_passthrough))
           && (alpha->method == ALPHA_METHOD_SET) && (alpha->alpha == 1.0);
       alpha->prefer_passthrough = prefer_passthrough;
       break;
diff --git a/gst/apetag/gstapedemux.c b/gst/apetag/gstapedemux.c
index a357f84a9cb927f77044345d4f2aeeb18ca53ade..edd2aad02be679b6295096475d3d30eb1afd2f24 100644
--- a/gst/apetag/gstapedemux.c
+++ b/gst/apetag/gstapedemux.c
@@ -118,27 +118,27 @@ static const struct _GstApeDemuxTagTableEntry
   const gchar *gst_tag;
 } tag_table[] = {
   {
-  "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
-  "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
-  "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
-  "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
-  "title", GST_TAG_TITLE}, {
-  "artist", GST_TAG_ARTIST}, {
-  "album", GST_TAG_ALBUM}, {
-  "composer", GST_TAG_COMPOSER}, {
-  "comment", GST_TAG_COMMENT}, {
-  "comments", GST_TAG_COMMENT}, {
-  "copyright", GST_TAG_COPYRIGHT}, {
-  "genre", GST_TAG_GENRE}, {
-  "isrc", GST_TAG_ISRC}, {
-  "disc", GST_TAG_ALBUM_VOLUME_NUMBER}, {
-  "disk", GST_TAG_ALBUM_VOLUME_NUMBER}, {
-  "discnumber", GST_TAG_ALBUM_VOLUME_NUMBER}, {
-  "disknumber", GST_TAG_ALBUM_VOLUME_NUMBER}, {
-  "track", GST_TAG_TRACK_NUMBER}, {
-  "tracknumber", GST_TAG_TRACK_NUMBER}, {
-  "year", GST_TAG_DATE}, {
-  "file", GST_TAG_LOCATION}
+      "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
+      "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
+      "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
+      "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
+      "title", GST_TAG_TITLE}, {
+      "artist", GST_TAG_ARTIST}, {
+      "album", GST_TAG_ALBUM}, {
+      "composer", GST_TAG_COMPOSER}, {
+      "comment", GST_TAG_COMMENT}, {
+      "comments", GST_TAG_COMMENT}, {
+      "copyright", GST_TAG_COPYRIGHT}, {
+      "genre", GST_TAG_GENRE}, {
+      "isrc", GST_TAG_ISRC}, {
+      "disc", GST_TAG_ALBUM_VOLUME_NUMBER}, {
+      "disk", GST_TAG_ALBUM_VOLUME_NUMBER}, {
+      "discnumber", GST_TAG_ALBUM_VOLUME_NUMBER}, {
+      "disknumber", GST_TAG_ALBUM_VOLUME_NUMBER}, {
+      "track", GST_TAG_TRACK_NUMBER}, {
+      "tracknumber", GST_TAG_TRACK_NUMBER}, {
+      "year", GST_TAG_DATE}, {
+      "file", GST_TAG_LOCATION}
 };
 
 static gboolean
diff --git a/gst/audiofx/audioamplify.c b/gst/audiofx/audioamplify.c
index 8228ebdbdcaa98aee9c75d2a08a2482cb7f8ede7..3507d281ffc895d4bac27475ee05a503f49785a4 100644
--- a/gst/audiofx/audioamplify.c
+++ b/gst/audiofx/audioamplify.c
@@ -329,43 +329,47 @@ gst_audio_amplify_process_function (gint clipping, GstAudioFormat format)
     GstAudioAmplifyProcessFunc func;
   } process[] = {
     {
-    GST_AUDIO_FORMAT_F32, METHOD_CLIP, gst_audio_amplify_transform_gfloat_clip}, {
-    GST_AUDIO_FORMAT_F32, METHOD_WRAP_NEGATIVE,
-          gst_audio_amplify_transform_gfloat_wrap_negative}, {
-    GST_AUDIO_FORMAT_F32, METHOD_WRAP_POSITIVE,
-          gst_audio_amplify_transform_gfloat_wrap_positive}, {
-    GST_AUDIO_FORMAT_F32, METHOD_NOCLIP,
-          gst_audio_amplify_transform_gfloat_noclip}, {
-    GST_AUDIO_FORMAT_F64, METHOD_CLIP,
-          gst_audio_amplify_transform_gdouble_clip}, {
-    GST_AUDIO_FORMAT_F64, METHOD_WRAP_NEGATIVE,
-          gst_audio_amplify_transform_gdouble_wrap_negative}, {
-    GST_AUDIO_FORMAT_F64, METHOD_WRAP_POSITIVE,
-          gst_audio_amplify_transform_gdouble_wrap_positive}, {
-    GST_AUDIO_FORMAT_F64, METHOD_NOCLIP,
-          gst_audio_amplify_transform_gdouble_noclip}, {
-    GST_AUDIO_FORMAT_S8, METHOD_CLIP, gst_audio_amplify_transform_gint8_clip}, {
-    GST_AUDIO_FORMAT_S8, METHOD_WRAP_NEGATIVE,
-          gst_audio_amplify_transform_gint8_wrap_negative}, {
-    GST_AUDIO_FORMAT_S8, METHOD_WRAP_POSITIVE,
-          gst_audio_amplify_transform_gint8_wrap_positive}, {
-    GST_AUDIO_FORMAT_S8, METHOD_NOCLIP,
-          gst_audio_amplify_transform_gint8_noclip}, {
-    GST_AUDIO_FORMAT_S16, METHOD_CLIP, gst_audio_amplify_transform_gint16_clip}, {
-    GST_AUDIO_FORMAT_S16, METHOD_WRAP_NEGATIVE,
-          gst_audio_amplify_transform_gint16_wrap_negative}, {
-    GST_AUDIO_FORMAT_S16, METHOD_WRAP_POSITIVE,
-          gst_audio_amplify_transform_gint16_wrap_positive}, {
-    GST_AUDIO_FORMAT_S16, METHOD_NOCLIP,
-          gst_audio_amplify_transform_gint16_noclip}, {
-    GST_AUDIO_FORMAT_S32, METHOD_CLIP, gst_audio_amplify_transform_gint32_clip}, {
-    GST_AUDIO_FORMAT_S32, METHOD_WRAP_NEGATIVE,
-          gst_audio_amplify_transform_gint32_wrap_negative}, {
-    GST_AUDIO_FORMAT_S32, METHOD_WRAP_POSITIVE,
-          gst_audio_amplify_transform_gint32_wrap_positive}, {
-    GST_AUDIO_FORMAT_S32, METHOD_NOCLIP,
-          gst_audio_amplify_transform_gint32_noclip}, {
-    0, 0, NULL}
+          GST_AUDIO_FORMAT_F32, METHOD_CLIP,
+        gst_audio_amplify_transform_gfloat_clip}, {
+          GST_AUDIO_FORMAT_F32, METHOD_WRAP_NEGATIVE,
+        gst_audio_amplify_transform_gfloat_wrap_negative}, {
+          GST_AUDIO_FORMAT_F32, METHOD_WRAP_POSITIVE,
+        gst_audio_amplify_transform_gfloat_wrap_positive}, {
+          GST_AUDIO_FORMAT_F32, METHOD_NOCLIP,
+        gst_audio_amplify_transform_gfloat_noclip}, {
+          GST_AUDIO_FORMAT_F64, METHOD_CLIP,
+        gst_audio_amplify_transform_gdouble_clip}, {
+          GST_AUDIO_FORMAT_F64, METHOD_WRAP_NEGATIVE,
+        gst_audio_amplify_transform_gdouble_wrap_negative}, {
+          GST_AUDIO_FORMAT_F64, METHOD_WRAP_POSITIVE,
+        gst_audio_amplify_transform_gdouble_wrap_positive}, {
+          GST_AUDIO_FORMAT_F64, METHOD_NOCLIP,
+        gst_audio_amplify_transform_gdouble_noclip}, {
+          GST_AUDIO_FORMAT_S8, METHOD_CLIP,
+        gst_audio_amplify_transform_gint8_clip}, {
+          GST_AUDIO_FORMAT_S8, METHOD_WRAP_NEGATIVE,
+        gst_audio_amplify_transform_gint8_wrap_negative}, {
+          GST_AUDIO_FORMAT_S8, METHOD_WRAP_POSITIVE,
+        gst_audio_amplify_transform_gint8_wrap_positive}, {
+          GST_AUDIO_FORMAT_S8, METHOD_NOCLIP,
+        gst_audio_amplify_transform_gint8_noclip}, {
+          GST_AUDIO_FORMAT_S16, METHOD_CLIP,
+        gst_audio_amplify_transform_gint16_clip}, {
+          GST_AUDIO_FORMAT_S16, METHOD_WRAP_NEGATIVE,
+        gst_audio_amplify_transform_gint16_wrap_negative}, {
+          GST_AUDIO_FORMAT_S16, METHOD_WRAP_POSITIVE,
+        gst_audio_amplify_transform_gint16_wrap_positive}, {
+          GST_AUDIO_FORMAT_S16, METHOD_NOCLIP,
+        gst_audio_amplify_transform_gint16_noclip}, {
+          GST_AUDIO_FORMAT_S32, METHOD_CLIP,
+        gst_audio_amplify_transform_gint32_clip}, {
+          GST_AUDIO_FORMAT_S32, METHOD_WRAP_NEGATIVE,
+        gst_audio_amplify_transform_gint32_wrap_negative}, {
+          GST_AUDIO_FORMAT_S32, METHOD_WRAP_POSITIVE,
+        gst_audio_amplify_transform_gint32_wrap_positive}, {
+          GST_AUDIO_FORMAT_S32, METHOD_NOCLIP,
+        gst_audio_amplify_transform_gint32_noclip}, {
+        0, 0, NULL}
   };
   const struct process *p;
 
diff --git a/gst/audiofx/gstscaletempo.c b/gst/audiofx/gstscaletempo.c
index 646181fca879432fc04a053c8d62476b6e8bcc05..4dfd40d08e9692d1cf5e3fb521542c90615c8680 100644
--- a/gst/audiofx/gstscaletempo.c
+++ b/gst/audiofx/gstscaletempo.c
@@ -602,7 +602,7 @@ gst_scaletempo_sink_event (GstBaseTransform * trans, GstEvent * event)
 
     if (segment.format != GST_FORMAT_TIME
         || scaletempo->scale != ABS (segment.rate)
-        || ! !scaletempo->reverse != ! !(segment.rate < 0.0)) {
+        || !!scaletempo->reverse != !!(segment.rate < 0.0)) {
       if (segment.format != GST_FORMAT_TIME || ABS (segment.rate - 1.0) < 1e-10) {
         scaletempo->scale = 1.0;
         gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c
index 28e953ef58aa1a6336bbefb2cfb30d46fe222a33..eb3ee985bfdfb250f2f14639e49bcb22f1673f04 100644
--- a/gst/audioparsers/gstaacparse.c
+++ b/gst/audioparsers/gstaacparse.c
@@ -544,6 +544,70 @@ gst_aac_parse_get_audio_sample_rate (GstAacParse * aacparse, GstBitReader * br,
   return TRUE;
 }
 
+static gboolean
+gst_aac_parse_program_config_element (GstAacParse * aacparse,
+    GstBitReader * br, gint * channels)
+{
+  guint8 G_GNUC_UNUSED element_instance_tag;
+  guint8 G_GNUC_UNUSED object_type;
+  guint8 G_GNUC_UNUSED sampling_frequency_index;
+  guint8 num_front_channel_elements;
+  guint8 num_side_channel_elements;
+  guint8 num_back_channel_elements;
+  guint8 num_lfe_channel_elements;
+
+  if (!gst_bit_reader_get_bits_uint8 (br, &element_instance_tag, 4))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &object_type, 2))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &sampling_frequency_index, 4))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &num_front_channel_elements, 4))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &num_side_channel_elements, 4))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &num_back_channel_elements, 4))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &num_lfe_channel_elements, 4))
+    return FALSE;
+  GST_LOG_OBJECT (aacparse, "channels front %d side %d back %d lfe %d ",
+      num_front_channel_elements, num_side_channel_elements,
+      num_back_channel_elements, num_lfe_channel_elements);
+  *channels = num_front_channel_elements + num_side_channel_elements +
+      num_back_channel_elements + num_lfe_channel_elements;
+
+  return TRUE;
+}
+
+static gboolean
+gst_aac_parse_ga_specific_config (GstAacParse * aacparse,
+    GstBitReader * br, gint * channels, guint8 channel_configuration)
+{
+  guint8 G_GNUC_UNUSED frame_length_flag;
+  guint8 depends_on_core_coder;
+  guint32 G_GNUC_UNUSED core_coder_delay;
+  guint8 G_GNUC_UNUSED extension_flag;
+
+  if (!gst_bit_reader_get_bits_uint8 (br, &frame_length_flag, 1))
+    return FALSE;
+  if (!gst_bit_reader_get_bits_uint8 (br, &depends_on_core_coder, 1))
+    return FALSE;
+
+  if (depends_on_core_coder) {
+    if (!gst_bit_reader_get_bits_uint32 (br, &core_coder_delay, 14))
+      return FALSE;
+  }
+
+  if (!gst_bit_reader_get_bits_uint8 (br, &extension_flag, 1))
+    return FALSE;
+
+  if (!channel_configuration) {
+    return gst_aac_parse_program_config_element (aacparse, br, channels);
+  }
+
+  return TRUE;
+}
+
 /* See table 1.13 in ISO/IEC 14496-3 */
 static gboolean
 gst_aac_parse_read_audio_specific_config (GstAacParse * aacparse,
@@ -567,8 +631,6 @@ gst_aac_parse_read_audio_specific_config (GstAacParse * aacparse,
     return FALSE;
   *channels = loas_channels_table[channel_configuration];
   GST_LOG_OBJECT (aacparse, "channel_configuration: %d", channel_configuration);
-  if (!*channels)
-    return FALSE;
 
   if (audio_object_type == 5 || audio_object_type == 29) {
     extension_audio_object_type = 5;
@@ -618,6 +680,32 @@ gst_aac_parse_read_audio_specific_config (GstAacParse * aacparse,
     *frame_samples = frame_flag ? 960 : 1024;
   }
 
+  switch (audio_object_type) {
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 6:
+    case 7:
+    case 17:
+    case 19:
+    case 20:
+    case 21:
+    case 22:
+    case 23:
+      if (!gst_aac_parse_ga_specific_config (aacparse, br, channels,
+              channel_configuration)) {
+        GST_WARNING_OBJECT (aacparse, "Error parsing GASpecificConfig");
+        return FALSE;
+      }
+      break;
+    default:
+      break;
+  }
+
+  if (!*channels)
+    return FALSE;
+
   /* There's LOTS of stuff next, but we ignore it for now as we have
      what we want (sample rate and number of channels */
   GST_DEBUG_OBJECT (aacparse,
diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c
index 64b3ca3c1e1442bb8f90e9668e090aace7b09ba8..44e75ff5c9e61b3cf49c0b860a72cc39fcd3a51e 100644
--- a/gst/audioparsers/gstac3parse.c
+++ b/gst/audioparsers/gstac3parse.c
@@ -59,82 +59,82 @@ static const struct
   const guint frame_size[3];    /* frame size for 32kHz, 44kHz, and 48kHz */
 } frmsizcod_table[38] = {
   {
-    32, {
-  64, 69, 96}}, {
-    32, {
-  64, 70, 96}}, {
-    40, {
-  80, 87, 120}}, {
-    40, {
-  80, 88, 120}}, {
-    48, {
-  96, 104, 144}}, {
-    48, {
-  96, 105, 144}}, {
-    56, {
-  112, 121, 168}}, {
-    56, {
-  112, 122, 168}}, {
-    64, {
-  128, 139, 192}}, {
-    64, {
-  128, 140, 192}}, {
-    80, {
-  160, 174, 240}}, {
-    80, {
-  160, 175, 240}}, {
-    96, {
-  192, 208, 288}}, {
-    96, {
-  192, 209, 288}}, {
-    112, {
-  224, 243, 336}}, {
-    112, {
-  224, 244, 336}}, {
-    128, {
-  256, 278, 384}}, {
-    128, {
-  256, 279, 384}}, {
-    160, {
-  320, 348, 480}}, {
-    160, {
-  320, 349, 480}}, {
-    192, {
-  384, 417, 576}}, {
-    192, {
-  384, 418, 576}}, {
-    224, {
-  448, 487, 672}}, {
-    224, {
-  448, 488, 672}}, {
-    256, {
-  512, 557, 768}}, {
-    256, {
-  512, 558, 768}}, {
-    320, {
-  640, 696, 960}}, {
-    320, {
-  640, 697, 960}}, {
-    384, {
-  768, 835, 1152}}, {
-    384, {
-  768, 836, 1152}}, {
-    448, {
-  896, 975, 1344}}, {
-    448, {
-  896, 976, 1344}}, {
-    512, {
-  1024, 1114, 1536}}, {
-    512, {
-  1024, 1115, 1536}}, {
-    576, {
-  1152, 1253, 1728}}, {
-    576, {
-  1152, 1254, 1728}}, {
-    640, {
-  1280, 1393, 1920}}, {
-    640, {
-  1280, 1394, 1920}}
+        32, {
+          64, 69, 96}}, {
+        32, {
+          64, 70, 96}}, {
+        40, {
+          80, 87, 120}}, {
+        40, {
+          80, 88, 120}}, {
+        48, {
+          96, 104, 144}}, {
+        48, {
+          96, 105, 144}}, {
+        56, {
+          112, 121, 168}}, {
+        56, {
+          112, 122, 168}}, {
+        64, {
+          128, 139, 192}}, {
+        64, {
+          128, 140, 192}}, {
+        80, {
+          160, 174, 240}}, {
+        80, {
+          160, 175, 240}}, {
+        96, {
+          192, 208, 288}}, {
+        96, {
+          192, 209, 288}}, {
+        112, {
+          224, 243, 336}}, {
+        112, {
+          224, 244, 336}}, {
+        128, {
+          256, 278, 384}}, {
+        128, {
+          256, 279, 384}}, {
+        160, {
+          320, 348, 480}}, {
+        160, {
+          320, 349, 480}}, {
+        192, {
+          384, 417, 576}}, {
+        192, {
+          384, 418, 576}}, {
+        224, {
+          448, 487, 672}}, {
+        224, {
+          448, 488, 672}}, {
+        256, {
+          512, 557, 768}}, {
+        256, {
+          512, 558, 768}}, {
+        320, {
+          640, 696, 960}}, {
+        320, {
+          640, 697, 960}}, {
+        384, {
+          768, 835, 1152}}, {
+        384, {
+          768, 836, 1152}}, {
+        448, {
+          896, 975, 1344}}, {
+        448, {
+          896, 976, 1344}}, {
+        512, {
+          1024, 1114, 1536}}, {
+        512, {
+          1024, 1115, 1536}}, {
+        576, {
+          1152, 1253, 1728}}, {
+        576, {
+          1152, 1254, 1728}}, {
+        640, {
+          1280, 1393, 1920}}, {
+        640, {
+          1280, 1394, 1920}}
 };
 
 static const guint fscod_rates[4] = { 48000, 44100, 32000, 0 };
diff --git a/gst/audioparsers/gstdcaparse.c b/gst/audioparsers/gstdcaparse.c
index e9c870250bcc6fe40976c0a0ed5fb2572ab7a8bb..ef2dec56e22cc7411099e9c17f1879fba2d36d76 100644
--- a/gst/audioparsers/gstdcaparse.c
+++ b/gst/audioparsers/gstdcaparse.c
@@ -505,6 +505,7 @@ gst_dca_parse_chain_priv (GstPad * pad, GstObject * parent, GstBuffer * buffer)
   size = gst_buffer_get_size (buffer);
   if (size >= 2) {
     newbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, 2, size - 2);
+    gst_buffer_copy_into (newbuf, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
     gst_buffer_unref (buffer);
     ret = dcaparse->baseparse_chainfunc (pad, parent, newbuf);
   } else {
diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c
index a53b7ebc776d653879e907d80d050a7793645c2d..89af1d7795af20eb78731b4df4d1c9b47e7b64fc 100644
--- a/gst/audioparsers/gstflacparse.c
+++ b/gst/audioparsers/gstflacparse.c
@@ -652,13 +652,15 @@ static gboolean
 gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
     const guint8 * data, gsize size, guint * ret)
 {
-  guint max, remaining;
+  guint max;
   guint i, search_start, search_end;
   FrameHeaderCheckReturn header_ret;
   guint16 block_size;
   gboolean suspect_start = FALSE, suspect_end = FALSE;
 
-  if (size < flacparse->min_framesize)
+  /* minimum frame header size is 5 bytes and we read that much
+   * without checking the size below */
+  if (size < MAX (flacparse->min_framesize, 5))
     goto need_more;
 
   header_ret =
@@ -673,15 +675,14 @@ gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
 
   /* mind unknown framesize */
   search_start = MAX (2, flacparse->min_framesize);
-  if (flacparse->max_framesize)
-    search_end = MIN (size, flacparse->max_framesize + 9 + 2);
-  else
-    search_end = size;
-  search_end -= 2;
 
-  remaining = size;
+  /* at minimum 5 bytes are read below so stop 5 bytes before the end.
+   * we also checked above that at least 5 bytes are available. */
+  search_end = size - 5;
+  if (flacparse->max_framesize)
+    search_end = MIN (search_end, flacparse->max_framesize + 9 + 2);
 
-  for (i = search_start; i < search_end; i++, remaining--) {
+  for (i = search_start; i < search_end; i++) {
 
     if ((GST_READ_UINT16_BE (data + i) & 0xfffe) != 0xfff8)
       continue;
@@ -690,7 +691,7 @@ gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
     suspect_end = FALSE;
     header_ret =
         gst_flac_parse_frame_header_is_valid (flacparse, data + i,
-        remaining, FALSE, NULL, &suspect_end);
+        size - i, FALSE, NULL, &suspect_end);
     if (header_ret == FRAME_HEADER_VALID) {
       if (flacparse->check_frame_checksums || suspect_start || suspect_end) {
         guint16 actual_crc = gst_flac_calculate_crc16 (data, i - 2);
@@ -1111,6 +1112,7 @@ gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer)
   GstMapInfo map;
   guint32 img_len = 0, img_type = 0;
   guint32 img_mimetype_len = 0, img_description_len = 0;
+  const guint8 *img_data;
 
   gst_buffer_map (buffer, &map, GST_MAP_READ);
   gst_byte_reader_init (&reader, map.data, map.size);
@@ -1137,7 +1139,7 @@ gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer)
   if (!gst_byte_reader_get_uint32_be (&reader, &img_len))
     goto error;
 
-  if (gst_byte_reader_get_pos (&reader) + img_len > map.size)
+  if (!gst_byte_reader_get_data (&reader, img_len, &img_data))
     goto error;
 
   GST_INFO_OBJECT (flacparse, "Got image of %d bytes", img_len);
@@ -1146,8 +1148,7 @@ gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer)
     if (flacparse->tags == NULL)
       flacparse->tags = gst_tag_list_new_empty ();
 
-    gst_tag_list_add_id3_image (flacparse->tags,
-        map.data + gst_byte_reader_get_pos (&reader), img_len, img_type);
+    gst_tag_list_add_id3_image (flacparse->tags, img_data, img_len, img_type);
   }
 
   gst_buffer_unmap (buffer, &map);
diff --git a/gst/audioparsers/gstwavpackparse.c b/gst/audioparsers/gstwavpackparse.c
index 16b9b4e58e686573f041c5ca12b7bf2c0e523f77..1b970cd26c2e772ed040c0f41cb4aa9ce20563b7 100644
--- a/gst/audioparsers/gstwavpackparse.c
+++ b/gst/audioparsers/gstwavpackparse.c
@@ -206,24 +206,24 @@ static const struct
   const GstAudioChannelPosition gst_pos;
 } layout_mapping[] = {
   {
-  0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
-  0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
-  0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
-  0x00008, GST_AUDIO_CHANNEL_POSITION_LFE1}, {
-  0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
-  0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
-  0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
-  0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
-  0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
-  0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
-  0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
-  0x00800, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, {
-  0x01000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
-  0x02000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
-  0x04000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
-  0x08000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, {
-  0x10000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, {
-  0x20000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
+      0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
+      0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
+      0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
+      0x00008, GST_AUDIO_CHANNEL_POSITION_LFE1}, {
+      0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
+      0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
+      0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
+      0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
+      0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
+      0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
+      0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
+      0x00800, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, {
+      0x01000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
+      0x02000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
+      0x04000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
+      0x08000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, {
+      0x10000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, {
+      0x20000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
 };
 
 #define MAX_CHANNEL_POSITIONS G_N_ELEMENTS (layout_mapping)
@@ -308,7 +308,7 @@ gst_wavpack_parse_frame_metadata (GstWavpackParse * parse, GstBuffer * buf,
 
   /* need to dig metadata blocks for some more */
   while (gst_byte_reader_get_remaining (&br)) {
-    gint size = 0;
+    guint32 size = 0;
     guint16 size2 = 0;
     guint8 c, id;
     const guint8 *data;
@@ -322,8 +322,11 @@ gst_wavpack_parse_frame_metadata (GstWavpackParse * parse, GstBuffer * buf,
     size <<= 8;
     size += c;
     size <<= 1;
-    if (id & ID_ODD_SIZE)
+    if (id & ID_ODD_SIZE) {
+      if (size == 0)
+        goto read_failed;
       size--;
+    }
 
     CHECK (gst_byte_reader_get_data (&br, size + (size & 1), &data));
     gst_byte_reader_init (&mbr, data, size);
@@ -337,6 +340,7 @@ gst_wavpack_parse_frame_metadata (GstWavpackParse * parse, GstBuffer * buf,
         break;
       case ID_WV_BITSTREAM:
       case ID_WVX_BITSTREAM:
+      case ID_WVX_NEW_BITSTREAM:
         break;
       case ID_SAMPLE_RATE:
         if (size == 3) {
diff --git a/gst/audioparsers/gstwavpackparse.h b/gst/audioparsers/gstwavpackparse.h
index 30325aa26b0ad04e2ef54c6954d7492c0d9973a1..848d4de5bf594e454a617b70273b8b272cf72d58 100644
--- a/gst/audioparsers/gstwavpackparse.h
+++ b/gst/audioparsers/gstwavpackparse.h
@@ -66,6 +66,7 @@ G_BEGIN_DECLS
 #define ID_CONFIG_BLOCK         (ID_OPTIONAL_DATA | 0x5)
 #define ID_MD5_CHECKSUM         (ID_OPTIONAL_DATA | 0x6)
 #define ID_SAMPLE_RATE          (ID_OPTIONAL_DATA | 0x7)
+#define ID_WVX_NEW_BITSTREAM    (ID_OPTIONAL_DATA | ID_WVX_BITSTREAM)
 
 #define FLAG_FINAL_BLOCK        (1 << 12)
 
diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c
index 0d18a6495c78f912d11eb2386b14f02ac4f86ad5..9c0495c2211c6c5a732d8b16a8e5820f0e10f276 100644
--- a/gst/avi/gstavidemux.c
+++ b/gst/avi/gstavidemux.c
@@ -4488,9 +4488,9 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment,
   gboolean next;
 
   seek_time = segment->position;
-  keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
-  before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
-  after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
+  keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
+  before = !!(flags & GST_SEEK_FLAG_SNAP_BEFORE);
+  after = !!(flags & GST_SEEK_FLAG_SNAP_AFTER);
 
   GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT
       " keyframe seeking:%d, %s", GST_TIME_ARGS (seek_time), keyframe,
@@ -4763,10 +4763,10 @@ avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event)
   gst_segment_do_seek (&seeksegment, rate, format, flags,
       cur_type, cur, stop_type, stop, &update);
 
-  keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
+  keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
   cur = seeksegment.position;
-  before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
-  after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
+  before = !!(flags & GST_SEEK_FLAG_SNAP_BEFORE);
+  after = !!(flags & GST_SEEK_FLAG_SNAP_AFTER);
 
   GST_DEBUG_OBJECT (avi,
       "Seek requested: ts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c
index 5a14e9f81c96c9e68c28e70c7efc424c8eb03e65..19f205605f257f491638fddcf6a894c1420eff96 100644
--- a/gst/avi/gstavimux.c
+++ b/gst/avi/gstavimux.c
@@ -1227,17 +1227,17 @@ gst_avi_mux_write_tag (const GstTagList * list, const gchar * tag,
     const gchar *tag;
   } rifftags[] = {
     {
-    GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
-    GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
-    GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
-    GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
-    GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
-    GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
-    GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
-    GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
-    GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
-    GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
-    0, NULL}
+        GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
+        GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
+        GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
+        GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
+        GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
+        GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
+        GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
+        GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
+        GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
+        GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
+        0, NULL}
   };
   gint n;
   gchar *str = NULL;
diff --git a/gst/avi/gstavisubtitle.c b/gst/avi/gstavisubtitle.c
index efc5f0405186472a9b73c8e73b4e12a028ae939a..c816934da61c8bf26535d55c9d5f00963bc4faf8 100644
--- a/gst/avi/gstavisubtitle.c
+++ b/gst/avi/gstavisubtitle.c
@@ -196,7 +196,7 @@ gst_avi_subtitle_parse_gab2_chunk (GstAviSubtitle * sub, GstBuffer * buf)
   /* read 'name' of subtitle */
   name_length = GST_READ_UINT32_LE (map.data + 5 + 2);
   GST_LOG_OBJECT (sub, "length of name: %u", name_length);
-  if (map.size <= 17 + name_length)
+  if (G_MAXUINT32 - 17 < name_length || map.size < 17 + name_length)
     goto wrong_name_length;
 
   name_utf8 =
@@ -216,7 +216,8 @@ gst_avi_subtitle_parse_gab2_chunk (GstAviSubtitle * sub, GstBuffer * buf)
   file_length = GST_READ_UINT32_LE (map.data + 13 + name_length);
   GST_LOG_OBJECT (sub, "length srt/ssa file: %u", file_length);
 
-  if (map.size < (17 + name_length + file_length))
+  if (G_MAXUINT32 - 17 - name_length < file_length
+      || map.size < 17 + name_length + file_length)
     goto wrong_total_length;
 
   /* store this, so we can send it again after a seek; note that we shouldn't
diff --git a/gst/cutter/gstcutter.c b/gst/cutter/gstcutter.c
index 760a9c9d9172200adc30aadc11f31d3f587a8f1d..827e1ca4a2cf710b0581a9690e0526fdd52a2722 100644
--- a/gst/cutter/gstcutter.c
+++ b/gst/cutter/gstcutter.c
@@ -54,6 +54,7 @@ GST_DEBUG_CATEGORY_STATIC (cutter_debug);
 #define CUTTER_DEFAULT_THRESHOLD_LEVEL    0.1
 #define CUTTER_DEFAULT_THRESHOLD_LENGTH  (500 * GST_MSECOND)
 #define CUTTER_DEFAULT_PRE_LENGTH        (200 * GST_MSECOND)
+#define EPSILON 1e-35f
 
 static GstStaticPadTemplate cutter_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -81,7 +82,8 @@ enum
   PROP_THRESHOLD_DB,
   PROP_RUN_LENGTH,
   PROP_PRE_LENGTH,
-  PROP_LEAKY
+  PROP_LEAKY,
+  PROP_AUDIO_LEVEL_META,
 };
 
 #define gst_cutter_parent_class parent_class
@@ -135,6 +137,17 @@ gst_cutter_class_init (GstCutterClass * klass)
       g_param_spec_boolean ("leaky", "Leaky",
           "do we leak buffers when below threshold ?",
           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstCutter:audio-level-meta:
+   *
+   * If %TRUE, generate or update GstAudioLevelMeta on output buffers.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_AUDIO_LEVEL_META,
+      g_param_spec_boolean ("audio-level-meta", "Audio Level Meta",
+          "Set GstAudioLevelMeta on buffers", FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   GST_DEBUG_CATEGORY_INIT (cutter_debug, "cutter", 0, "Audio cutting");
 
@@ -175,6 +188,7 @@ gst_cutter_init (GstCutter * filter)
   filter->pre_run_length = 0 * GST_SECOND;
   filter->pre_buffer = NULL;
   filter->leaky = FALSE;
+  filter->audio_level_meta = FALSE;
 }
 
 static GstMessage *
@@ -293,6 +307,23 @@ gst_cutter_event (GstPad * pad, GstObject * parent, GstEvent * event)
   return ret;
 }
 
+static void
+set_audio_level_meta (GstBuffer * buffer, guint8 level)
+{
+  GstAudioLevelMeta *meta;
+
+  /* Update the existing meta, if any, so we can have an upstream element
+   * filling the voice activity part of the meta. */
+  meta = gst_buffer_get_audio_level_meta (buffer);
+  if (meta) {
+    meta->level = level;
+  } else {
+    /* Assume audio does not contain voice, it can be detected by another
+     * downstream element. */
+    gst_buffer_add_audio_level_meta (buffer, level, FALSE);
+  }
+}
+
 static GstFlowReturn
 gst_cutter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
 {
@@ -355,6 +386,13 @@ gst_cutter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
   GST_LOG_OBJECT (filter, "buffer stats: NMS %f, RMS %f, audio length %f", NMS,
       RMS, gst_guint64_to_gdouble (duration));
 
+  if (filter->audio_level_meta) {
+    gdouble RMSdB = 20 * log10 (RMS + EPSILON);
+
+    buf = gst_buffer_make_writable (buf);
+    set_audio_level_meta (buf, -RMSdB);
+  }
+
   if (RMS < filter->threshold_level)
     filter->silent_run_length += gst_guint64_to_gdouble (duration);
   else {
@@ -469,6 +507,9 @@ gst_cutter_set_property (GObject * object, guint prop_id,
       /* set if the pre-record buffer is leaky or not */
       filter->leaky = g_value_get_boolean (value);
       break;
+    case PROP_AUDIO_LEVEL_META:
+      filter->audio_level_meta = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -500,6 +541,9 @@ gst_cutter_get_property (GObject * object, guint prop_id,
     case PROP_LEAKY:
       g_value_set_boolean (value, filter->leaky);
       break;
+    case PROP_AUDIO_LEVEL_META:
+      g_value_set_boolean (value, filter->audio_level_meta);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
diff --git a/gst/cutter/gstcutter.h b/gst/cutter/gstcutter.h
index 3167cea007d13f9447bb91d4f59ba92da3c7e194..d00de4d9f6a66a6d2dccbc2a713af4987b9bcf57 100644
--- a/gst/cutter/gstcutter.h
+++ b/gst/cutter/gstcutter.h
@@ -62,6 +62,7 @@ struct _GstCutter
   double pre_run_length;        /* how long is it currently ? */
   GList *pre_buffer;            /* list of GstBuffers in pre-record buffer */
   gboolean leaky;               /* do we leak an overflowing prebuffer ? */
+  gboolean audio_level_meta;    /* whether or not generate GstAudioLevelMeta */
 
   GstAudioInfo info;
   GstSegment segment;
diff --git a/gst/debugutils/gsttaginject.c b/gst/debugutils/gsttaginject.c
index 223a0da3882f3f7dabcfa9d17d4cf945793806db..fa680baac5396aa2fa9f423bbb192c9d19994311 100644
--- a/gst/debugutils/gsttaginject.c
+++ b/gst/debugutils/gsttaginject.c
@@ -32,6 +32,9 @@
  * |[
  * gst-launch-1.0 audiotestsrc num-buffers=100 ! taginject tags="keywords=\{\"testone\",\"audio\"\},title=\"audio\ testtone\"" ! vorbisenc ! oggmux ! filesink location=test.ogg
  * ]| set keywords and title demonstrating quoting of special chars and handling lists
+ * |[
+ * gst-launch-1.0.exe audiotestsrc num-buffers=500 ! taginject tags="title=MyTitle,artist=MyArtist,album=MyAlbum,genre=MyGenre" scope=global ! qtmux ! filesink location=test.m4a
+ * ]| set title, artist, album and genre. set scope as global
  *
  */
 
@@ -59,7 +62,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_tag_inject_debug);
 
 enum
 {
-  PROP_TAGS = 1
+  PROP_TAGS = 1,
+  PROP_SCOPE
 };
 
 
@@ -114,6 +118,19 @@ gst_tag_inject_class_init (GstTagInjectClass * klass)
           "List of tags to inject into the target file",
           NULL, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * taginject:scope:
+   *
+   * Scope of tags to inject (stream | global).
+   *
+   * Since: 1.24
+   **/
+  g_object_class_install_property (gobject_class, PROP_SCOPE,
+      g_param_spec_enum ("scope", "Scope",
+          "Scope of tags to inject (stream | global)",
+          g_type_from_name ("GstTagScope"), GST_TAG_SCOPE_STREAM,
+          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
   gobject_class->finalize = gst_tag_inject_finalize;
 
   gst_element_class_set_static_metadata (gstelement_class,
@@ -136,6 +153,7 @@ gst_tag_inject_init (GstTagInject * self)
   gst_base_transform_set_gap_aware (trans, TRUE);
 
   self->tags = NULL;
+  self->tags_scope = GST_TAG_SCOPE_STREAM;
 }
 
 static GstFlowReturn
@@ -168,6 +186,8 @@ gst_tag_inject_set_property (GObject * object, guint prop_id,
           g_strdup_printf ("taglist,%s", g_value_get_string (value));
       if (!(self->tags = gst_tag_list_new_from_string (structure))) {
         GST_WARNING ("unparsable taglist = '%s'", structure);
+      } else {
+        gst_tag_list_set_scope (self->tags, self->tags_scope);
       }
 
       /* make sure that tags will be send */
@@ -175,6 +195,11 @@ gst_tag_inject_set_property (GObject * object, guint prop_id,
       g_free (structure);
       break;
     }
+    case PROP_SCOPE:
+      self->tags_scope = g_value_get_enum (value);
+      if (self->tags)
+        gst_tag_list_set_scope (self->tags, self->tags_scope);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
diff --git a/gst/debugutils/gsttaginject.h b/gst/debugutils/gsttaginject.h
index a5459275f01cf07c35a41e080963f539844b13e5..8e59858b61ac7a28b227cc6102c553f721099ecc 100644
--- a/gst/debugutils/gsttaginject.h
+++ b/gst/debugutils/gsttaginject.h
@@ -53,6 +53,7 @@ struct _GstTagInject
   /*< private > */
   GstTagList *tags;
   gboolean tags_sent;
+  GstTagScope tags_scope;
 };
 
 struct _GstTagInjectClass
diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c
index 6c209c084394d17125d1b4da90256d9a0bf944fa..998b3a4af43a48780c705053491bddd12b0771cf 100644
--- a/gst/deinterlace/gstdeinterlace.c
+++ b/gst/deinterlace/gstdeinterlace.c
@@ -282,14 +282,22 @@ gst_deinterlace_locking_get_type (void)
   return deinterlace_locking_type;
 }
 
+#if G_BYTE_ORDER == G_BIG_ENDIAN
 #define DEINTERLACE_VIDEO_FORMATS \
     "{ AYUV, ARGB, ABGR, RGBA, BGRA, Y444, xRGB, xBGR, RGBx, BGRx, RGB, " \
-    "BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21 }"
+    "BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21, " \
+    "Y444_16BE, Y444_12BE, Y444_10BE, I422_12BE, I422_10BE, I420_12BE, I420_10BE } "
+#else
+#define DEINTERLACE_VIDEO_FORMATS \
+    "{ AYUV, ARGB, ABGR, RGBA, BGRA, Y444, xRGB, xBGR, RGBx, BGRx, RGB, " \
+    "BGR, YUY2, YVYU, UYVY, Y42B, I420, YV12, Y41B, NV12, NV21, " \
+    "Y444_16LE, Y444_12LE, Y444_10LE, I422_12LE, I422_10LE, I420_12LE, I420_10LE } "
+#endif
 
 #define DEINTERLACE_CAPS GST_VIDEO_CAPS_MAKE(DEINTERLACE_VIDEO_FORMATS)
 
 #define DEINTERLACE_ALL_CAPS DEINTERLACE_CAPS ";" \
-    GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
+    GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ANY)
 
 static GstStaticCaps progressive_caps =
 GST_STATIC_CAPS ("video/x-raw(ANY),interlace-mode=(string)progressive");
@@ -375,17 +383,17 @@ static const struct
   GType (*get_type) (void);
 } _method_types[] = {
   {
-  gst_deinterlace_method_tomsmocomp_get_type}, {
-  gst_deinterlace_method_greedy_h_get_type}, {
-  gst_deinterlace_method_greedy_l_get_type}, {
-  gst_deinterlace_method_vfir_get_type}, {
-  gst_deinterlace_method_linear_get_type}, {
-  gst_deinterlace_method_linear_blend_get_type}, {
-  gst_deinterlace_method_scaler_bob_get_type}, {
-  gst_deinterlace_method_weave_get_type}, {
-  gst_deinterlace_method_weave_tff_get_type}, {
-  gst_deinterlace_method_weave_bff_get_type}, {
-  gst_deinterlace_method_yadif_get_type}
+      gst_deinterlace_method_tomsmocomp_get_type}, {
+      gst_deinterlace_method_greedy_h_get_type}, {
+      gst_deinterlace_method_greedy_l_get_type}, {
+      gst_deinterlace_method_vfir_get_type}, {
+      gst_deinterlace_method_linear_get_type}, {
+      gst_deinterlace_method_linear_blend_get_type}, {
+      gst_deinterlace_method_scaler_bob_get_type}, {
+      gst_deinterlace_method_weave_get_type}, {
+      gst_deinterlace_method_weave_tff_get_type}, {
+      gst_deinterlace_method_weave_bff_get_type}, {
+      gst_deinterlace_method_yadif_get_type}
 };
 
 static void
@@ -1280,7 +1288,7 @@ gst_deinterlace_update_qos (GstDeinterlace * self, gdouble proportion,
   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
     if (G_UNLIKELY (diff > 0))
       self->earliest_time =
-          timestamp + 2 * diff + ((self->fields ==
+          timestamp + MIN (2 * diff, GST_SECOND) + ((self->fields ==
               GST_DEINTERLACE_ALL) ? self->field_duration : 2 *
           self->field_duration);
     else
@@ -2377,8 +2385,10 @@ gst_deinterlace_caps_double_framerate (GstCaps * caps, gboolean half)
     } else if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION_RANGE) {
       const GValue *min, *max;
       GValue nrange = { 0, }, nmin = {
-      0,}, nmax = {
-      0,};
+        0,
+      }, nmax = {
+        0,
+      };
       gint n, d;
 
       g_value_init (&nrange, GST_TYPE_FRACTION_RANGE);
diff --git a/gst/deinterlace/gstdeinterlacemethod.c b/gst/deinterlace/gstdeinterlacemethod.c
index 24893f8dfa0703fa7cfa15aa1f6cd2af95e78ea4..42fb287a08227433f7845db1b88777971f1fe2f7 100644
--- a/gst/deinterlace/gstdeinterlacemethod.c
+++ b/gst/deinterlace/gstdeinterlacemethod.c
@@ -89,6 +89,24 @@ gst_deinterlace_method_supported_impl (GstDeinterlaceMethodClass * klass,
       return (klass->deinterlace_frame_rgb != NULL);
     case GST_VIDEO_FORMAT_BGR:
       return (klass->deinterlace_frame_bgr != NULL);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+    case GST_VIDEO_FORMAT_Y444_16BE:
+    case GST_VIDEO_FORMAT_Y444_12BE:
+    case GST_VIDEO_FORMAT_Y444_10BE:
+    case GST_VIDEO_FORMAT_I422_12BE:
+    case GST_VIDEO_FORMAT_I422_10BE:
+    case GST_VIDEO_FORMAT_I420_12BE:
+    case GST_VIDEO_FORMAT_I420_10BE:
+#else
+    case GST_VIDEO_FORMAT_Y444_16LE:
+    case GST_VIDEO_FORMAT_Y444_12LE:
+    case GST_VIDEO_FORMAT_Y444_10LE:
+    case GST_VIDEO_FORMAT_I422_12LE:
+    case GST_VIDEO_FORMAT_I422_10LE:
+    case GST_VIDEO_FORMAT_I420_12LE:
+    case GST_VIDEO_FORMAT_I420_10LE:
+#endif
+      return (klass->deinterlace_frame_planar_high != NULL);
     default:
       return FALSE;
   }
@@ -171,6 +189,25 @@ gst_deinterlace_method_setup_impl (GstDeinterlaceMethod * self,
     case GST_VIDEO_FORMAT_BGR:
       self->deinterlace_frame = klass->deinterlace_frame_bgr;
       break;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+    case GST_VIDEO_FORMAT_Y444_16BE:
+    case GST_VIDEO_FORMAT_Y444_12BE:
+    case GST_VIDEO_FORMAT_Y444_10BE:
+    case GST_VIDEO_FORMAT_I422_12BE:
+    case GST_VIDEO_FORMAT_I422_10BE:
+    case GST_VIDEO_FORMAT_I420_12BE:
+    case GST_VIDEO_FORMAT_I420_10BE:
+#else
+    case GST_VIDEO_FORMAT_Y444_16LE:
+    case GST_VIDEO_FORMAT_Y444_12LE:
+    case GST_VIDEO_FORMAT_Y444_10LE:
+    case GST_VIDEO_FORMAT_I422_12LE:
+    case GST_VIDEO_FORMAT_I422_10LE:
+    case GST_VIDEO_FORMAT_I420_12LE:
+    case GST_VIDEO_FORMAT_I420_10LE:
+#endif
+      self->deinterlace_frame = klass->deinterlace_frame_planar_high;
+      break;
     default:
       self->deinterlace_frame = NULL;
       break;
@@ -287,6 +324,29 @@ gst_deinterlace_simple_method_supported (GstDeinterlaceMethodClass * mklass,
           && klass->copy_scanline_planar_u != NULL &&
           klass->interpolate_scanline_planar_v != NULL
           && klass->copy_scanline_planar_v != NULL);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+    case GST_VIDEO_FORMAT_Y444_16BE:
+    case GST_VIDEO_FORMAT_Y444_12BE:
+    case GST_VIDEO_FORMAT_Y444_10BE:
+    case GST_VIDEO_FORMAT_I422_12BE:
+    case GST_VIDEO_FORMAT_I422_10BE:
+    case GST_VIDEO_FORMAT_I420_12BE:
+    case GST_VIDEO_FORMAT_I420_10BE:
+#else
+    case GST_VIDEO_FORMAT_Y444_16LE:
+    case GST_VIDEO_FORMAT_Y444_12LE:
+    case GST_VIDEO_FORMAT_Y444_10LE:
+    case GST_VIDEO_FORMAT_I422_12LE:
+    case GST_VIDEO_FORMAT_I422_10LE:
+    case GST_VIDEO_FORMAT_I420_12LE:
+    case GST_VIDEO_FORMAT_I420_10LE:
+#endif
+      return (klass->interpolate_scanline_planar_y_16bits != NULL
+          && klass->copy_scanline_planar_y_16bits != NULL &&
+          klass->interpolate_scanline_planar_u_16bits != NULL
+          && klass->copy_scanline_planar_u_16bits != NULL &&
+          klass->interpolate_scanline_planar_v_16bits != NULL
+          && klass->copy_scanline_planar_v_16bits != NULL);
     default:
       return FALSE;
   }
@@ -743,6 +803,33 @@ gst_deinterlace_simple_method_setup (GstDeinterlaceMethod * method,
           klass->interpolate_scanline_planar_v;
       self->copy_scanline_planar[2] = klass->copy_scanline_planar_v;
       break;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+    case GST_VIDEO_FORMAT_Y444_16BE:
+    case GST_VIDEO_FORMAT_Y444_12BE:
+    case GST_VIDEO_FORMAT_Y444_10BE:
+    case GST_VIDEO_FORMAT_I422_12BE:
+    case GST_VIDEO_FORMAT_I422_10BE:
+    case GST_VIDEO_FORMAT_I420_12BE:
+    case GST_VIDEO_FORMAT_I420_10BE:
+#else
+    case GST_VIDEO_FORMAT_Y444_16LE:
+    case GST_VIDEO_FORMAT_Y444_12LE:
+    case GST_VIDEO_FORMAT_Y444_10LE:
+    case GST_VIDEO_FORMAT_I422_12LE:
+    case GST_VIDEO_FORMAT_I422_10LE:
+    case GST_VIDEO_FORMAT_I420_12LE:
+    case GST_VIDEO_FORMAT_I420_10LE:
+#endif
+      self->interpolate_scanline_planar[0] =
+          klass->interpolate_scanline_planar_y_16bits;
+      self->copy_scanline_planar[0] = klass->copy_scanline_planar_y_16bits;
+      self->interpolate_scanline_planar[1] =
+          klass->interpolate_scanline_planar_u_16bits;
+      self->copy_scanline_planar[1] = klass->copy_scanline_planar_u_16bits;
+      self->interpolate_scanline_planar[2] =
+          klass->interpolate_scanline_planar_v_16bits;
+      self->copy_scanline_planar[2] = klass->copy_scanline_planar_v_16bits;
+      break;
     default:
       break;
   }
@@ -788,6 +875,9 @@ gst_deinterlace_simple_method_class_init (GstDeinterlaceSimpleMethodClass
       gst_deinterlace_simple_method_deinterlace_frame_nv12;
   dm_class->deinterlace_frame_nv21 =
       gst_deinterlace_simple_method_deinterlace_frame_nv12;
+  /* same as 8bits planar */
+  dm_class->deinterlace_frame_planar_high =
+      gst_deinterlace_simple_method_deinterlace_frame_planar;
   dm_class->fields_required = 2;
   dm_class->setup = gst_deinterlace_simple_method_setup;
   dm_class->supported = gst_deinterlace_simple_method_supported;
@@ -849,6 +939,16 @@ gst_deinterlace_simple_method_class_init (GstDeinterlaceSimpleMethodClass
       gst_deinterlace_simple_method_interpolate_scanline_planar_v;
   klass->copy_scanline_planar_v =
       gst_deinterlace_simple_method_copy_scanline_planar_v;
+
+  /* planar high bitdepth formats use the same methods as 8bits planar,
+   * (i.e,memcpy) but interpolate_scanline_planar_{y,u,v}_16bits methods will
+   * be configured by each subclass */
+  klass->copy_scanline_planar_y_16bits =
+      gst_deinterlace_simple_method_copy_scanline_planar_y;
+  klass->copy_scanline_planar_u_16bits =
+      gst_deinterlace_simple_method_copy_scanline_planar_u;
+  klass->copy_scanline_planar_v_16bits =
+      gst_deinterlace_simple_method_copy_scanline_planar_v;
 }
 
 static void
diff --git a/gst/deinterlace/gstdeinterlacemethod.h b/gst/deinterlace/gstdeinterlacemethod.h
index 6c421c1e7bbfde51cea5cfb01ad52c0c0ecf6637..8390d52ddd77c1dc2704fea7409cb6680e6b062d 100644
--- a/gst/deinterlace/gstdeinterlacemethod.h
+++ b/gst/deinterlace/gstdeinterlacemethod.h
@@ -101,6 +101,8 @@ struct _GstDeinterlaceMethodClass {
   GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_rgb;
   GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_bgr;
 
+  GstDeinterlaceMethodDeinterlaceFunction deinterlace_frame_planar_high;
+
   const gchar *name;
   const gchar *nick;
 };
@@ -213,6 +215,14 @@ struct _GstDeinterlaceSimpleMethodClass {
   GstDeinterlaceSimpleMethodFunction interpolate_scanline_planar_u;
   GstDeinterlaceSimpleMethodFunction copy_scanline_planar_v;
   GstDeinterlaceSimpleMethodFunction interpolate_scanline_planar_v;
+
+  /* Planar high bitdepth formats */
+  GstDeinterlaceSimpleMethodFunction copy_scanline_planar_y_16bits;
+  GstDeinterlaceSimpleMethodFunction interpolate_scanline_planar_y_16bits;
+  GstDeinterlaceSimpleMethodFunction copy_scanline_planar_u_16bits;
+  GstDeinterlaceSimpleMethodFunction interpolate_scanline_planar_u_16bits;
+  GstDeinterlaceSimpleMethodFunction copy_scanline_planar_v_16bits;
+  GstDeinterlaceSimpleMethodFunction interpolate_scanline_planar_v_16bits;
 };
 
 GType gst_deinterlace_simple_method_get_type (void);
diff --git a/gst/deinterlace/tvtime-dist.c b/gst/deinterlace/tvtime-dist.c
index 47c1056e373c88e45c267bba3598faec549eca5a..0c2628a620b12317a2cb199d282ccdd385e93ac5 100644
--- a/gst/deinterlace/tvtime-dist.c
+++ b/gst/deinterlace/tvtime-dist.c
@@ -99,8 +99,14 @@ void deinterlace_line_vfir (guint8 * ORC_RESTRICT d1,
     const guint8 * ORC_RESTRICT s1, const guint8 * ORC_RESTRICT s2,
     const guint8 * ORC_RESTRICT s3, const guint8 * ORC_RESTRICT s4,
     const guint8 * ORC_RESTRICT s5, int n);
+void deinterlace_line_vfir_16bits (guint16 * ORC_RESTRICT d1,
+    const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2,
+    const guint16 * ORC_RESTRICT s3, const guint16 * ORC_RESTRICT s4,
+    const guint16 * ORC_RESTRICT s5, int n);
 void deinterlace_line_linear (guint8 * ORC_RESTRICT d1,
     const guint8 * ORC_RESTRICT s1, const guint8 * ORC_RESTRICT s2, int n);
+void deinterlace_line_linear_16bits (guint16 * ORC_RESTRICT d1,
+    const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2, int n);
 void deinterlace_line_linear_blend (guint8 * ORC_RESTRICT d1,
     const guint8 * ORC_RESTRICT s1, const guint8 * ORC_RESTRICT s2,
     const guint8 * ORC_RESTRICT s3, int n);
@@ -440,6 +446,296 @@ deinterlace_line_vfir (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1,
 #endif
 
 
+/* deinterlace_line_vfir_16bits */
+#ifdef DISABLE_ORC
+void
+deinterlace_line_vfir_16bits (guint16 * ORC_RESTRICT d1,
+    const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2,
+    const guint16 * ORC_RESTRICT s3, const guint16 * ORC_RESTRICT s4,
+    const guint16 * ORC_RESTRICT s5, int n)
+{
+  int i;
+  orc_union16 *ORC_RESTRICT ptr0;
+  const orc_union16 *ORC_RESTRICT ptr4;
+  const orc_union16 *ORC_RESTRICT ptr5;
+  const orc_union16 *ORC_RESTRICT ptr6;
+  const orc_union16 *ORC_RESTRICT ptr7;
+  const orc_union16 *ORC_RESTRICT ptr8;
+  orc_union16 var35;
+  orc_union16 var36;
+  orc_union16 var37;
+  orc_union16 var38;
+  orc_union16 var39;
+#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__)
+  volatile orc_union32 var40;
+#else
+  orc_union32 var40;
+#endif
+  orc_union16 var41;
+  orc_union32 var42;
+  orc_union32 var43;
+  orc_union32 var44;
+  orc_union32 var45;
+  orc_union32 var46;
+  orc_union32 var47;
+  orc_union32 var48;
+  orc_union32 var49;
+  orc_union32 var50;
+  orc_union32 var51;
+  orc_union32 var52;
+  orc_union32 var53;
+  orc_union32 var54;
+
+  ptr0 = (orc_union16 *) d1;
+  ptr4 = (orc_union16 *) s1;
+  ptr5 = (orc_union16 *) s2;
+  ptr6 = (orc_union16 *) s3;
+  ptr7 = (orc_union16 *) s4;
+  ptr8 = (orc_union16 *) s5;
+
+  /* 16: loadpl */
+  var40.i = 0x00000004;         /* 4 or 1.97626e-323f */
+
+  for (i = 0; i < n; i++) {
+    /* 0: loadw */
+    var35 = ptr4[i];
+    /* 1: convuwl */
+    var42.i = (orc_uint16) var35.i;
+    /* 2: loadw */
+    var36 = ptr8[i];
+    /* 3: convuwl */
+    var43.i = (orc_uint16) var36.i;
+    /* 4: addl */
+    var44.i = ((orc_uint32) var42.i) + ((orc_uint32) var43.i);
+    /* 5: loadw */
+    var37 = ptr5[i];
+    /* 6: convuwl */
+    var45.i = (orc_uint16) var37.i;
+    /* 7: loadw */
+    var38 = ptr7[i];
+    /* 8: convuwl */
+    var46.i = (orc_uint16) var38.i;
+    /* 9: addl */
+    var47.i = ((orc_uint32) var45.i) + ((orc_uint32) var46.i);
+    /* 10: shll */
+    var48.i = ((orc_uint32) var47.i) << 2;
+    /* 11: loadw */
+    var39 = ptr6[i];
+    /* 12: convuwl */
+    var49.i = (orc_uint16) var39.i;
+    /* 13: shll */
+    var50.i = ((orc_uint32) var49.i) << 1;
+    /* 14: subl */
+    var51.i = ((orc_uint32) var48.i) - ((orc_uint32) var44.i);
+    /* 15: addl */
+    var52.i = ((orc_uint32) var51.i) + ((orc_uint32) var50.i);
+    /* 17: addl */
+    var53.i = ((orc_uint32) var52.i) + ((orc_uint32) var40.i);
+    /* 18: shrsl */
+    var54.i = var53.i >> 3;
+    /* 19: convsuslw */
+    var41.i = ORC_CLAMP_UW (var54.i);
+    /* 20: storew */
+    ptr0[i] = var41;
+  }
+
+}
+
+#else
+static void
+_backup_deinterlace_line_vfir_16bits (OrcExecutor * ORC_RESTRICT ex)
+{
+  int i;
+  int n = ex->n;
+  orc_union16 *ORC_RESTRICT ptr0;
+  const orc_union16 *ORC_RESTRICT ptr4;
+  const orc_union16 *ORC_RESTRICT ptr5;
+  const orc_union16 *ORC_RESTRICT ptr6;
+  const orc_union16 *ORC_RESTRICT ptr7;
+  const orc_union16 *ORC_RESTRICT ptr8;
+  orc_union16 var35;
+  orc_union16 var36;
+  orc_union16 var37;
+  orc_union16 var38;
+  orc_union16 var39;
+#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__)
+  volatile orc_union32 var40;
+#else
+  orc_union32 var40;
+#endif
+  orc_union16 var41;
+  orc_union32 var42;
+  orc_union32 var43;
+  orc_union32 var44;
+  orc_union32 var45;
+  orc_union32 var46;
+  orc_union32 var47;
+  orc_union32 var48;
+  orc_union32 var49;
+  orc_union32 var50;
+  orc_union32 var51;
+  orc_union32 var52;
+  orc_union32 var53;
+  orc_union32 var54;
+
+  ptr0 = (orc_union16 *) ex->arrays[0];
+  ptr4 = (orc_union16 *) ex->arrays[4];
+  ptr5 = (orc_union16 *) ex->arrays[5];
+  ptr6 = (orc_union16 *) ex->arrays[6];
+  ptr7 = (orc_union16 *) ex->arrays[7];
+  ptr8 = (orc_union16 *) ex->arrays[8];
+
+  /* 16: loadpl */
+  var40.i = 0x00000004;         /* 4 or 1.97626e-323f */
+
+  for (i = 0; i < n; i++) {
+    /* 0: loadw */
+    var35 = ptr4[i];
+    /* 1: convuwl */
+    var42.i = (orc_uint16) var35.i;
+    /* 2: loadw */
+    var36 = ptr8[i];
+    /* 3: convuwl */
+    var43.i = (orc_uint16) var36.i;
+    /* 4: addl */
+    var44.i = ((orc_uint32) var42.i) + ((orc_uint32) var43.i);
+    /* 5: loadw */
+    var37 = ptr5[i];
+    /* 6: convuwl */
+    var45.i = (orc_uint16) var37.i;
+    /* 7: loadw */
+    var38 = ptr7[i];
+    /* 8: convuwl */
+    var46.i = (orc_uint16) var38.i;
+    /* 9: addl */
+    var47.i = ((orc_uint32) var45.i) + ((orc_uint32) var46.i);
+    /* 10: shll */
+    var48.i = ((orc_uint32) var47.i) << 2;
+    /* 11: loadw */
+    var39 = ptr6[i];
+    /* 12: convuwl */
+    var49.i = (orc_uint16) var39.i;
+    /* 13: shll */
+    var50.i = ((orc_uint32) var49.i) << 1;
+    /* 14: subl */
+    var51.i = ((orc_uint32) var48.i) - ((orc_uint32) var44.i);
+    /* 15: addl */
+    var52.i = ((orc_uint32) var51.i) + ((orc_uint32) var50.i);
+    /* 17: addl */
+    var53.i = ((orc_uint32) var52.i) + ((orc_uint32) var40.i);
+    /* 18: shrsl */
+    var54.i = var53.i >> 3;
+    /* 19: convsuslw */
+    var41.i = ORC_CLAMP_UW (var54.i);
+    /* 20: storew */
+    ptr0[i] = var41;
+  }
+
+}
+
+void
+deinterlace_line_vfir_16bits (guint16 * ORC_RESTRICT d1,
+    const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2,
+    const guint16 * ORC_RESTRICT s3, const guint16 * ORC_RESTRICT s4,
+    const guint16 * ORC_RESTRICT s5, int n)
+{
+  OrcExecutor _ex, *ex = &_ex;
+  static volatile int p_inited = 0;
+  static OrcCode *c = 0;
+  void (*func) (OrcExecutor *);
+
+  if (!p_inited) {
+    orc_once_mutex_lock ();
+    if (!p_inited) {
+      OrcProgram *p;
+
+#if 1
+      static const orc_uint8 bc[] = {
+        1, 9, 28, 100, 101, 105, 110, 116, 101, 114, 108, 97, 99, 101, 95, 108,
+        105, 110, 101, 95, 118, 102, 105, 114, 95, 49, 54, 98, 105, 116, 115,
+        11,
+        2, 2, 12, 2, 2, 12, 2, 2, 12, 2, 2, 12, 2, 2, 12, 2,
+        2, 14, 4, 2, 0, 0, 0, 14, 4, 1, 0, 0, 0, 14, 4, 4,
+        0, 0, 0, 14, 4, 3, 0, 0, 0, 20, 4, 20, 4, 20, 4, 154,
+        32, 4, 154, 33, 8, 103, 32, 32, 33, 154, 33, 5, 154, 34, 7, 103,
+        33, 33, 34, 124, 33, 33, 16, 154, 34, 6, 124, 34, 34, 17, 129, 33,
+        33, 32, 103, 33, 33, 34, 103, 33, 33, 18, 125, 33, 33, 19, 166, 0,
+        33, 2, 0,
+      };
+      p = orc_program_new_from_static_bytecode (bc);
+      orc_program_set_backup_function (p, _backup_deinterlace_line_vfir_16bits);
+#else
+      p = orc_program_new ();
+      orc_program_set_name (p, "deinterlace_line_vfir_16bits");
+      orc_program_set_backup_function (p, _backup_deinterlace_line_vfir_16bits);
+      orc_program_add_destination (p, 2, "d1");
+      orc_program_add_source (p, 2, "s1");
+      orc_program_add_source (p, 2, "s2");
+      orc_program_add_source (p, 2, "s3");
+      orc_program_add_source (p, 2, "s4");
+      orc_program_add_source (p, 2, "s5");
+      orc_program_add_constant (p, 4, 0x00000002, "c1");
+      orc_program_add_constant (p, 4, 0x00000001, "c2");
+      orc_program_add_constant (p, 4, 0x00000004, "c3");
+      orc_program_add_constant (p, 4, 0x00000003, "c4");
+      orc_program_add_temporary (p, 4, "t1");
+      orc_program_add_temporary (p, 4, "t2");
+      orc_program_add_temporary (p, 4, "t3");
+
+      orc_program_append_2 (p, "convuwl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "convuwl", 0, ORC_VAR_T2, ORC_VAR_S5, ORC_VAR_D1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "addl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T2,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "convuwl", 0, ORC_VAR_T2, ORC_VAR_S2, ORC_VAR_D1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "convuwl", 0, ORC_VAR_T3, ORC_VAR_S4, ORC_VAR_D1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "addl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_T3,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "shll", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "convuwl", 0, ORC_VAR_T3, ORC_VAR_S3, ORC_VAR_D1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "shll", 0, ORC_VAR_T3, ORC_VAR_T3, ORC_VAR_C2,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "subl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_T1,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "addl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_T3,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "addl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C3,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "shrsl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C4,
+          ORC_VAR_D1);
+      orc_program_append_2 (p, "convsuslw", 0, ORC_VAR_D1, ORC_VAR_T2,
+          ORC_VAR_D1, ORC_VAR_D1);
+#endif
+
+      orc_program_compile (p);
+      c = orc_program_take_code (p);
+      orc_program_free (p);
+    }
+    p_inited = TRUE;
+    orc_once_mutex_unlock ();
+  }
+  ex->arrays[ORC_VAR_A2] = c;
+  ex->program = 0;
+
+  ex->n = n;
+  ex->arrays[ORC_VAR_D1] = d1;
+  ex->arrays[ORC_VAR_S1] = (void *) s1;
+  ex->arrays[ORC_VAR_S2] = (void *) s2;
+  ex->arrays[ORC_VAR_S3] = (void *) s3;
+  ex->arrays[ORC_VAR_S4] = (void *) s4;
+  ex->arrays[ORC_VAR_S5] = (void *) s5;
+
+  func = c->exec;
+  func (ex);
+}
+#endif
+
+
 /* deinterlace_line_linear */
 #ifdef DISABLE_ORC
 void
@@ -558,6 +854,128 @@ deinterlace_line_linear (guint8 * ORC_RESTRICT d1,
 #endif
 
 
+/* deinterlace_line_linear_16bits */
+#ifdef DISABLE_ORC
+void
+deinterlace_line_linear_16bits (guint16 * ORC_RESTRICT d1,
+    const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2, int n)
+{
+  int i;
+  orc_union16 *ORC_RESTRICT ptr0;
+  const orc_union16 *ORC_RESTRICT ptr4;
+  const orc_union16 *ORC_RESTRICT ptr5;
+  orc_union16 var32;
+  orc_union16 var33;
+  orc_union16 var34;
+
+  ptr0 = (orc_union16 *) d1;
+  ptr4 = (orc_union16 *) s1;
+  ptr5 = (orc_union16 *) s2;
+
+
+  for (i = 0; i < n; i++) {
+    /* 0: loadw */
+    var32 = ptr4[i];
+    /* 1: loadw */
+    var33 = ptr5[i];
+    /* 2: avguw */
+    var34.i = ((orc_uint16) var32.i + (orc_uint16) var33.i + 1) >> 1;
+    /* 3: storew */
+    ptr0[i] = var34;
+  }
+
+}
+
+#else
+static void
+_backup_deinterlace_line_linear_16bits (OrcExecutor * ORC_RESTRICT ex)
+{
+  int i;
+  int n = ex->n;
+  orc_union16 *ORC_RESTRICT ptr0;
+  const orc_union16 *ORC_RESTRICT ptr4;
+  const orc_union16 *ORC_RESTRICT ptr5;
+  orc_union16 var32;
+  orc_union16 var33;
+  orc_union16 var34;
+
+  ptr0 = (orc_union16 *) ex->arrays[0];
+  ptr4 = (orc_union16 *) ex->arrays[4];
+  ptr5 = (orc_union16 *) ex->arrays[5];
+
+
+  for (i = 0; i < n; i++) {
+    /* 0: loadw */
+    var32 = ptr4[i];
+    /* 1: loadw */
+    var33 = ptr5[i];
+    /* 2: avguw */
+    var34.i = ((orc_uint16) var32.i + (orc_uint16) var33.i + 1) >> 1;
+    /* 3: storew */
+    ptr0[i] = var34;
+  }
+
+}
+
+void
+deinterlace_line_linear_16bits (guint16 * ORC_RESTRICT d1,
+    const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2, int n)
+{
+  OrcExecutor _ex, *ex = &_ex;
+  static volatile int p_inited = 0;
+  static OrcCode *c = 0;
+  void (*func) (OrcExecutor *);
+
+  if (!p_inited) {
+    orc_once_mutex_lock ();
+    if (!p_inited) {
+      OrcProgram *p;
+
+#if 1
+      static const orc_uint8 bc[] = {
+        1, 9, 30, 100, 101, 105, 110, 116, 101, 114, 108, 97, 99, 101, 95, 108,
+        105, 110, 101, 95, 108, 105, 110, 101, 97, 114, 95, 49, 54, 98, 105,
+        116,
+        115, 11, 2, 2, 12, 2, 2, 12, 2, 2, 76, 0, 4, 5, 2, 0,
+
+      };
+      p = orc_program_new_from_static_bytecode (bc);
+      orc_program_set_backup_function (p,
+          _backup_deinterlace_line_linear_16bits);
+#else
+      p = orc_program_new ();
+      orc_program_set_name (p, "deinterlace_line_linear_16bits");
+      orc_program_set_backup_function (p,
+          _backup_deinterlace_line_linear_16bits);
+      orc_program_add_destination (p, 2, "d1");
+      orc_program_add_source (p, 2, "s1");
+      orc_program_add_source (p, 2, "s2");
+
+      orc_program_append_2 (p, "avguw", 0, ORC_VAR_D1, ORC_VAR_S1, ORC_VAR_S2,
+          ORC_VAR_D1);
+#endif
+
+      orc_program_compile (p);
+      c = orc_program_take_code (p);
+      orc_program_free (p);
+    }
+    p_inited = TRUE;
+    orc_once_mutex_unlock ();
+  }
+  ex->arrays[ORC_VAR_A2] = c;
+  ex->program = 0;
+
+  ex->n = n;
+  ex->arrays[ORC_VAR_D1] = d1;
+  ex->arrays[ORC_VAR_S1] = (void *) s1;
+  ex->arrays[ORC_VAR_S2] = (void *) s2;
+
+  func = c->exec;
+  func (ex);
+}
+#endif
+
+
 /* deinterlace_line_linear_blend */
 #ifdef DISABLE_ORC
 void
diff --git a/gst/deinterlace/tvtime-dist.h b/gst/deinterlace/tvtime-dist.h
index adbc9014aff0b99c6b1b4922b0fc63737a065ced..23e8066ee6ea43bda0e5c6c04d063128773d946e 100644
--- a/gst/deinterlace/tvtime-dist.h
+++ b/gst/deinterlace/tvtime-dist.h
@@ -1,8 +1,7 @@
 
 /* autogenerated from tvtime.orc */
 
-#ifndef _TVTIME_H_
-#define _TVTIME_H_
+#pragma once
 
 #include <glib.h>
 
@@ -81,7 +80,9 @@ typedef union { orc_int64 i; double f; orc_int32 x2[2]; float x2f[2]; orc_int16
 #endif
 
 void deinterlace_line_vfir (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, const guint8 * ORC_RESTRICT s2, const guint8 * ORC_RESTRICT s3, const guint8 * ORC_RESTRICT s4, const guint8 * ORC_RESTRICT s5, int n);
+void deinterlace_line_vfir_16bits (guint16 * ORC_RESTRICT d1, const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2, const guint16 * ORC_RESTRICT s3, const guint16 * ORC_RESTRICT s4, const guint16 * ORC_RESTRICT s5, int n);
 void deinterlace_line_linear (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, const guint8 * ORC_RESTRICT s2, int n);
+void deinterlace_line_linear_16bits (guint16 * ORC_RESTRICT d1, const guint16 * ORC_RESTRICT s1, const guint16 * ORC_RESTRICT s2, int n);
 void deinterlace_line_linear_blend (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, const guint8 * ORC_RESTRICT s2, const guint8 * ORC_RESTRICT s3, int n);
 void deinterlace_line_greedy (orc_uint8 * ORC_RESTRICT d1, const orc_uint8 * ORC_RESTRICT s1, const orc_uint8 * ORC_RESTRICT s2, const orc_uint8 * ORC_RESTRICT s3, const orc_uint8 * ORC_RESTRICT s4, int p1, int n);
 
@@ -89,5 +90,3 @@ void deinterlace_line_greedy (orc_uint8 * ORC_RESTRICT d1, const orc_uint8 * ORC
 }
 #endif
 
-#endif
-
diff --git a/gst/deinterlace/tvtime.orc b/gst/deinterlace/tvtime.orc
index 44217e74db3d0e6a5e626ab416ec84297f753460..376d6e4ef8d81db748fa780b735af3cb7ae82851 100644
--- a/gst/deinterlace/tvtime.orc
+++ b/gst/deinterlace/tvtime.orc
@@ -26,6 +26,33 @@ shrsw t2, t2, 3
 convsuswb d1, t2
 
 
+.function deinterlace_line_vfir_16bits
+.dest 2 d1 guint16
+.source 2 s1 guint16
+.source 2 s2 guint16
+.source 2 s3 guint16
+.source 2 s4 guint16
+.source 2 s5 guint16
+.temp 4 t1
+.temp 4 t2
+.temp 4 t3
+
+convuwl t1, s1
+convuwl t2, s5
+addl t1, t1, t2
+convuwl t2, s2
+convuwl t3, s4
+addl t2, t2, t3
+shll t2, t2, 2
+convuwl t3, s3
+shll t3, t3, 1
+subl t2, t2, t1
+addl t2, t2, t3
+addl t2, t2, 4
+shrsl t2, t2, 3
+convsuslw d1, t2
+
+
 .function deinterlace_line_linear
 .dest 1 d1 guint8
 .source 1 s1 guint8
@@ -34,6 +61,14 @@ convsuswb d1, t2
 avgub d1, s1, s2
 
 
+.function deinterlace_line_linear_16bits
+.dest 2 d1 guint16
+.source 2 s1 guint16
+.source 2 s2 guint16
+
+avguw d1, s1, s2
+
+
 .function deinterlace_line_linear_blend
 .dest 1 d1 guint8
 .source 1 s1 guint8
diff --git a/gst/deinterlace/tvtime/greedy.c b/gst/deinterlace/tvtime/greedy.c
index 5a01cc6f3c4c4186cd0e4cd9b9e5a8e0969965e2..aa88e903124bacf34a301372a9c1b8dbdaed884a 100644
--- a/gst/deinterlace/tvtime/greedy.c
+++ b/gst/deinterlace/tvtime/greedy.c
@@ -74,7 +74,7 @@ deinterlace_greedy_interpolate_scanline_orc (GstDeinterlaceSimpleMethod * self,
 {
   guint max_comb = GST_DEINTERLACE_METHOD_GREEDY_L (self)->max_comb;
 
-  if (scanlines->m1 == NULL || scanlines->mp == NULL) {
+  if (scanlines->m1 == NULL) {
     deinterlace_line_linear (out, scanlines->t0, scanlines->b0, size);
   } else {
     deinterlace_line_greedy (out, scanlines->m1, scanlines->t0, scanlines->b0,
@@ -89,7 +89,7 @@ deinterlace_greedy_interpolate_scanline_orc_planar_u (GstDeinterlaceSimpleMethod
 {
   guint max_comb = GST_DEINTERLACE_METHOD_GREEDY_L (self)->max_comb;
 
-  if (scanlines->m1 == NULL || scanlines->mp == NULL) {
+  if (scanlines->m1 == NULL) {
     deinterlace_line_linear (out, scanlines->t0, scanlines->b0, size);
   } else {
     deinterlace_line_greedy (out, scanlines->m1, scanlines->t0, scanlines->b0,
@@ -104,7 +104,7 @@ deinterlace_greedy_interpolate_scanline_orc_planar_v (GstDeinterlaceSimpleMethod
 {
   guint max_comb = GST_DEINTERLACE_METHOD_GREEDY_L (self)->max_comb;
 
-  if (scanlines->m1 == NULL || scanlines->mp == NULL) {
+  if (scanlines->m1 == NULL) {
     deinterlace_line_linear (out, scanlines->t0, scanlines->b0, size);
   } else {
     deinterlace_line_greedy (out, scanlines->m1, scanlines->t0, scanlines->b0,
diff --git a/gst/deinterlace/tvtime/greedyh.c b/gst/deinterlace/tvtime/greedyh.c
index 84daa7e0d855313d0f28f6d0dc18cc1b64fc76f2..fe251b8ec35e544c1c5291106cf36c361a5d3f8d 100644
--- a/gst/deinterlace/tvtime/greedyh.c
+++ b/gst/deinterlace/tvtime/greedyh.c
@@ -725,26 +725,35 @@ deinterlace_frame_di_greedyh_plane (GstDeinterlaceMethodGreedyH * self,
   guint8 *Dest = GST_VIDEO_FRAME_COMP_DATA (outframe, plane);
   gint RowStride = GST_VIDEO_FRAME_COMP_STRIDE (outframe, plane);
   gint FieldHeight = GST_VIDEO_FRAME_COMP_HEIGHT (outframe, plane) / 2;
-  gint Pitch = RowStride * 2;
+  gint Pitch;
   const guint8 *L1;             // ptr to Line1, of 3
   const guint8 *L2;             // ptr to Line2, the weave line
   const guint8 *L3;             // ptr to Line3
   const guint8 *L2P;            // ptr to prev Line2
   gint InfoIsOdd;
   gint Line;
+  const GstVideoFrame *frame;
 
-  L1 = GST_VIDEO_FRAME_COMP_DATA (history[cur_field_idx].frame, plane);
-  if (history[cur_field_idx].flags & PICTURE_INTERLACED_BOTTOM)
-    L1 += RowStride;
+  frame = history[cur_field_idx].frame;
 
+  L1 = GST_VIDEO_FRAME_COMP_DATA (frame, plane);
   L2 = GST_VIDEO_FRAME_COMP_DATA (history[cur_field_idx + 1].frame, plane);
-  if (history[cur_field_idx + 1].flags & PICTURE_INTERLACED_BOTTOM)
-    L2 += RowStride;
+  L2P = GST_VIDEO_FRAME_COMP_DATA (history[cur_field_idx - 1].frame, plane);
+
+  if (GST_VIDEO_INFO_INTERLACE_MODE (&frame->info) ==
+      GST_VIDEO_INTERLACE_MODE_ALTERNATE) {
+    Pitch = RowStride;
+  } else {
+    Pitch = RowStride * 2;
+    if (history[cur_field_idx].flags & PICTURE_INTERLACED_BOTTOM)
+      L1 += RowStride;
+    if (history[cur_field_idx + 1].flags & PICTURE_INTERLACED_BOTTOM)
+      L2 += RowStride;
+    if (history[cur_field_idx - 1].flags & PICTURE_INTERLACED_BOTTOM)
+      L2P += RowStride;
+  }
 
   L3 = L1 + Pitch;
-  L2P = GST_VIDEO_FRAME_COMP_DATA (history[cur_field_idx - 1].frame, plane);
-  if (history[cur_field_idx - 1].flags & PICTURE_INTERLACED_BOTTOM)
-    L2P += RowStride;
 
   // copy first even line no matter what, and the first odd line if we're
   // processing an EVEN field. (note diff from other deint rtns.)
diff --git a/gst/deinterlace/tvtime/linear.c b/gst/deinterlace/tvtime/linear.c
index 9c45353e0ba683c93b2c184861478d6dbd7e63b3..36a6fce557dcd0c58ed1b681f74a71a304a2cdab 100644
--- a/gst/deinterlace/tvtime/linear.c
+++ b/gst/deinterlace/tvtime/linear.c
@@ -54,6 +54,13 @@ deinterlace_scanline_linear_c (GstDeinterlaceSimpleMethod * self,
   deinterlace_line_linear (out, s1, s2, size);
 }
 
+static void
+deinterlace_scanline_linear_c_16bits (GstDeinterlaceSimpleMethod * self,
+    guint16 * out, const guint16 * s1, const guint16 * s2, gint size)
+{
+  deinterlace_line_linear_16bits (out, s1, s2, size / 2);
+}
+
 static void
 deinterlace_scanline_linear_packed_c (GstDeinterlaceSimpleMethod * self,
     guint8 * out, const GstDeinterlaceScanlineData * scanlines, guint size)
@@ -82,6 +89,15 @@ deinterlace_scanline_linear_planar_v_c (GstDeinterlaceSimpleMethod * self,
   deinterlace_scanline_linear_c (self, out, scanlines->t0, scanlines->b0, size);
 }
 
+static void
+deinterlace_scanline_linear_planar_plane_16bits_c (GstDeinterlaceSimpleMethod *
+    self, guint8 * out, const GstDeinterlaceScanlineData * scanlines,
+    guint size)
+{
+  deinterlace_scanline_linear_c_16bits (self, (guint16 *) out,
+      (const guint16 *) scanlines->t0, (const guint16 *) scanlines->b0, size);
+}
+
 G_DEFINE_TYPE (GstDeinterlaceMethodLinear, gst_deinterlace_method_linear,
     GST_TYPE_DEINTERLACE_SIMPLE_METHOD);
 
@@ -117,6 +133,12 @@ gst_deinterlace_method_linear_class_init (GstDeinterlaceMethodLinearClass *
   dism_class->interpolate_scanline_planar_v =
       deinterlace_scanline_linear_planar_v_c;
 
+  dism_class->interpolate_scanline_planar_y_16bits =
+      deinterlace_scanline_linear_planar_plane_16bits_c;
+  dism_class->interpolate_scanline_planar_u_16bits =
+      deinterlace_scanline_linear_planar_plane_16bits_c;
+  dism_class->interpolate_scanline_planar_v_16bits =
+      deinterlace_scanline_linear_planar_plane_16bits_c;
 }
 
 static void
diff --git a/gst/deinterlace/tvtime/vfir.c b/gst/deinterlace/tvtime/vfir.c
index f77178c861128bc61ac24b436b149d1b4f69f6e6..390811b4a32ba851c246097d72cca19647d4beac 100644
--- a/gst/deinterlace/tvtime/vfir.c
+++ b/gst/deinterlace/tvtime/vfir.c
@@ -75,6 +75,19 @@ deinterlace_c (guint8 * dst, const guint8 * lum_m4, const guint8 * lum_m3,
   }
 }
 
+static inline void
+deinterlace_c_16bits (guint16 * dst, const guint16 * lum_m4,
+    const guint16 * lum_m3, const guint16 * lum_m2, const guint16 * lum_m1,
+    const guint16 * lum, gint size)
+{
+  if (lum_m2 == NULL) {
+    deinterlace_line_linear_16bits (dst, lum_m1, lum_m3, size / 2);
+  } else {
+    deinterlace_line_vfir_16bits (dst, lum_m4, lum_m3, lum_m2, lum_m1,
+        lum, size / 2);
+  }
+}
+
 static void
 deinterlace_line_packed_c (GstDeinterlaceSimpleMethod * self, guint8 * dst,
     const GstDeinterlaceScanlineData * scanlines, guint size)
@@ -127,6 +140,20 @@ deinterlace_line_planar_v_c (GstDeinterlaceSimpleMethod * self, guint8 * dst,
   deinterlace_c (dst, lum_m4, lum_m3, lum_m2, lum_m1, lum, size);
 }
 
+static void
+deinterlace_line_planar_plane_16bits_c (GstDeinterlaceSimpleMethod * self,
+    guint8 * dst, const GstDeinterlaceScanlineData * scanlines, guint size)
+{
+  const guint16 *lum_m4 = (const guint16 *) scanlines->tt1;
+  const guint16 *lum_m3 = (const guint16 *) scanlines->t0;
+  const guint16 *lum_m2 = (const guint16 *) scanlines->m1;
+  const guint16 *lum_m1 = (const guint16 *) scanlines->b0;
+  const guint16 *lum = (const guint16 *) scanlines->bb1;
+
+  deinterlace_c_16bits ((guint16 *) dst, lum_m4, lum_m3, lum_m2, lum_m1, lum,
+      size);
+}
+
 #undef BUILD_X86_ASM
 
 #ifdef BUILD_X86_ASM
@@ -306,6 +333,13 @@ gst_deinterlace_method_vfir_class_init (GstDeinterlaceMethodVFIRClass * klass)
   dism_class->interpolate_scanline_planar_u = deinterlace_line_planar_u_c;
   dism_class->interpolate_scanline_planar_v = deinterlace_line_planar_v_c;
 #endif
+
+  dism_class->interpolate_scanline_planar_y_16bits =
+      deinterlace_line_planar_plane_16bits_c;
+  dism_class->interpolate_scanline_planar_u_16bits =
+      deinterlace_line_planar_plane_16bits_c;
+  dism_class->interpolate_scanline_planar_v_16bits =
+      deinterlace_line_planar_plane_16bits_c;
 }
 
 static void
diff --git a/gst/deinterlace/tvtime/weave.c b/gst/deinterlace/tvtime/weave.c
index 804c889f0710ebf49b02a37d77c8f19f7d8b936c..1b2c3075a49066ab310da7d3bcf6e576bae9033e 100644
--- a/gst/deinterlace/tvtime/weave.c
+++ b/gst/deinterlace/tvtime/weave.c
@@ -150,6 +150,14 @@ gst_deinterlace_method_weave_class_init (GstDeinterlaceMethodWeaveClass * klass)
   dism_class->interpolate_scanline_planar_v =
       deinterlace_scanline_weave_planar_v;
 
+  /* we do just memcpy, nothing different */
+  dism_class->interpolate_scanline_planar_y_16bits =
+      deinterlace_scanline_weave_planar_y;
+  dism_class->interpolate_scanline_planar_u_16bits =
+      deinterlace_scanline_weave_planar_u;
+  dism_class->interpolate_scanline_planar_v_16bits =
+      deinterlace_scanline_weave_planar_v;
+
   dism_class->copy_scanline_ayuv = copy_scanline_packed;
   dism_class->copy_scanline_yuy2 = copy_scanline_packed;
   dism_class->copy_scanline_yvyu = copy_scanline_packed;
diff --git a/gst/deinterlace/tvtime/weavebff.c b/gst/deinterlace/tvtime/weavebff.c
index 7424e96fed1333648605b87ee6750238101cb500..d0be4c70e4e33d93ef0fe06f139826517a1f7fa6 100644
--- a/gst/deinterlace/tvtime/weavebff.c
+++ b/gst/deinterlace/tvtime/weavebff.c
@@ -151,6 +151,14 @@ gst_deinterlace_method_weave_bff_class_init (GstDeinterlaceMethodWeaveBFFClass *
   dism_class->interpolate_scanline_planar_v =
       deinterlace_scanline_weave_planar_v;
 
+  /* we do just memcpy, nothing different */
+  dism_class->interpolate_scanline_planar_y_16bits =
+      deinterlace_scanline_weave_planar_y;
+  dism_class->interpolate_scanline_planar_u_16bits =
+      deinterlace_scanline_weave_planar_u;
+  dism_class->interpolate_scanline_planar_v_16bits =
+      deinterlace_scanline_weave_planar_v;
+
   dism_class->copy_scanline_ayuv = copy_scanline_packed;
   dism_class->copy_scanline_yuy2 = copy_scanline_packed;
   dism_class->copy_scanline_yvyu = copy_scanline_packed;
diff --git a/gst/deinterlace/tvtime/weavetff.c b/gst/deinterlace/tvtime/weavetff.c
index f33bb2e5518f9ea0355d16b86c95d21e03c2724f..19692562741b0a47ae22b585bd852b2354e8a752 100644
--- a/gst/deinterlace/tvtime/weavetff.c
+++ b/gst/deinterlace/tvtime/weavetff.c
@@ -152,6 +152,14 @@ gst_deinterlace_method_weave_tff_class_init (GstDeinterlaceMethodWeaveTFFClass *
   dism_class->interpolate_scanline_planar_v =
       deinterlace_scanline_weave_planar_v;
 
+  /* we do just memcpy, nothing different */
+  dism_class->interpolate_scanline_planar_y_16bits =
+      deinterlace_scanline_weave_planar_y;
+  dism_class->interpolate_scanline_planar_u_16bits =
+      deinterlace_scanline_weave_planar_u;
+  dism_class->interpolate_scanline_planar_v_16bits =
+      deinterlace_scanline_weave_planar_v;
+
   dism_class->copy_scanline_ayuv = copy_scanline_packed;
   dism_class->copy_scanline_yuy2 = copy_scanline_packed;
   dism_class->copy_scanline_yvyu = copy_scanline_packed;
diff --git a/gst/deinterlace/yadif.c b/gst/deinterlace/yadif.c
index d29c72fc77bbfc63f64ff4dc11810e8afe35ba74..4f271dde02d2815b669caff55ea1efb4d12ec3c4 100644
--- a/gst/deinterlace/yadif.c
+++ b/gst/deinterlace/yadif.c
@@ -76,6 +76,10 @@ static void
 filter_scanline_yadif_planar (GstDeinterlaceSimpleMethod * self,
     guint8 * out, const GstDeinterlaceScanlineData * scanlines, guint size);
 
+static void
+filter_scanline_yadif_planar_16bits (GstDeinterlaceSimpleMethod * self,
+    guint8 * out, const GstDeinterlaceScanlineData * scanlines, guint size);
+
 static void
 filter_scanline_yadif_semiplanar (GstDeinterlaceSimpleMethod * self,
     guint8 * out, const GstDeinterlaceScanlineData * scanlines, guint size);
@@ -114,6 +118,24 @@ filter_line_c_planar_mode2 (void *ORC_RESTRICT dst,
     const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
     const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w);
 
+static void
+filter_line_c_planar_mode0_16bits (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
+    const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
+    const void *ORC_RESTRICT ttwo, const void *ORC_RESTRICT btwo,
+    const void *ORC_RESTRICT tptwo, const void *ORC_RESTRICT bptwo,
+    const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
+    const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w);
+
+static void
+filter_line_c_planar_mode2_16bits (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
+    const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
+    const void *ORC_RESTRICT ttwo, const void *ORC_RESTRICT btwo,
+    const void *ORC_RESTRICT tptwo, const void *ORC_RESTRICT bptwo,
+    const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
+    const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w);
+
 static void (*filter_mode2) (void *ORC_RESTRICT dst,
     const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
     const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
@@ -130,6 +152,21 @@ static void (*filter_mode0) (void *ORC_RESTRICT dst,
     const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
     const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w);
 
+static void (*filter_mode2_16bits) (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
+    const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
+    const void *ORC_RESTRICT ttwo, const void *ORC_RESTRICT btwo,
+    const void *ORC_RESTRICT tptwo, const void *ORC_RESTRICT bptwo,
+    const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
+    const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w);
+
+static void (*filter_mode0_16bits) (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
+    const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
+    const void *ORC_RESTRICT ttwo, const void *ORC_RESTRICT btwo,
+    const void *ORC_RESTRICT tptwo, const void *ORC_RESTRICT bptwo,
+    const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
+    const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w);
 
 static void
 copy_scanline (GstDeinterlaceSimpleMethod * self, guint8 * out,
@@ -166,6 +203,9 @@ static void
   dism_class->copy_scanline_bgr = copy_scanline;
   dism_class->copy_scanline_nv12 = copy_scanline;
   dism_class->copy_scanline_nv21 = copy_scanline;
+  dism_class->copy_scanline_planar_y_16bits = copy_scanline;
+  dism_class->copy_scanline_planar_u_16bits = copy_scanline;
+  dism_class->copy_scanline_planar_v_16bits = copy_scanline;
 
   dism_class->interpolate_scanline_planar_y = filter_scanline_yadif_planar;
   dism_class->interpolate_scanline_planar_u = filter_scanline_yadif_planar;
@@ -182,6 +222,12 @@ static void
   dism_class->interpolate_scanline_bgr = filter_scanline_yadif_packed_3;
   dism_class->interpolate_scanline_nv12 = filter_scanline_yadif_semiplanar;
   dism_class->interpolate_scanline_nv21 = filter_scanline_yadif_semiplanar;
+  dism_class->interpolate_scanline_planar_y_16bits =
+      filter_scanline_yadif_planar_16bits;
+  dism_class->interpolate_scanline_planar_u_16bits =
+      filter_scanline_yadif_planar_16bits;
+  dism_class->interpolate_scanline_planar_v_16bits =
+      filter_scanline_yadif_planar_16bits;
 }
 
 #define FFABS(a) ABS(a)
@@ -202,7 +248,7 @@ static void
 /* The is_not_edge argument here controls when the code will enter a branch
  * which reads up to and including x-3 and x+3. */
 
-#define FILTER(start, end, is_not_edge) \
+#define FILTER(start, end, is_not_edge) G_STMT_START { \
     for (x = start;  x < end; x++) { \
         int c = stzero[x]; \
         int d = (smone[x] + smp[x])>>1; \
@@ -240,7 +286,8 @@ static void
  \
         sdst[x] = spatial_pred; \
  \
-    }
+    } \
+} G_STMT_END
 
 ALWAYS_INLINE static void
 filter_line_c (guint8 * sdst, const guint8 * stzero, const guint8 * sbzero,
@@ -257,7 +304,7 @@ filter_line_c (guint8 * sdst, const guint8 * stzero, const guint8 * sbzero,
    * This allows the FILTER macro to be
    * called so that it processes all the pixels normally.  A constant value of
    * true for is_not_edge lets the compiler ignore the if statement. */
-  FILTER (start, end, 1)
+  FILTER (start, end, 1);
 }
 
 #define MAX_ALIGN 8
@@ -295,7 +342,43 @@ filter_line_c_planar (void *ORC_RESTRICT dst, const void *ORC_RESTRICT tzero,
    * This allows the FILTER macro to be
    * called so that it processes all the pixels normally.  A constant value of
    * true for is_not_edge lets the compiler ignore the if statement. */
-  FILTER (start, end, 1)
+  FILTER (start, end, 1);
+}
+
+ALWAYS_INLINE static void
+filter_line_c_planar_16bits (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero,
+    const void *ORC_RESTRICT bzero, const void *ORC_RESTRICT mone,
+    const void *ORC_RESTRICT mp, const void *ORC_RESTRICT ttwo,
+    const void *ORC_RESTRICT btwo, const void *ORC_RESTRICT tptwo,
+    const void *ORC_RESTRICT bptwo, const void *ORC_RESTRICT ttone,
+    const void *ORC_RESTRICT ttp, const void *ORC_RESTRICT bbone,
+    const void *ORC_RESTRICT bbp, int w, int mode)
+{
+  int x;
+  const int start = 0;
+  const int colors = 1;
+  const int y_alternates_every = 0;
+  const int end = w;
+  guint16 *sdst = (guint16 *) dst + 3;
+  guint16 *stzero = (guint16 *) tzero + 3;
+  guint16 *sbzero = (guint16 *) bzero + 3;
+  guint16 *smone = (guint16 *) mone + 3;
+  guint16 *smp = (guint16 *) mp + 3;
+  guint16 *sttwo = (guint16 *) ttwo + 3;
+  guint16 *sbtwo = (guint16 *) btwo + 3;
+  guint16 *stptwo = (guint16 *) tptwo + 3;
+  guint16 *sbptwo = (guint16 *) bptwo + 3;
+  guint16 *sttone = (guint16 *) ttone + 3;
+  guint16 *sttp = (guint16 *) ttp + 3;
+  guint16 *sbbone = (guint16 *) bbone + 3;
+  guint16 *sbbp = (guint16 *) bbp + 3;
+  /* The function is called for processing the middle
+   * pixels of each line, excluding 3 at each end.
+   * This allows the FILTER macro to be
+   * called so that it processes all the pixels normally.  A constant value of
+   * true for is_not_edge lets the compiler ignore the if statement. */
+  FILTER (start, end, 1);
 }
 
 ALWAYS_INLINE G_GNUC_UNUSED static void
@@ -324,6 +407,32 @@ filter_line_c_planar_mode2 (void *ORC_RESTRICT dst,
       ttone, ttp, bbone, bbp, w, 2);
 }
 
+ALWAYS_INLINE G_GNUC_UNUSED static void
+filter_line_c_planar_mode0_16bits (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
+    const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
+    const void *ORC_RESTRICT ttwo, const void *ORC_RESTRICT btwo,
+    const void *ORC_RESTRICT tptwo, const void *ORC_RESTRICT bptwo,
+    const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
+    const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w)
+{
+  filter_line_c_planar_16bits (dst, tzero, bzero, mone, mp, ttwo, btwo, tptwo,
+      bptwo, ttone, ttp, bbone, bbp, w, 0);
+}
+
+ALWAYS_INLINE G_GNUC_UNUSED static void
+filter_line_c_planar_mode2_16bits (void *ORC_RESTRICT dst,
+    const void *ORC_RESTRICT tzero, const void *ORC_RESTRICT bzero,
+    const void *ORC_RESTRICT mone, const void *ORC_RESTRICT mp,
+    const void *ORC_RESTRICT ttwo, const void *ORC_RESTRICT btwo,
+    const void *ORC_RESTRICT tptwo, const void *ORC_RESTRICT bptwo,
+    const void *ORC_RESTRICT ttone, const void *ORC_RESTRICT ttp,
+    const void *ORC_RESTRICT bbone, const void *ORC_RESTRICT bbp, int w)
+{
+  filter_line_c_planar_16bits (dst, tzero, bzero, mone, mp, ttwo, btwo, tptwo,
+      bptwo, ttone, ttp, bbone, bbp, w, 2);
+}
+
 ALWAYS_INLINE static void
 filter_edges (guint8 * sdst, const guint8 * stzero, const guint8 * sbzero,
     const guint8 * smone, const guint8 * smp, const guint8 * sttwo,
@@ -338,9 +447,42 @@ filter_edges (guint8 * sdst, const guint8 * stzero, const guint8 * sbzero,
 
   /* Only edge pixels need to be processed here.  A constant value of false
    * for is_not_edge should let the compiler ignore the whole branch. */
-  FILTER (0, border, 0)
-      FILTER (w - edge, w - border, 1)
-      FILTER (w - border, w, 0)
+  FILTER (0, border, 0);
+  FILTER (w - edge, w - border, 1);
+  FILTER (w - border, w, 0);
+}
+
+ALWAYS_INLINE static void
+filter_edges_16bits (guint8 * sdst_, const guint8 * stzero_,
+    const guint8 * sbzero_,
+    const guint8 * smone_, const guint8 * smp_, const guint8 * sttwo_,
+    const guint8 * sbtwo_, const guint8 * stptwo_, const guint8 * sbptwo_,
+    const guint8 * sttone_, const guint8 * sttp_, const guint8 * sbbone_,
+    const guint8 * sbbp_, int w, int colors, int y_alternates_every,
+    int mode, const int bpp)
+{
+  int x;
+  const int edge = colors * (MAX_ALIGN / bpp);
+  const int border = 3 * colors;
+  guint16 *sdst = (guint16 *) sdst_;
+  guint16 *stzero = (guint16 *) stzero_;
+  guint16 *sbzero = (guint16 *) sbzero_;
+  guint16 *smone = (guint16 *) smone_;
+  guint16 *smp = (guint16 *) smp_;
+  guint16 *sttwo = (guint16 *) sttwo_;
+  guint16 *sbtwo = (guint16 *) sbtwo_;
+  guint16 *stptwo = (guint16 *) stptwo_;
+  guint16 *sbptwo = (guint16 *) sbptwo_;
+  guint16 *sttone = (guint16 *) sttone_;
+  guint16 *sttp = (guint16 *) sttp_;
+  guint16 *sbbone = (guint16 *) sbbone_;
+  guint16 *sbbp = (guint16 *) sbbp_;
+
+  /* Only edge pixels need to be processed here.  A constant value of false
+   * for is_not_edge should let the compiler ignore the whole branch. */
+  FILTER (0, border, 0);
+  FILTER (w - edge, w - border, 1);
+  FILTER (w - border, w, 0);
 }
 
 static void
@@ -450,9 +592,49 @@ filter_scanline_yadif_planar (GstDeinterlaceSimpleMethod * self,
         (void *) s.bbp, w - edge);
 }
 
+ALWAYS_INLINE static void
+filter_scanline_yadif_planar_16bits (GstDeinterlaceSimpleMethod * self,
+    guint8 * out, const GstDeinterlaceScanlineData * s_orig, guint size)
+{
+  guint8 *dst = out;
+  const int bpp = 2;
+  int w = size / bpp;
+  int edge = MAX_ALIGN / bpp;
+  GstDeinterlaceScanlineData s = *s_orig;
+
+  int mode = (s.tt1 == NULL || s.bb1 == NULL || s.ttp == NULL
+      || s.bbp == NULL) ? 2 : 0;
+
+  /* When starting up, some data might not yet be available, so use the current frame */
+  if (s.m1 == NULL)
+    s.m1 = s.mp;
+  if (s.tt1 == NULL)
+    s.tt1 = s.ttp;
+  if (s.bb1 == NULL)
+    s.bb1 = s.bbp;
+  if (s.t2 == NULL)
+    s.t2 = s.tp2;
+  if (s.b2 == NULL)
+    s.b2 = s.bp2;
+
+  filter_edges_16bits (dst, s.t0, s.b0, s.m1, s.mp, s.t2, s.b2, s.tp2, s.bp2,
+      s.tt1, s.ttp, s.bb1, s.bbp, w, 1, 0, mode, bpp);
+  if (mode == 0)
+    filter_mode0_16bits (dst, (void *) s.t0, (void *) s.b0, (void *) s.m1,
+        (void *) s.mp, (void *) s.t2, (void *) s.b2, (void *) s.tp2,
+        (void *) s.bp2, (void *) s.tt1, (void *) s.ttp, (void *) s.bb1,
+        (void *) s.bbp, w - edge);
+  else
+    filter_mode2_16bits (dst, (void *) s.t0, (void *) s.b0, (void *) s.m1,
+        (void *) s.mp, (void *) s.t2, (void *) s.b2, (void *) s.tp2,
+        (void *) s.bp2, (void *) s.tt1, (void *) s.ttp, (void *) s.bb1,
+        (void *) s.bbp, w - edge);
+}
+
 static void
 gst_deinterlace_method_yadif_init (GstDeinterlaceMethodYadif * self)
 {
+  /* TODO: add asm support for high bitdepth */
 #if (defined __x86_64__ || defined _M_X64) && defined HAVE_NASM
   if (
 #  if defined HAVE_ORC
@@ -466,16 +648,22 @@ gst_deinterlace_method_yadif_init (GstDeinterlaceMethodYadif * self)
     GST_DEBUG ("SSSE3 optimization enabled");
     filter_mode0 = gst_yadif_filter_line_mode0_ssse3;
     filter_mode2 = gst_yadif_filter_line_mode2_ssse3;
+    filter_mode0_16bits = filter_line_c_planar_mode0_16bits;
+    filter_mode2_16bits = filter_line_c_planar_mode2_16bits;
   } else {
     GST_DEBUG ("SSE2 optimization enabled");
     filter_mode0 = gst_yadif_filter_line_mode0_sse2;
     filter_mode2 = gst_yadif_filter_line_mode2_sse2;
+    filter_mode0_16bits = filter_line_c_planar_mode0_16bits;
+    filter_mode2_16bits = filter_line_c_planar_mode2_16bits;
   }
 #else
   {
     GST_DEBUG ("SSE optimization disabled");
     filter_mode0 = filter_line_c_planar_mode0;
     filter_mode2 = filter_line_c_planar_mode2;
+    filter_mode0_16bits = filter_line_c_planar_mode0_16bits;
+    filter_mode2_16bits = filter_line_c_planar_mode2_16bits;
   }
 #endif
 }
diff --git a/gst/dtmf/gstdtmfsrc.c b/gst/dtmf/gstdtmfsrc.c
index 1945c0f79558dc4dbcdae717332f6441751798ec..cd46da95e97e462a97011f36208397cfe5aa7a25 100644
--- a/gst/dtmf/gstdtmfsrc.c
+++ b/gst/dtmf/gstdtmfsrc.c
@@ -247,7 +247,7 @@ static void
 event_free (GstDTMFSrcEvent * event)
 {
   if (event)
-    g_slice_free (GstDTMFSrcEvent, event);
+    g_free (event);
 }
 
 static void
@@ -481,7 +481,7 @@ gst_dtmf_src_add_start_event (GstDTMFSrc * dtmfsrc, gint event_number,
     gint event_volume)
 {
 
-  GstDTMFSrcEvent *event = g_slice_new0 (GstDTMFSrcEvent);
+  GstDTMFSrcEvent *event = g_new0 (GstDTMFSrcEvent, 1);
   event->event_type = DTMF_EVENT_TYPE_START;
   event->sample = 0;
   event->event_number = CLAMP (event_number, MIN_EVENT, MAX_EVENT);
@@ -494,7 +494,7 @@ static void
 gst_dtmf_src_add_stop_event (GstDTMFSrc * dtmfsrc)
 {
 
-  GstDTMFSrcEvent *event = g_slice_new0 (GstDTMFSrcEvent);
+  GstDTMFSrcEvent *event = g_new0 (GstDTMFSrcEvent, 1);
   event->event_type = DTMF_EVENT_TYPE_STOP;
   event->sample = 0;
   event->event_number = 0;
@@ -689,7 +689,7 @@ gst_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset,
           break;
       }
       if (event)
-        g_slice_free (GstDTMFSrcEvent, event);
+        g_free (event);
     } else if (dtmfsrc->last_event->packet_count * dtmfsrc->interval >=
         MIN_DUTY_CYCLE) {
       event = g_async_queue_try_pop (dtmfsrc->event_queue);
@@ -703,7 +703,7 @@ gst_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset,
             gst_dtmf_src_post_message (dtmfsrc, "dtmf-event-dropped", event);
             break;
           case DTMF_EVENT_TYPE_STOP:
-            g_slice_free (GstDTMFSrcEvent, dtmfsrc->last_event);
+            g_free (dtmfsrc->last_event);
             dtmfsrc->last_event = NULL;
             gst_dtmf_src_post_message (dtmfsrc, "dtmf-event-processed", event);
             break;
@@ -723,7 +723,7 @@ gst_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset,
 
             break;
         }
-        g_slice_free (GstDTMFSrcEvent, event);
+        g_free (event);
       }
     }
   } while (dtmfsrc->last_event == NULL);
@@ -773,7 +773,7 @@ paused:
   if (dtmfsrc->last_event) {
     GST_DEBUG_OBJECT (dtmfsrc, "Stopping current event");
     /* Don't forget to release the stream lock */
-    g_slice_free (GstDTMFSrcEvent, dtmfsrc->last_event);
+    g_free (dtmfsrc->last_event);
     dtmfsrc->last_event = NULL;
   }
 
@@ -797,7 +797,7 @@ gst_dtmf_src_unlock (GstBaseSrc * src)
   GST_OBJECT_UNLOCK (dtmfsrc);
 
   GST_DEBUG_OBJECT (dtmfsrc, "Pushing the PAUSE_TASK event on unlock request");
-  event = g_slice_new0 (GstDTMFSrcEvent);
+  event = g_new0 (GstDTMFSrcEvent, 1);
   event->event_type = DTMF_EVENT_TYPE_PAUSE_TASK;
   g_async_queue_push (dtmfsrc->event_queue, event);
 
@@ -902,7 +902,7 @@ gst_dtmf_src_change_state (GstElement * element, GstStateChange transition)
 
       while (event != NULL) {
         gst_dtmf_src_post_message (dtmfsrc, "dtmf-event-dropped", event);
-        g_slice_free (GstDTMFSrcEvent, event);
+        g_free (event);
         event = g_async_queue_try_pop (dtmfsrc->event_queue);
       }
       dtmfsrc->last_event_was_start = FALSE;
@@ -929,7 +929,7 @@ gst_dtmf_src_change_state (GstElement * element, GstStateChange transition)
 
       while (event != NULL) {
         gst_dtmf_src_post_message (dtmfsrc, "dtmf-event-dropped", event);
-        g_slice_free (GstDTMFSrcEvent, event);
+        g_free (event);
         event = g_async_queue_try_pop (dtmfsrc->event_queue);
       }
       dtmfsrc->last_event_was_start = FALSE;
diff --git a/gst/dtmf/gstrtpdtmfdepay.c b/gst/dtmf/gstrtpdtmfdepay.c
index a819467a6870b3bd2fa3ad5b05dbeaa9f9b49c3e..16dd455b4a85b03d76667c3d0e5bccbaf1bf0c39 100644
--- a/gst/dtmf/gstrtpdtmfdepay.c
+++ b/gst/dtmf/gstrtpdtmfdepay.c
@@ -138,9 +138,11 @@ static GstStaticPadTemplate gst_rtp_dtmf_depay_src_template =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/x-raw, "
-        "format = (string) \"" GST_AUDIO_NE (S16) "\", "
-        "rate = " GST_AUDIO_RATE_RANGE ", " "channels = (int) 1")
+    GST_STATIC_CAPS ("audio/x-raw, "    //
+        "format = (string) " GST_AUDIO_NE (S16) ", "    //
+        "rate = " GST_AUDIO_RATE_RANGE ", "     //
+        "channels = (int) 1, "  //
+        "layout = (string) interleaved")
     );
 
 static GstStaticPadTemplate gst_rtp_dtmf_depay_sink_template =
@@ -187,7 +189,7 @@ gst_rtp_dtmf_depay_class_init (GstRtpDTMFDepayClass * klass)
   GST_DEBUG_CATEGORY_INIT (gst_rtp_dtmf_depay_debug,
       "rtpdtmfdepay", 0, "rtpdtmfdepay element");
   gst_element_class_set_static_metadata (gstelement_class,
-      "RTP DTMF packet depayloader", "Codec/Depayloader/Network",
+      "RTP DTMF packet depayloader", "Codec/Depayloader/Network/RTP",
       "Generates DTMF Sound from telephone-event RTP packets",
       "Youness Alaoui <youness.alaoui@collabora.co.uk>");
 
@@ -283,6 +285,8 @@ gst_rtp_dtmf_depay_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
       filtercaps);
   gst_caps_unref (filtercaps);
 
+  srccaps = gst_caps_truncate (srccaps);
+
   gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (filter), srccaps);
   gst_caps_unref (srccaps);
 
diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c
index eceb9b5f4d20133f77ff66769795b1a96b589831..8d3e362f670bce9473d1be4611a046e631cb7e29 100644
--- a/gst/dtmf/gstrtpdtmfsrc.c
+++ b/gst/dtmf/gstrtpdtmfsrc.c
@@ -179,7 +179,7 @@ gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * klass)
       &gst_rtp_dtmf_src_template);
 
   gst_element_class_set_static_metadata (gstelement_class,
-      "RTP DTMF packet generator", "Source/Network",
+      "RTP DTMF packet generator", "Source/Network/RTP",
       "Generates RTP DTMF packets", "Zeeshan Ali <zeeshan.ali@nokia.com>");
 
   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_finalize);
@@ -245,8 +245,8 @@ gst_rtp_dtmf_src_event_free (GstRTPDTMFSrcEvent * event)
 {
   if (event) {
     if (event->payload)
-      g_slice_free (GstRTPDTMFPayload, event->payload);
-    g_slice_free (GstRTPDTMFSrcEvent, event);
+      g_free (event->payload);
+    g_free (event);
   }
 }
 
@@ -352,9 +352,7 @@ gst_rtp_dtmf_src_handle_custom_upstream (GstRTPDTMFSrc * dtmfsrc,
     GstEvent * event)
 {
   gboolean result = FALSE;
-  gchar *struct_str;
   const GstStructure *structure;
-
   GstState state;
   GstStateChangeReturn ret;
 
@@ -364,11 +362,10 @@ gst_rtp_dtmf_src_handle_custom_upstream (GstRTPDTMFSrc * dtmfsrc,
     goto ret;
   }
 
-  GST_DEBUG_OBJECT (dtmfsrc, "Received event is of our interest");
   structure = gst_event_get_structure (event);
-  struct_str = gst_structure_to_string (structure);
-  GST_DEBUG_OBJECT (dtmfsrc, "Event has structure %s", struct_str);
-  g_free (struct_str);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Received event: %" GST_PTR_FORMAT, structure);
+
   if (structure && gst_structure_has_name (structure, "dtmf-event"))
     result = gst_rtp_dtmf_src_handle_dtmf_event (dtmfsrc, structure);
 
@@ -384,7 +381,9 @@ gst_rtp_dtmf_src_handle_event (GstBaseSrc * basesrc, GstEvent * event)
 
   dtmfsrc = GST_RTP_DTMF_SRC (basesrc);
 
-  GST_DEBUG_OBJECT (dtmfsrc, "Received an event on the src pad");
+  GST_DEBUG_OBJECT (dtmfsrc, "Received %s event on the src pad",
+      GST_EVENT_TYPE_NAME (event));
+
   if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) {
     result = gst_rtp_dtmf_src_handle_custom_upstream (dtmfsrc, event);
   }
@@ -508,10 +507,10 @@ gst_rtp_dtmf_src_add_start_event (GstRTPDTMFSrc * dtmfsrc, gint event_number,
     gint event_volume)
 {
 
-  GstRTPDTMFSrcEvent *event = g_slice_new0 (GstRTPDTMFSrcEvent);
+  GstRTPDTMFSrcEvent *event = g_new0 (GstRTPDTMFSrcEvent, 1);
   event->event_type = RTP_DTMF_EVENT_TYPE_START;
 
-  event->payload = g_slice_new0 (GstRTPDTMFPayload);
+  event->payload = g_new0 (GstRTPDTMFPayload, 1);
   event->payload->event = CLAMP (event_number, MIN_EVENT, MAX_EVENT);
   event->payload->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME);
 
@@ -522,7 +521,7 @@ static void
 gst_rtp_dtmf_src_add_stop_event (GstRTPDTMFSrc * dtmfsrc)
 {
 
-  GstRTPDTMFSrcEvent *event = g_slice_new0 (GstRTPDTMFSrcEvent);
+  GstRTPDTMFSrcEvent *event = g_new0 (GstRTPDTMFSrcEvent, 1);
   event->event_type = RTP_DTMF_EVENT_TYPE_STOP;
 
   g_async_queue_push (dtmfsrc->event_queue, event);
@@ -808,7 +807,7 @@ send_last:
   /* This is the end of the event */
   if (dtmfsrc->last_packet == TRUE && dtmfsrc->redundancy_count == 0) {
 
-    g_slice_free (GstRTPDTMFPayload, dtmfsrc->payload);
+    g_free (dtmfsrc->payload);
     dtmfsrc->payload = NULL;
 
     dtmfsrc->last_packet = FALSE;
@@ -1084,6 +1083,9 @@ gst_rtp_dtmf_src_change_state (GstElement * element, GstStateChange transition)
       }
       dtmfsrc->last_event_was_start = FALSE;
 
+      // Clear out any unfinished events
+      g_clear_pointer (&dtmfsrc->payload, g_free);
+
       /* Indicate that we don't do PRE_ROLL */
       break;
 
@@ -1121,7 +1123,7 @@ gst_rtp_dtmf_src_unlock (GstBaseSrc * src)
   GST_OBJECT_UNLOCK (dtmfsrc);
 
   GST_DEBUG_OBJECT (dtmfsrc, "Pushing the PAUSE_TASK event on unlock request");
-  event = g_slice_new0 (GstRTPDTMFSrcEvent);
+  event = g_new0 (GstRTPDTMFSrcEvent, 1);
   event->event_type = RTP_DTMF_EVENT_TYPE_PAUSE_TASK;
   g_async_queue_push (dtmfsrc->event_queue, event);
 
diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c
index 7381ff5c7f8519a046cafffdefce0d16c3951607..184d987e79b0de659c72741015fce80f2174ff85 100644
--- a/gst/flv/gstflvdemux.c
+++ b/gst/flv/gstflvdemux.c
@@ -152,11 +152,11 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
     gboolean key;
 
     gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
-    key = ! !(GST_INDEX_ASSOC_FLAGS (entry) & GST_ASSOCIATION_FLAG_KEY_UNIT);
+    key = !!(GST_INDEX_ASSOC_FLAGS (entry) & GST_ASSOCIATION_FLAG_KEY_UNIT);
     GST_LOG_OBJECT (demux, "position already mapped to time %" GST_TIME_FORMAT
         ", keyframe %d", GST_TIME_ARGS (time), key);
     /* there is not really a way to delete the existing one */
-    if (time != ts || key != ! !keyframe)
+    if (time != ts || key != !!keyframe)
       GST_DEBUG_OBJECT (demux, "metadata mismatch");
 #endif
     gst_object_unref (index);
@@ -1125,6 +1125,48 @@ gst_flv_demux_sync_streams (GstFlvDemux * demux)
   }
 }
 
+/* ensure demux->new_seg_event is set, (re)creating it if required.
+ *
+ * Returns: TRUE if the previous segment has been replaced and should be sent on the
+ * other stream pad as well.
+*/
+static gboolean
+ensure_new_segment (GstFlvDemux * demux, GstPad * pad)
+{
+  gboolean replaced_previous_segment = FALSE;
+
+  if (demux->new_seg_event) {
+    const GstSegment *segment;
+
+    gst_event_parse_segment (demux->new_seg_event, &segment);
+    if (demux->segment.position < segment->start) {
+      GST_DEBUG_OBJECT (pad,
+          "position is out of current segment boundaries, generate a new one");
+      g_clear_pointer (&demux->new_seg_event, gst_event_unref);
+      /* re-sending the segment on the other stream will create a GAP but
+       * this will only happen at the very beginning of the stream so it's
+       * not that bad.
+       */
+      replaced_previous_segment = TRUE;
+    }
+  }
+
+  if (!demux->new_seg_event) {
+    GST_DEBUG_OBJECT (pad, "pushing newsegment from %"
+        GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (demux->segment.position),
+        GST_TIME_ARGS (demux->segment.stop));
+    demux->segment.start = demux->segment.time = demux->segment.position;
+    demux->new_seg_event = gst_event_new_segment (&demux->segment);
+    if (demux->segment_seqnum != GST_SEQNUM_INVALID)
+      gst_event_set_seqnum (demux->new_seg_event, demux->segment_seqnum);
+  } else {
+    GST_DEBUG_OBJECT (pad, "pushing pre-generated newsegment event");
+  }
+
+  return replaced_previous_segment;
+}
+
 static GstFlowReturn
 gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
 {
@@ -1365,19 +1407,20 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Do we need a newsegment event ? */
   if (G_UNLIKELY (demux->audio_need_segment)) {
-    if (!demux->new_seg_event) {
-      GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
+    gboolean replaced_video_segment;
+
+    replaced_video_segment = ensure_new_segment (demux, demux->audio_pad);
+    gst_pad_push_event (demux->audio_pad, gst_event_ref (demux->new_seg_event));
+
+    if (replaced_video_segment) {
+      GST_DEBUG_OBJECT (demux->video_pad, "updating segment from %"
           GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
           GST_TIME_ARGS (demux->segment.position),
           GST_TIME_ARGS (demux->segment.stop));
-      demux->segment.start = demux->segment.time = demux->segment.position;
-      demux->new_seg_event = gst_event_new_segment (&demux->segment);
-      gst_event_set_seqnum (demux->new_seg_event, demux->segment_seqnum);
-    } else {
-      GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
-    }
 
-    gst_pad_push_event (demux->audio_pad, gst_event_ref (demux->new_seg_event));
+      gst_pad_push_event (demux->video_pad,
+          gst_event_ref (demux->new_seg_event));
+    }
 
     demux->audio_need_segment = FALSE;
   }
@@ -1841,20 +1884,20 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Do we need a newsegment event ? */
   if (G_UNLIKELY (demux->video_need_segment)) {
-    if (!demux->new_seg_event) {
-      GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
+    gboolean replaced_audio_segment;
+
+    replaced_audio_segment = ensure_new_segment (demux, demux->video_pad);
+    gst_pad_push_event (demux->video_pad, gst_event_ref (demux->new_seg_event));
+
+    if (replaced_audio_segment) {
+      GST_DEBUG_OBJECT (demux->audio_pad, "updating segment from %"
           GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
           GST_TIME_ARGS (demux->segment.position),
           GST_TIME_ARGS (demux->segment.stop));
-      demux->segment.start = demux->segment.time = demux->segment.position;
-      demux->new_seg_event = gst_event_new_segment (&demux->segment);
-      if (demux->segment_seqnum != GST_SEQNUM_INVALID)
-        gst_event_set_seqnum (demux->new_seg_event, demux->segment_seqnum);
-    } else {
-      GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event");
-    }
 
-    gst_pad_push_event (demux->video_pad, gst_event_ref (demux->new_seg_event));
+      gst_pad_push_event (demux->audio_pad,
+          gst_event_ref (demux->new_seg_event));
+    }
 
     demux->video_need_segment = FALSE;
   }
@@ -1913,7 +1956,7 @@ beach:
 
 static GstClockTime
 gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
-    GstBuffer * buffer, size_t * tag_size)
+    GstBuffer * buffer, size_t *tag_size)
 {
   guint32 dts = 0, dts_ext = 0;
   guint32 tag_data_size;
@@ -3014,7 +3057,7 @@ flv_demux_handle_seek_push (GstFlvDemux * demux, GstEvent * event)
   if (format != GST_FORMAT_TIME)
     goto wrong_format;
 
-  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
+  flush = !!(flags & GST_SEEK_FLAG_FLUSH);
 
   /* Work on a copy until we are sure the seek succeeded. */
   memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
@@ -3185,7 +3228,7 @@ gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, GstEvent * event,
     demux->seeking = seeking;
   GST_OBJECT_UNLOCK (demux);
 
-  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
+  flush = !!(flags & GST_SEEK_FLAG_FLUSH);
 
   if (flush) {
     /* Flush start up and downstream to make sure data flow and loops are
diff --git a/gst/flv/gstflvmux.c b/gst/flv/gstflvmux.c
index 431cc96365e65ed65592cb81bf48944bfbd5b18c..69a34bbc78d1e0098c72b9e2f8c2bbbe2d5de12c 100644
--- a/gst/flv/gstflvmux.c
+++ b/gst/flv/gstflvmux.c
@@ -47,6 +47,8 @@
 #include "gstflvmux.h"
 #include "amfdefs.h"
 
+#include <gst/glib-compat-private.h>
+
 GST_DEBUG_CATEGORY_STATIC (flvmux_debug);
 #define GST_CAT_DEFAULT flvmux_debug
 
@@ -57,12 +59,14 @@ enum
   PROP_METADATACREATOR,
   PROP_ENCODER,
   PROP_SKIP_BACKWARDS_STREAMS,
+  PROP_ENFORCE_INCREASING_TIMESTAMPS,
 };
 
 #define DEFAULT_STREAMABLE FALSE
 #define MAX_INDEX_ENTRIES 128
-#define DEFAULT_METADATACREATOR "GStreamer " PACKAGE_VERSION " FLV muxer"
+#define DEFAULT_METADATACREATOR "GStreamer {VERSION} FLV muxer"
 #define DEFAULT_SKIP_BACKWARDS_STREAMS FALSE
+#define DEFAULT_ENFORCE_INCREASING_TIMESTAMPS TRUE
 
 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -176,12 +180,12 @@ gst_flv_mux_skip_buffer (GstAggregatorPad * apad, GstAggregator * aggregator,
     t = gst_flv_mux_segment_to_running_time (&apad->segment,
         GST_BUFFER_DTS_OR_PTS (buffer));
 
-    if (t < (GST_MSECOND * mux->last_dts)) {
+    if (GST_CLOCK_TIME_IS_VALID (mux->last_dts)
+        && t < (GST_MSECOND * mux->last_dts)) {
       GST_WARNING_OBJECT (fpad,
           "Timestamp %" GST_TIME_FORMAT " going backwards from last used %"
-          GST_TIME_FORMAT ", dropping %" GST_PTR_FORMAT,
-          GST_TIME_ARGS (t), GST_TIME_ARGS (GST_MSECOND * mux->last_dts),
-          buffer);
+          GST_TIME_FORMAT ", dropping %" GST_PTR_FORMAT, GST_TIME_ARGS (t),
+          GST_TIME_ARGS (GST_MSECOND * mux->last_dts), buffer);
       /* Look for non-delta buffer */
       fpad->drop_deltas = TRUE;
       return TRUE;
@@ -219,7 +223,7 @@ typedef struct
 static void
 gst_flv_mux_index_entry_free (GstFlvMuxIndexEntry * entry)
 {
-  g_slice_free (GstFlvMuxIndexEntry, entry);
+  g_free (entry);
 }
 
 static GstBuffer *
@@ -291,6 +295,23 @@ gst_flv_mux_class_init (GstFlvMuxClass * klass)
           DEFAULT_SKIP_BACKWARDS_STREAMS,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+ /**
+   * GstFlvMux:enforce-increasing-timestamps:
+   *
+   * If set to true, flvmux will modify buffers timestamps to ensure they are always
+   * strictly increasing, inside one stream and also between the audio and video streams.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_ENFORCE_INCREASING_TIMESTAMPS,
+      g_param_spec_boolean ("enforce-increasing-timestamps",
+          "Enforce increasing timestamps",
+          "If set to true, flvmux will modify buffers timestamps to ensure they are always "
+          "strictly increasing, inside one stream and also between the audio and video streams",
+          DEFAULT_ENFORCE_INCREASING_TIMESTAMPS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstaggregator_class->create_new_pad =
       GST_DEBUG_FUNCPTR (gst_flv_mux_create_new_pad);
   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad);
@@ -328,8 +349,9 @@ gst_flv_mux_init (GstFlvMux * mux)
   mux->streamable = DEFAULT_STREAMABLE;
   mux->metadatacreator = g_strdup (DEFAULT_METADATACREATOR);
   mux->encoder = g_strdup (DEFAULT_METADATACREATOR);
+  mux->enforce_increasing_timestamps = DEFAULT_ENFORCE_INCREASING_TIMESTAMPS;
 
-  mux->new_tags = FALSE;
+  mux->new_metadata = FALSE;
 
   gst_flv_mux_reset (GST_ELEMENT (mux));
 }
@@ -384,9 +406,9 @@ gst_flv_mux_reset (GstElement * element)
   mux->byte_count = 0;
 
   mux->duration = GST_CLOCK_TIME_NONE;
-  mux->new_tags = FALSE;
+  mux->new_metadata = FALSE;
   mux->first_timestamp = GST_CLOCK_TIME_NONE;
-  mux->last_dts = 0;
+  mux->last_dts = GST_CLOCK_TIME_NONE;
 
   mux->state = GST_FLV_MUX_STATE_HEADER;
   mux->sent_header = FALSE;
@@ -442,7 +464,7 @@ gst_flv_mux_sink_event (GstAggregator * aggregator, GstAggregatorPad * pad,
       gst_event_parse_tag (event, &list);
       gst_tag_setter_merge_tags (setter, list, mode);
       gst_flv_mux_store_codec_tags (mux, flvpad, list);
-      mux->new_tags = TRUE;
+      mux->new_metadata = TRUE;
       ret = TRUE;
       break;
     }
@@ -466,6 +488,8 @@ gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
   guint old_codec;
   GstBuffer *old_codec_data = NULL;
 
+  GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps);
+
   old_codec = pad->codec;
   if (pad->codec_data)
     old_codec_data = gst_buffer_ref (pad->codec_data);
@@ -497,6 +521,7 @@ gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
 
   if (ret && mux->streamable && mux->state != GST_FLV_MUX_STATE_HEADER) {
     if (old_codec != pad->codec) {
+      GST_DEBUG_OBJECT (pad, "pad info changed");
       pad->info_changed = TRUE;
     }
 
@@ -505,16 +530,21 @@ gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
 
       gst_buffer_map (old_codec_data, &map, GST_MAP_READ);
       if (map.size != gst_buffer_get_size (pad->codec_data) ||
-          gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size))
+          gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size)) {
+        GST_DEBUG_OBJECT (pad, "codec data changed");
         pad->info_changed = TRUE;
+      }
 
       gst_buffer_unmap (old_codec_data, &map);
     } else if (!old_codec_data && pad->codec_data) {
+      GST_DEBUG_OBJECT (pad, "codec data changed");
       pad->info_changed = TRUE;
     }
 
-    if (pad->info_changed)
+    if (pad->info_changed) {
       mux->state = GST_FLV_MUX_STATE_HEADER;
+      mux->new_metadata = TRUE;
+    }
   }
 
   if (old_codec_data)
@@ -534,6 +564,8 @@ gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
   guint old_codec, old_rate, old_width, old_channels;
   GstBuffer *old_codec_data = NULL;
 
+  GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps);
+
   old_codec = pad->codec;
   old_rate = pad->rate;
   old_width = pad->width;
@@ -676,6 +708,7 @@ gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
   if (ret && mux->streamable && mux->state != GST_FLV_MUX_STATE_HEADER) {
     if (old_codec != pad->codec || old_rate != pad->rate ||
         old_width != pad->width || old_channels != pad->channels) {
+      GST_DEBUG_OBJECT (pad, "pad info changed");
       pad->info_changed = TRUE;
     }
 
@@ -684,16 +717,21 @@ gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
 
       gst_buffer_map (old_codec_data, &map, GST_MAP_READ);
       if (map.size != gst_buffer_get_size (pad->codec_data) ||
-          gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size))
+          gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size)) {
+        GST_DEBUG_OBJECT (pad, "codec data changed");
         pad->info_changed = TRUE;
+      }
 
       gst_buffer_unmap (old_codec_data, &map);
     } else if (!old_codec_data && pad->codec_data) {
+      GST_DEBUG_OBJECT (pad, "codec data changed");
       pad->info_changed = TRUE;
     }
 
-    if (pad->info_changed)
+    if (pad->info_changed) {
       mux->state = GST_FLV_MUX_STATE_HEADER;
+      mux->new_metadata = TRUE;
+    }
   }
 
   if (old_codec_data)
@@ -801,12 +839,6 @@ gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
 static GstFlowReturn
 gst_flv_mux_push (GstFlvMux * mux, GstBuffer * buffer)
 {
-  GstAggregator *agg = GST_AGGREGATOR (mux);
-  GstAggregatorPad *srcpad = GST_AGGREGATOR_PAD (agg->srcpad);
-
-  if (GST_BUFFER_PTS_IS_VALID (buffer))
-    srcpad->segment.position = GST_BUFFER_PTS (buffer);
-
   /* pushing the buffer that rewrites the header will make it no longer be the
    * total output size in bytes, but it doesn't matter at that point */
   mux->byte_count += gst_buffer_get_size (buffer);
@@ -899,7 +931,9 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
 
   tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
 
-  dts = mux->last_dts;
+  dts =
+      GST_CLOCK_TIME_IS_VALID (mux->last_dts) ? mux->
+      last_dts : mux->first_timestamp / GST_MSECOND;
 
   /* Timestamp must start at zero */
   if (GST_CLOCK_TIME_IS_VALID (mux->first_timestamp)) {
@@ -1115,30 +1149,38 @@ gst_flv_mux_create_metadata (GstFlvMux * mux)
     tags_written++;
   }
 
-  _gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (mux->metadatacreator),
-      &tmp, &data);
+  GString *tag_string = g_string_new (mux->metadatacreator);
+  g_string_replace (tag_string, "{VERSION}", PACKAGE_VERSION, 0);
+
+  _gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + tag_string->len, &tmp, &data);
   data[0] = 0;                  /* 15 bytes name */
   data[1] = 15;
   memcpy (&data[2], "metadatacreator", 15);
   data[17] = 2;                 /* string */
-  data[18] = (strlen (mux->metadatacreator) >> 8) & 0xff;
-  data[19] = (strlen (mux->metadatacreator)) & 0xff;
-  memcpy (&data[20], mux->metadatacreator, strlen (mux->metadatacreator));
+  data[18] = (tag_string->len >> 8) & 0xff;
+  data[19] = tag_string->len & 0xff;
+  memcpy (&data[20], tag_string->str, tag_string->len);
   script_tag = gst_buffer_append (script_tag, tmp);
   tags_written++;
 
-  _gst_buffer_new_and_alloc (2 + 7 + 1 + 2 + strlen (mux->encoder),
-      &tmp, &data);
+  g_string_truncate (tag_string, 0);
+  g_string_append (tag_string, mux->encoder);
+  g_string_replace (tag_string, "{VERSION}", PACKAGE_VERSION, 0);
+
+  _gst_buffer_new_and_alloc (2 + 7 + 1 + 2 + tag_string->len, &tmp, &data);
   data[0] = 0;                  /* 7 bytes name */
   data[1] = 7;
   memcpy (&data[2], "encoder", 7);
   data[9] = 2;                  /* string */
-  data[10] = (strlen (mux->encoder) >> 8) & 0xff;
-  data[11] = (strlen (mux->encoder)) & 0xff;
-  memcpy (&data[12], mux->encoder, strlen (mux->encoder));
+  data[10] = (tag_string->len >> 8) & 0xff;
+  data[11] = tag_string->len & 0xff;
+  memcpy (&data[12], tag_string->str, tag_string->len);
   script_tag = gst_buffer_append (script_tag, tmp);
   tags_written++;
 
+  g_string_free (tag_string, TRUE);
+  tag_string = NULL;
+
   {
     time_t secs;
     struct tm tm;
@@ -1231,21 +1273,29 @@ gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
         " from rounding last pad timestamp %" GST_TIME_FORMAT,
         GST_PAD_NAME (pad), GST_TIME_ARGS (pts * GST_MSECOND),
         GST_TIME_ARGS (pad->last_timestamp));
-  } else {
+  } else if (GST_CLOCK_TIME_IS_VALID (mux->last_dts)) {
     pts = dts = mux->last_dts;
     GST_DEBUG_OBJECT (mux,
         "Pad %s: Created dts and pts %" GST_TIME_FORMAT
         " from last mux timestamp",
         GST_PAD_NAME (pad), GST_TIME_ARGS (pts * GST_MSECOND));
+  } else {
+    pts = dts = mux->first_timestamp / GST_MSECOND;
+    GST_DEBUG_OBJECT (mux,
+        "Pad %s: Created dts and pts %" GST_TIME_FORMAT
+        " from first timestamp",
+        GST_PAD_NAME (pad), GST_TIME_ARGS (pts * GST_MSECOND));
   }
 
   /* We prevent backwards timestamps because they confuse librtmp,
    * it expects timestamps to go forward not only inside one stream, but
    * also between the audio & video streams.
    */
-  if (dts < mux->last_dts) {
-    GST_WARNING_OBJECT (pad, "Got backwards dts! (%" GST_TIME_FORMAT
-        " < %" GST_TIME_FORMAT ")", GST_TIME_ARGS (dts * GST_MSECOND),
+  if (GST_CLOCK_TIME_IS_VALID (mux->last_dts) && dts < mux->last_dts
+      && mux->enforce_increasing_timestamps) {
+    GST_WARNING_OBJECT (pad,
+        "Got backwards dts! (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
+        GST_TIME_ARGS (dts * GST_MSECOND),
         GST_TIME_ARGS (mux->last_dts * GST_MSECOND));
     dts = mux->last_dts;
   }
@@ -1589,7 +1639,7 @@ gst_flv_mux_write_header (GstFlvMux * mux)
     ret = gst_flv_mux_push (mux, metadata);
     if (ret != GST_FLOW_OK)
       goto failure_metadata;
-    mux->new_tags = FALSE;
+    mux->new_metadata = FALSE;
   }
   if (video_codec_data != NULL) {
     ret = gst_flv_mux_push (mux, video_codec_data);
@@ -1624,9 +1674,9 @@ static GstClockTime
 gst_flv_mux_segment_to_running_time (const GstSegment * segment, GstClockTime t)
 {
   /* we can get a dts before the segment, if dts < pts and pts is inside
-   * the segment, so we consider early times as 0 */
+   * the segment, so we consider early times to map to segment start */
   if (t < segment->start)
-    return 0;
+    t = segment->start;
   return gst_segment_to_running_time (segment, GST_FORMAT_TIME, t);
 }
 
@@ -1644,7 +1694,7 @@ gst_flv_mux_update_index (GstFlvMux * mux, GstBuffer * buffer,
     return;
 
   if (GST_BUFFER_PTS_IS_VALID (buffer)) {
-    GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry);
+    GstFlvMuxIndexEntry *entry = g_new (GstFlvMuxIndexEntry, 1);
     GstClockTime pts =
         gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD
         (pad)->segment, GST_BUFFER_PTS (buffer));
@@ -1660,6 +1710,10 @@ gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvMuxPad * pad,
 {
   GstBuffer *tag;
   GstFlowReturn ret;
+  GstClockTime pts =
+      gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD (pad)->segment,
+      GST_BUFFER_PTS (buffer));
+  GstClockTime duration = GST_BUFFER_DURATION (buffer);
   GstClockTime dts =
       gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD (pad)->segment,
       GST_BUFFER_DTS (buffer));
@@ -1678,6 +1732,14 @@ gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvMuxPad * pad,
   if (ret == GST_FLOW_OK && GST_CLOCK_TIME_IS_VALID (dts))
     pad->last_timestamp = dts;
 
+  if (ret == GST_FLOW_OK && GST_CLOCK_TIME_IS_VALID (pts)) {
+    GstAggregator *agg = GST_AGGREGATOR (mux);
+    GstAggregatorPad *srcpad = GST_AGGREGATOR_PAD (agg->srcpad);
+    srcpad->segment.position = pts;
+    if (GST_CLOCK_TIME_IS_VALID (duration))
+      srcpad->segment.position += duration;
+  }
+
   return ret;
 }
 
@@ -1939,12 +2001,16 @@ gst_flv_mux_find_best_pad (GstAggregator * aggregator, GstClockTime * ts,
           break;
         }
 
-        if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS_OR_PTS (buffer))) {
-          t = gst_flv_mux_segment_to_running_time (&apad->segment,
-              GST_BUFFER_DTS_OR_PTS (buffer));
-        }
+        t = gst_flv_mux_segment_to_running_time (&apad->segment,
+            GST_BUFFER_DTS_OR_PTS (buffer));
 
-        if (!GST_CLOCK_TIME_IS_VALID (best_ts) ||
+        if (!GST_CLOCK_TIME_IS_VALID (t)) {
+          GST_WARNING_OBJECT (apad, "Buffer has no timestamp: %" GST_PTR_FORMAT,
+              buffer);
+          gst_object_replace ((GstObject **) & best, GST_OBJECT (apad));
+          best_ts = GST_CLOCK_TIME_NONE;
+          done = TRUE;
+        } else if (!GST_CLOCK_TIME_IS_VALID (best_ts) ||
             (GST_CLOCK_TIME_IS_VALID (t) && t < best_ts)) {
           gst_object_replace ((GstObject **) & best, GST_OBJECT (apad));
           best_ts = t;
@@ -2008,19 +2074,19 @@ gst_flv_mux_aggregate (GstAggregator * aggregator, gboolean timeout)
       goto out;
     }
 
-    ret = gst_flv_mux_write_header (mux);
-    if (ret != GST_FLOW_OK) {
-      goto out;
-    }
-
-    mux->state = GST_FLV_MUX_STATE_DATA;
-
     if (!mux->streamable || mux->first_timestamp == GST_CLOCK_TIME_NONE) {
       if (best && GST_CLOCK_TIME_IS_VALID (ts))
         mux->first_timestamp = ts;
       else
         mux->first_timestamp = 0;
     }
+
+    ret = gst_flv_mux_write_header (mux);
+    if (ret != GST_FLOW_OK) {
+      goto out;
+    }
+
+    mux->state = GST_FLV_MUX_STATE_DATA;
   } else {
     best = gst_flv_mux_find_best_pad (aggregator, &ts, timeout);
   }
@@ -2034,11 +2100,11 @@ gst_flv_mux_aggregate (GstAggregator * aggregator, gboolean timeout)
     }
   }
 
-  if (mux->new_tags && mux->streamable) {
+  if (mux->new_metadata && mux->streamable) {
     GstBuffer *buf = gst_flv_mux_create_metadata (mux);
     if (buf)
       gst_flv_mux_push (mux, buf);
-    mux->new_tags = FALSE;
+    mux->new_metadata = FALSE;
   }
 
   if (best) {
@@ -2109,6 +2175,9 @@ gst_flv_mux_get_property (GObject * object,
     case PROP_SKIP_BACKWARDS_STREAMS:
       g_value_set_boolean (value, mux->skip_backwards_streams);
       break;
+    case PROP_ENFORCE_INCREASING_TIMESTAMPS:
+      g_value_set_boolean (value, mux->enforce_increasing_timestamps);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2152,6 +2221,9 @@ gst_flv_mux_set_property (GObject * object,
     case PROP_SKIP_BACKWARDS_STREAMS:
       mux->skip_backwards_streams = g_value_get_boolean (value);
       break;
+    case PROP_ENFORCE_INCREASING_TIMESTAMPS:
+      mux->enforce_increasing_timestamps = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2162,21 +2234,20 @@ static GstClockTime
 gst_flv_mux_get_next_time (GstAggregator * aggregator)
 {
   GstFlvMux *mux = GST_FLV_MUX (aggregator);
-  GstAggregatorPad *agg_audio_pad = GST_AGGREGATOR_PAD_CAST (mux->audio_pad);
-  GstAggregatorPad *agg_video_pad = GST_AGGREGATOR_PAD_CAST (mux->video_pad);
+  GstFlvMuxPad *best = NULL;
+  GstClockTime best_time = GST_CLOCK_TIME_NONE;
 
   GST_OBJECT_LOCK (aggregator);
   if (mux->state == GST_FLV_MUX_STATE_HEADER &&
       ((mux->audio_pad && mux->audio_pad->codec == G_MAXUINT) ||
           (mux->video_pad && mux->video_pad->codec == G_MAXUINT)))
     goto wait_for_data;
-
-  if (!((agg_audio_pad && gst_aggregator_pad_has_buffer (agg_audio_pad)) ||
-          (agg_video_pad && gst_aggregator_pad_has_buffer (agg_video_pad))))
-    goto wait_for_data;
   GST_OBJECT_UNLOCK (aggregator);
 
-  return gst_aggregator_simple_get_next_time (aggregator);
+  best = gst_flv_mux_find_best_pad (aggregator, &best_time, TRUE);
+  gst_clear_object (&best);
+
+  return best_time;
 
 wait_for_data:
   GST_OBJECT_UNLOCK (aggregator);
diff --git a/gst/flv/gstflvmux.h b/gst/flv/gstflvmux.h
index 40d87562c242c76a7f70e0294b227fcdbc83e4e8..a5c10a5c62b7847ff216e96695c73f3b2fc391be 100644
--- a/gst/flv/gstflvmux.h
+++ b/gst/flv/gstflvmux.h
@@ -96,9 +96,10 @@ struct _GstFlvMux {
   gchar *metadatacreator;
   gchar *encoder;
   gboolean skip_backwards_streams;
+  gboolean enforce_increasing_timestamps;
 
   GstTagList *tags;
-  gboolean new_tags;
+  gboolean new_metadata;
   GList *index;
   guint64 byte_count;
   GstClockTime duration;
diff --git a/gst/flv/gstindex.c b/gst/flv/gstindex.c
index 4b11bb219838f536236d829c863248acf1de5593..9b2bedc2c78f1f78c878e11850b102206e906e91 100644
--- a/gst/flv/gstindex.c
+++ b/gst/flv/gstindex.c
@@ -283,7 +283,7 @@ gst_index_get_property (GObject * object, guint prop_id,
 static GstIndexGroup *
 gst_index_group_new (guint groupnum)
 {
-  GstIndexGroup *indexgroup = g_slice_new (GstIndexGroup);
+  GstIndexGroup *indexgroup = g_new (GstIndexGroup, 1);
 
   indexgroup->groupnum = groupnum;
   indexgroup->entries = NULL;
@@ -298,7 +298,7 @@ gst_index_group_new (guint groupnum)
 static void
 gst_index_group_free (GstIndexGroup * group)
 {
-  g_slice_free (GstIndexGroup, group);
+  g_free (group);
 }
 
 /* do not resurrect this, add a derived dummy index class instead */
@@ -542,7 +542,7 @@ gst_index_set_resolver_full (GstIndex * index, GstIndexResolver resolver,
 GstIndexEntry *
 gst_index_entry_copy (GstIndexEntry * entry)
 {
-  GstIndexEntry *new_entry = g_slice_new (GstIndexEntry);
+  GstIndexEntry *new_entry = g_new (GstIndexEntry, 1);
 
   memcpy (new_entry, entry, sizeof (GstIndexEntry));
   return new_entry;
@@ -576,7 +576,7 @@ gst_index_entry_free (GstIndexEntry * entry)
       break;
   }
 
-  g_slice_free (GstIndexEntry, entry);
+  g_free (entry);
 }
 
 #if 0
@@ -606,7 +606,7 @@ gst_index_add_format (GstIndex * index, gint id, GstFormat format)
   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
     return NULL;
 
-  entry = g_slice_new (GstIndexEntry);
+  entry = g_new (GstIndexEntry, 1);
   entry->type = GST_INDEX_ENTRY_FORMAT;
   entry->id = id;
   entry->data.format.format = format;
@@ -641,7 +641,7 @@ gst_index_add_id (GstIndex * index, gint id, gchar * description)
   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
     return NULL;
 
-  entry = g_slice_new (GstIndexEntry);
+  entry = g_new (GstIndexEntry, 1);
   entry->type = GST_INDEX_ENTRY_ID;
   entry->id = id;
   entry->data.id.description = description;
@@ -756,7 +756,7 @@ gst_index_get_writer_id (GstIndex * index, GstObject * writer, gint * id)
     if (!entry) {
       /* index is probably not writable, make an entry anyway
        * to keep it in our cache */
-      entry = g_slice_new (GstIndexEntry);
+      entry = g_new (GstIndexEntry, 1);
       entry->type = GST_INDEX_ENTRY_ID;
       entry->id = *id;
       entry->data.id.description = writer_string;
@@ -808,7 +808,7 @@ gst_index_add_associationv (GstIndex * index, gint id,
   if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
     return NULL;
 
-  entry = g_slice_new (GstIndexEntry);
+  entry = g_new (GstIndexEntry, 1);
 
   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
   entry->id = id;
diff --git a/gst/flv/gstmemindex.c b/gst/flv/gstmemindex.c
index eef99c2c584cc563bcac274cf4738d32971711b9..216bca5f4f974218059b1f30c441c1714bdcaa8d 100644
--- a/gst/flv/gstmemindex.c
+++ b/gst/flv/gstmemindex.c
@@ -142,7 +142,7 @@ gst_mem_index_free_format (gpointer key, gpointer value, gpointer user_data)
     g_tree_destroy (index->tree);
   }
 
-  g_slice_free (GstMemIndexFormatIndex, index);
+  g_free (index);
 }
 
 static void
@@ -157,7 +157,7 @@ gst_mem_index_free_id (gpointer key, gpointer value, gpointer user_data)
     id_index->format_index = NULL;
   }
 
-  g_slice_free (GstMemIndexId, id_index);
+  g_free (id_index);
 }
 
 static void
@@ -191,7 +191,7 @@ gst_mem_index_add_id (GstIndex * index, GstIndexEntry * entry)
   id_index = g_hash_table_lookup (memindex->id_index, &entry->id);
 
   if (!id_index) {
-    id_index = g_slice_new0 (GstMemIndexId);
+    id_index = g_new0 (GstMemIndexId, 1);
 
     id_index->id = entry->id;
     id_index->format_index = g_hash_table_new (g_int_hash, g_int_equal);
@@ -226,7 +226,7 @@ gst_mem_index_index_format (GstMemIndexId * id_index, GstIndexEntry * entry,
   index = g_hash_table_lookup (id_index->format_index, format);
 
   if (!index) {
-    index = g_slice_new0 (GstMemIndexFormatIndex);
+    index = g_new0 (GstMemIndexFormatIndex, 1);
 
     index->format = *format;
     index->offset = assoc;
diff --git a/gst/goom/mmx.c b/gst/goom/mmx.c
index bc2a6c46bdedfb354939198682342ca21201ecd2..14256a91b9f84077d8a4e35daaba62ef7a0e8a10 100644
--- a/gst/goom/mmx.c
+++ b/gst/goom/mmx.c
@@ -126,7 +126,6 @@ zoom_filter_mmx (int prevX, int prevY,
         "psrlw $8, %%mm0 \n\t"
         "packuswb %%mm7, %%mm0 \n\t" "movd %%mm0,%0 \n\t":"=g" (expix2[loop])
         :"r" (pos), "r" (coeffs), "r" (expix1)
-
         );
 
     emms ();
diff --git a/gst/imagefreeze/gstimagefreeze.c b/gst/imagefreeze/gstimagefreeze.c
index 6a2431ab0c396a78336888c3f01067210150cf84..2a897c1e639a30cd64202e7bb4f999dc6ca39c01 100644
--- a/gst/imagefreeze/gstimagefreeze.c
+++ b/gst/imagefreeze/gstimagefreeze.c
@@ -89,11 +89,11 @@ static gboolean gst_image_freeze_src_query (GstPad * pad, GstObject * parent,
 static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("video/x-raw(ANY)"));
+    GST_STATIC_CAPS ("video/x-raw(ANY); video/x-bayer(ANY)"));
 
 static GstStaticPadTemplate src_pad_template =
-GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("video/x-raw(ANY)"));
+    GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw(ANY); video/x-bayer(ANY)"));
 
 GST_DEBUG_CATEGORY_STATIC (gst_image_freeze_debug);
 #define GST_CAT_DEFAULT gst_image_freeze_debug
@@ -696,7 +696,7 @@ gst_image_freeze_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
           &stop_type, &stop);
       gst_event_unref (event);
 
-      flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
+      flush = !!(flags & GST_SEEK_FLAG_FLUSH);
 
       if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
         GST_ERROR_OBJECT (pad, "Seek in invalid format: %s",
diff --git a/gst/isomp4/atoms.c b/gst/isomp4/atoms.c
index cb9de639ea672cab4ed0717cb4ac19bb0556c70c..4332f861c57d0ae83bff5dc96b1748592da2a1c6 100644
--- a/gst/isomp4/atoms.c
+++ b/gst/isomp4/atoms.c
@@ -5706,9 +5706,9 @@ build_opus_extension (guint32 rate, guint8 channels, guint8 mapping_family,
   gst_byte_writer_init (&bw);
   hdl &= gst_byte_writer_put_uint8 (&bw, 0x00); /* version number */
   hdl &= gst_byte_writer_put_uint8 (&bw, channels);
-  hdl &= gst_byte_writer_put_uint16_le (&bw, pre_skip);
-  hdl &= gst_byte_writer_put_uint32_le (&bw, rate);
-  hdl &= gst_byte_writer_put_uint16_le (&bw, output_gain);
+  hdl &= gst_byte_writer_put_uint16_be (&bw, pre_skip);
+  hdl &= gst_byte_writer_put_uint32_be (&bw, rate);
+  hdl &= gst_byte_writer_put_uint16_be (&bw, output_gain);
   hdl &= gst_byte_writer_put_uint8 (&bw, mapping_family);
   if (mapping_family > 0) {
     hdl &= gst_byte_writer_put_uint8 (&bw, stream_count);
diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h
index 0831b4cf2aacace65002be2785300933fc80081c..0865a18c9d022f7c1230531e0e829d49a64bdf68 100644
--- a/gst/isomp4/fourcc.h
+++ b/gst/isomp4/fourcc.h
@@ -304,6 +304,16 @@ G_BEGIN_DECLS
 #define FOURCC_av1m     GST_MAKE_FOURCC('a','v','1','m')
 #define FOURCC_av1s     GST_MAKE_FOURCC('a','v','1','s')
 #define FOURCC_av1M     GST_MAKE_FOURCC('a','v','1','M')
+#define FOURCC_SHQ0     GST_MAKE_FOURCC('S','H','Q','0')
+#define FOURCC_SHQ1     GST_MAKE_FOURCC('S','H','Q','1')
+#define FOURCC_SHQ2     GST_MAKE_FOURCC('S','H','Q','2')
+#define FOURCC_SHQ3     GST_MAKE_FOURCC('S','H','Q','3')
+#define FOURCC_SHQ4     GST_MAKE_FOURCC('S','H','Q','4')
+#define FOURCC_SHQ5     GST_MAKE_FOURCC('S','H','Q','5')
+#define FOURCC_SHQ6     GST_MAKE_FOURCC('S','H','Q','6')
+#define FOURCC_SHQ7     GST_MAKE_FOURCC('S','H','Q','7')
+#define FOURCC_SHQ8     GST_MAKE_FOURCC('S','H','Q','8')
+#define FOURCC_SHQ9     GST_MAKE_FOURCC('S','H','Q','9')
 
 #define FOURCC_cfhd     GST_MAKE_FOURCC('C','F','H','D')
 #define FOURCC_ap4x     GST_MAKE_FOURCC('a','p','4','x')
@@ -425,6 +435,9 @@ G_BEGIN_DECLS
 #define FOURCC_tenc     GST_MAKE_FOURCC('t','e','n','c')
 #define FOURCC_cenc     GST_MAKE_FOURCC('c','e','n','c')
 #define FOURCC_cbcs     GST_MAKE_FOURCC('c','b','c','s')
+#define FOURCC_sbgp     GST_MAKE_FOURCC('s','b','g','p')
+#define FOURCC_sgpd     GST_MAKE_FOURCC('s','g','p','d')
+#define FOURCC_seig     GST_MAKE_FOURCC('s','e','i','g')
 
 /* Audible AAX encrypted audio */
 #define FOURCC_aavd     GST_MAKE_FOURCC('a','a','v','d')
diff --git a/gst/isomp4/gstqtmux-doc.c b/gst/isomp4/gstqtmux-doc.c
index bc9b3e81e9ac77aa2845ed7ce6363d54b4467bab..68f08fddc6b0e5a13e69e0fd4b5c7573821d5afa 100644
--- a/gst/isomp4/gstqtmux-doc.c
+++ b/gst/isomp4/gstqtmux-doc.c
@@ -274,7 +274,7 @@
  * @title: ismlmux
  * @short_description: Muxer for ISML smooth streaming (.isml) files
  *
- * This element merges streams (audio and video) into MJ2 (.mj2) files.
+ * This element merges streams (audio and video) into ISML (.isml) files.
  *
  * The following background intends to explain why various similar muxers
  * are present in this plugin.
@@ -309,10 +309,10 @@
  *
  * ## Example pipelines
  * |[
- * gst-launch-1.0 v4l2src num-buffers=50 ! queue ! jp2kenc ! mj2mux ! filesink location=video.mj2
+ * gst-launch-1.0 videotestsrc num-buffers=50 ! queue ! x264enc ! ismlmux fragment-duration=10 ! filesink location=video.isml
  * ]|
- * Records a video stream captured from a v4l2 device, encodes it into JPEG-2000
- * and muxes it into an mj2 file.
+ * Records a video stream captured from a v4l2 device, encodes it into H.264
+ * and muxes it into an isml file.
  *
  * Documentation last reviewed on 2011-04-21
  */
diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c
index 5f6cf80b5e3c6647ccd8b49af65df41f5bb7af03..29250c70f29b9ae94b0efe8fb113c127598c22c4 100644
--- a/gst/isomp4/gstqtmux.c
+++ b/gst/isomp4/gstqtmux.c
@@ -926,16 +926,16 @@ extract_608_field_from_s334_1a (const guint8 * ccdata, gsize ccdata_size,
   /* Iterate over the ccdata and put the corresponding tuples for the given field
    * in the storage */
   for (i = 0; i < ccdata_size; i += 3) {
-    if ((field == 1 && (ccdata[i * 3] & 0x80)) ||
-        (field == 2 && !(ccdata[i * 3] & 0x80))) {
+    if ((field == 1 && (ccdata[i] & 0x80)) ||
+        (field == 2 && !(ccdata[i] & 0x80))) {
       GST_DEBUG ("Storing matching cc for field %d : 0x%02x 0x%02x", field,
-          ccdata[i * 3 + 1], ccdata[i * 3 + 2]);
+          ccdata[i + 1], ccdata[i + 2]);
       if (res_size >= storage_size) {
         storage_size += 128;
         storage = g_realloc (storage, storage_size);
       }
-      storage[res_size] = ccdata[i * 3 + 1];
-      storage[res_size + 1] = ccdata[i * 3 + 2];
+      storage[res_size] = ccdata[i + 1];
+      storage[res_size + 1] = ccdata[i + 2];
       res_size += 2;
     }
   }
@@ -2200,6 +2200,9 @@ gst_qt_mux_set_header_on_caps (GstQTMux * mux, GstBuffer * buf)
   GstCaps *caps, *tcaps;
 
   tcaps = gst_pad_get_current_caps (GST_AGGREGATOR_SRC_PAD (mux));
+  if (G_UNLIKELY (tcaps == NULL))
+    return;
+
   caps = gst_caps_copy (tcaps);
   gst_caps_unref (tcaps);
 
@@ -5793,15 +5796,17 @@ static gboolean
 gst_qt_mux_can_renegotiate (GstQTMux * qtmux, GstPad * pad, GstCaps * caps)
 {
   GstQTMuxPad *qtmuxpad = GST_QT_MUX_PAD_CAST (pad);
+  gboolean ret = TRUE;
 
   /* does not go well to renegotiate stream mid-way, unless
    * the old caps are a subset of the new one (this means upstream
    * added more info to the caps, as both should be 'fixed' caps) */
 
+  GST_OBJECT_LOCK (qtmux);
   if (!qtmuxpad->configured_caps) {
     GST_DEBUG_OBJECT (qtmux, "pad %s accepted caps %" GST_PTR_FORMAT,
         GST_PAD_NAME (pad), caps);
-    return TRUE;
+    goto out;
   }
 
   g_assert (caps != NULL);
@@ -5810,14 +5815,18 @@ gst_qt_mux_can_renegotiate (GstQTMux * qtmux, GstPad * pad, GstCaps * caps)
     GST_WARNING_OBJECT (qtmux,
         "pad %s refused renegotiation to %" GST_PTR_FORMAT " from %"
         GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, qtmuxpad->configured_caps);
-    return FALSE;
+    ret = FALSE;
+    goto out;
   }
 
   GST_DEBUG_OBJECT (qtmux,
       "pad %s accepted renegotiation to %" GST_PTR_FORMAT " from %"
       GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, qtmuxpad->configured_caps);
 
-  return TRUE;
+out:
+  GST_OBJECT_UNLOCK (qtmux);
+
+  return ret;
 }
 
 static gboolean
@@ -6081,9 +6090,9 @@ gst_qt_mux_audio_sink_set_caps (GstQTMuxPad * qtpad, GstCaps * caps)
   } else if (strcmp (mimetype, "audio/x-ac3") == 0) {
     entry.fourcc = FOURCC_ac_3;
 
-    /* Fixed values according to TS 102 366 but it also mentions that
-     * they should be ignored */
-    entry.channels = 2;
+    /* TS 102 366 mentions that these fields should be ignored,
+     * but be friendly and fill in the channel count like FFmpeg does */
+    entry.channels = channels;
     entry.sample_size = 16;
 
     /* AC-3 needs an extension atom but its data can only be obtained from
@@ -6592,24 +6601,87 @@ gst_qt_mux_video_sink_set_caps (GstQTMuxPad * qtpad, GstCaps * caps)
     entry.fourcc = FOURCC_cfhd;
     sync = FALSE;
   } else if (strcmp (mimetype, "video/x-av1") == 0) {
-    gint presentation_delay;
-    guint8 presentation_delay_byte = 0;
-    GstBuffer *av1_codec_data;
+    gint presentation_delay = -1;
+    GstBuffer *av1_codec_data = NULL;
 
-    if (gst_structure_get_int (structure, "presentation-delay",
-            &presentation_delay)) {
-      presentation_delay_byte = 1 << 5;
-      presentation_delay_byte |= MAX (0xF, presentation_delay & 0xF);
-    }
+    if (codec_data) {
+      av1_codec_data = gst_buffer_ref ((GstBuffer *) codec_data);
+    } else {
+      GstMapInfo map;
+      const gchar *tmp;
+      guint tmp2;
+
+      gst_structure_get_int (structure, "presentation-delay",
+          &presentation_delay);
+
+      av1_codec_data = gst_buffer_new_allocate (NULL, 4, NULL);
+      gst_buffer_map (av1_codec_data, &map, GST_MAP_WRITE);
+
+      /*
+       *  unsigned int (1) marker = 1;
+       *  unsigned int (7) version = 1;
+       *  unsigned int (3) seq_profile;
+       *  unsigned int (5) seq_level_idx_0;
+       *  unsigned int (1) seq_tier_0;
+       *  unsigned int (1) high_bitdepth;
+       *  unsigned int (1) twelve_bit;
+       *  unsigned int (1) monochrome;
+       *  unsigned int (1) chroma_subsampling_x;
+       *  unsigned int (1) chroma_subsampling_y;
+       *  unsigned int (2) chroma_sample_position;
+       *  unsigned int (3) reserved = 0;
+       *
+       *  unsigned int (1) initial_presentation_delay_present;
+       *  if (initial_presentation_delay_present) {
+       *    unsigned int (4) initial_presentation_delay_minus_one;
+       *  } else {
+       *    unsigned int (4) reserved = 0;
+       *  }
+       */
+
+      map.data[0] = 0x81;
+      map.data[1] = 0x00;
+      if ((tmp = gst_structure_get_string (structure, "profile"))) {
+        if (strcmp (tmp, "main") == 0)
+          map.data[1] |= (0 << 5);
+        if (strcmp (tmp, "high") == 0)
+          map.data[1] |= (1 << 5);
+        if (strcmp (tmp, "professional") == 0)
+          map.data[1] |= (2 << 5);
+      }
+      /* FIXME: level set to 1 */
+      map.data[1] |= 0x01;
+      /* FIXME: tier set to 0 */
+
+      if (gst_structure_get_uint (structure, "bit-depth-luma", &tmp2)) {
+        if (tmp2 == 10) {
+          map.data[2] |= 0x40;
+        } else if (tmp2 == 12) {
+          map.data[2] |= 0x60;
+        }
+      }
+
+      /* Assume 4:2:0 if nothing else is given */
+      map.data[2] |= 0x0C;
+      if ((tmp = gst_structure_get_string (structure, "chroma-format"))) {
+        if (strcmp (tmp, "4:0:0") == 0)
+          map.data[2] |= 0x1C;
+        if (strcmp (tmp, "4:2:0") == 0)
+          map.data[2] |= 0x0C;
+        if (strcmp (tmp, "4:2:2") == 0)
+          map.data[2] |= 0x08;
+        if (strcmp (tmp, "4:4:4") == 0)
+          map.data[2] |= 0x00;
+      }
+
+      /* FIXME: keep chroma-site unknown */
 
+      if (presentation_delay != -1) {
+        map.data[3] = 0x10 | (MAX (0xF, presentation_delay) & 0xF);
+      }
 
-    av1_codec_data = gst_buffer_new_allocate (NULL, 5, NULL);
-    /* Fill version and 3 bytes of flags to 0 */
-    gst_buffer_memset (av1_codec_data, 0, 0, 4);
-    gst_buffer_fill (av1_codec_data, 4, &presentation_delay_byte, 1);
-    if (codec_data)
-      av1_codec_data = gst_buffer_append (av1_codec_data,
-          gst_buffer_ref ((GstBuffer *) codec_data));
+      gst_buffer_unmap (av1_codec_data, &map);
+    }
 
     entry.fourcc = FOURCC_av01;
 
@@ -6975,8 +7047,10 @@ gst_qt_mux_sink_event (GstAggregator * agg, GstAggregatorPad * agg_pad,
         GST_OBJECT_UNLOCK (qtmux);
       }
 
+      GST_OBJECT_LOCK (qtmux);
       if (ret)
         gst_caps_replace (&qtmux_pad->configured_caps, caps);
+      GST_OBJECT_UNLOCK (qtmux);
 
       gst_event_unref (event);
       event = NULL;
diff --git a/gst/isomp4/gstrtpxqtdepay.c b/gst/isomp4/gstrtpxqtdepay.c
index 3900db28f575f5f603c4b2536766f2d38b5fad79..d6ea1283efdaa985f43aa0da4d2ada455b03fe5e 100644
--- a/gst/isomp4/gstrtpxqtdepay.c
+++ b/gst/isomp4/gstrtpxqtdepay.c
@@ -146,6 +146,9 @@ gst_rtp_xqt_depay_class_init (GstRtpXQTDepayClass * klass)
 static void
 gst_rtp_xqt_depay_init (GstRtpXQTDepay * rtpxqtdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpxqtdepay), TRUE);
+
   rtpxqtdepay->adapter = gst_adapter_new ();
 }
 
@@ -551,6 +554,7 @@ gst_rtp_xqt_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
       case 2:
       {
         guint slen;
+        GstBufferList *outbufs = NULL;
 
         /* multiple samples per packet. 
          *                      1                   2                   3
@@ -571,6 +575,7 @@ gst_rtp_xqt_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
          * . ......                                                        .
          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          */
+        outbufs = gst_buffer_list_new ();
         while (payload_len > 8) {
           s = (payload[0] & 0x80) != 0; /* contains sync sample */
           slen = (payload[2] << 8) | payload[3];
@@ -590,7 +595,7 @@ gst_rtp_xqt_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
           if (!s)
             GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
 
-          gst_rtp_base_depayload_push (depayload, outbuf);
+          gst_buffer_list_add (outbufs, outbuf);
 
           /* aligned on 32 bit boundary */
           slen = GST_ROUND_UP_4 (slen);
@@ -598,6 +603,8 @@ gst_rtp_xqt_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
           payload += slen;
           payload_len -= slen;
         }
+        gst_rtp_base_depayload_push_list (depayload, outbufs);
+        outbuf = NULL;
         break;
       }
       case 3:
@@ -630,30 +637,35 @@ done:
 need_resync:
   {
     GST_DEBUG_OBJECT (rtpxqtdepay, "waiting for marker");
+    gst_rtp_base_depayload_dropped (depayload);
     goto done;
   }
 wrong_version:
   {
     GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
         ("Unknown payload version."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     goto done;
   }
 pck_reserved:
   {
     GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
         ("PCK reserved 0."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     goto done;
   }
 wrong_length:
   {
     GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
         ("Wrong payload length."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     goto done;
   }
 unknown_format:
   {
     GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
         ("Unknown payload format."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     goto done;
   }
 }
diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c
index 23eafe0de61a6a5bde953a0b6ff156070ef77454..6e6322767fda526c1845bd7c7b454a65698e5eab 100644
--- a/gst/isomp4/qtdemux.c
+++ b/gst/isomp4/qtdemux.c
@@ -233,6 +233,11 @@ struct _QtDemuxCencSampleSetInfo
 {
   GstStructure *default_properties;
 
+  /* sample groups */
+  GPtrArray *track_group_properties;
+  GPtrArray *fragment_group_properties;
+  GPtrArray *sample_to_group_map;
+
   /* @crypto_info holds one GstStructure per sample */
   GPtrArray *crypto_info;
 };
@@ -381,6 +386,8 @@ static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
 static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
     QtDemuxStream * stream, gint segment_index, GstClockTime pos);
 
+static void qtdemux_check_if_is_gapless_audio (GstQTDemux * qtdemux);
+
 static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
 static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
 
@@ -469,6 +476,11 @@ gst_qtdemux_finalize (GObject * object)
   GstQTDemux *qtdemux = GST_QTDEMUX (object);
 
   g_free (qtdemux->redirect_location);
+  g_free (qtdemux->cenc_aux_info_sizes);
+  g_mutex_clear (&qtdemux->expose_lock);
+
+  g_ptr_array_free (qtdemux->active_streams, TRUE);
+  g_ptr_array_free (qtdemux->old_streams, TRUE);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -478,22 +490,13 @@ gst_qtdemux_dispose (GObject * object)
 {
   GstQTDemux *qtdemux = GST_QTDEMUX (object);
 
-  if (qtdemux->adapter) {
-    g_object_unref (G_OBJECT (qtdemux->adapter));
-    qtdemux->adapter = NULL;
-  }
-  gst_tag_list_unref (qtdemux->tag_list);
-  gst_flow_combiner_free (qtdemux->flowcombiner);
+  g_clear_object (&qtdemux->adapter);
+  gst_clear_tag_list (&qtdemux->tag_list);
+  g_clear_pointer (&qtdemux->flowcombiner, gst_flow_combiner_unref);
+
   g_queue_clear_full (&qtdemux->protection_event_queue,
       (GDestroyNotify) gst_event_unref);
 
-  g_free (qtdemux->cenc_aux_info_sizes);
-  qtdemux->cenc_aux_info_sizes = NULL;
-  g_mutex_clear (&qtdemux->expose_lock);
-
-  g_ptr_array_free (qtdemux->active_streams, TRUE);
-  g_ptr_array_free (qtdemux->old_streams, TRUE);
-
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -658,7 +661,12 @@ gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
 
   if (qtdemux->duration != 0 &&
       qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
-    *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
+    /* If this is single-stream audio media with gapless data,
+     * report the duration of the valid subset of the overall data. */
+    if (qtdemux->gapless_audio_info.type != GAPLESS_AUDIO_INFO_TYPE_NONE)
+      *duration = qtdemux->gapless_audio_info.valid_duration;
+    else
+      *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
     res = TRUE;
   } else {
     *duration = GST_CLOCK_TIME_NONE;
@@ -1125,7 +1133,7 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
   gint64 min_byte_offset = -1;
   guint i;
 
-  min_offset = desired_time;
+  min_offset = next ? G_MAXUINT64 : desired_time;
 
   /* for each stream, find the index of the sample in the segment
    * and move back to the previous keyframe. */
@@ -1144,6 +1152,10 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
     if (CUR_STREAM (str)->sparse && !use_sparse)
       continue;
 
+    /* raw audio streams can be ignored as we can seek anywhere within them */
+    if (str->subtype == FOURCC_soun && str->need_clip)
+      continue;
+
     seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time);
     GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);
 
@@ -1184,10 +1196,10 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
       index++;
 
     if (!empty_segment) {
-      /* find previous keyframe */
+      /* find previous or next keyframe */
       kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, next);
 
-      /* we will settle for one before if none found after */
+      /* if looking for next one, we will settle for one before if none found after */
       if (next && kindex == -1)
         kindex = gst_qtdemux_find_keyframe (qtdemux, str, index, FALSE);
 
@@ -1213,8 +1225,12 @@ gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
           /* this keyframe is inside the segment, convert back to
            * segment time */
           seg_time = (media_time - seg->media_start) + seg->time;
-          if ((!next && (seg_time < min_offset)) ||
-              (next && (seg_time > min_offset)))
+
+          /* Adjust the offset based on the earliest suitable keyframe found,
+           * based on which GST_SEEK_FLAG_SNAP_* is present (indicated by 'next').
+           * For SNAP_BEFORE we look for the earliest keyframe before desired_time,
+           * and in case of SNAP_AFTER - for the closest one after it. */
+          if (seg_time < min_offset)
             min_offset = seg_time;
         }
       }
@@ -1409,8 +1425,8 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
     gint64 min_offset;
     gboolean next, before, after;
 
-    before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
-    after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
+    before = !!(flags & GST_SEEK_FLAG_SNAP_BEFORE);
+    after = !!(flags & GST_SEEK_FLAG_SNAP_AFTER);
     next = after && !before;
     if (segment->rate < 0)
       next = !next;
@@ -1490,8 +1506,8 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
 
   GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format));
 
-  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
-  instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
+  flush = !!(flags & GST_SEEK_FLAG_FLUSH);
+  instant_rate_change = !!(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
 
   /* Directly send the instant-rate-change event here before taking the
    * stream-lock so that it can be applied as soon as possible */
@@ -1638,7 +1654,7 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
 
       gst_event_parse_seek (event, NULL, &seek_format, &flags, NULL, NULL, NULL,
           NULL);
-      instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
+      instant_rate_change = !!(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
 
       if (seqnum == qtdemux->segment_seqnum) {
         GST_LOG_OBJECT (pad,
@@ -1897,12 +1913,16 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
   structure = gst_caps_get_structure (caps, 0);
   variant = gst_structure_get_string (structure, "variant");
 
+  if (variant && strcmp (variant, "mse-bytestream") == 0) {
+    demux->variant = VARIANT_MSE_BYTESTREAM;
+  }
+
   if (variant && strcmp (variant, "mss-fragmented") == 0) {
     QtDemuxStream *stream;
     const GValue *value;
 
     demux->fragmented = TRUE;
-    demux->mss_mode = TRUE;
+    demux->variant = VARIANT_MSS_FRAGMENTED;
 
     if (QTDEMUX_N_STREAMS (demux) > 1) {
       /* can't do this, we can only renegotiate for another mss format */
@@ -1973,8 +1993,6 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
       }
     }
     gst_caps_replace (&demux->media_caps, (GstCaps *) mediacaps);
-  } else {
-    demux->mss_mode = FALSE;
   }
 
   return TRUE;
@@ -1987,7 +2005,8 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
 
   GST_DEBUG_OBJECT (qtdemux, "Resetting demux");
 
-  if (hard || qtdemux->upstream_format_is_time) {
+  if (hard || qtdemux->upstream_format_is_time
+      || qtdemux->variant == VARIANT_MSE_BYTESTREAM) {
     qtdemux->state = QTDEMUX_STATE_INITIAL;
     qtdemux->neededbytes = 16;
     qtdemux->todrop = 0;
@@ -2040,6 +2059,11 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
     qtdemux->have_group_id = FALSE;
     qtdemux->group_id = G_MAXUINT;
 
+    qtdemux->gapless_audio_info.type = GAPLESS_AUDIO_INFO_TYPE_NONE;
+    qtdemux->gapless_audio_info.num_start_padding_pcm_frames = 0;
+    qtdemux->gapless_audio_info.num_end_padding_pcm_frames = 0;
+    qtdemux->gapless_audio_info.num_valid_pcm_frames = 0;
+
     g_queue_clear_full (&qtdemux->protection_event_queue,
         (GDestroyNotify) gst_event_unref);
 
@@ -2062,7 +2086,7 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
     qtdemux->n_meta_streams = 0;
     qtdemux->exposed = FALSE;
     qtdemux->fragmented = FALSE;
-    qtdemux->mss_mode = FALSE;
+    qtdemux->variant = VARIANT_NONE;
     gst_caps_replace (&qtdemux->media_caps, NULL);
     qtdemux->timescale = 0;
     qtdemux->got_moov = FALSE;
@@ -2083,10 +2107,12 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
       g_free (qtdemux->preferred_protection_system_id);
       qtdemux->preferred_protection_system_id = NULL;
     }
-  } else if (qtdemux->mss_mode) {
+  } else if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
     gst_flow_combiner_reset (qtdemux->flowcombiner);
     g_ptr_array_foreach (qtdemux->active_streams,
         (GFunc) gst_qtdemux_stream_clear, NULL);
+  } else if (qtdemux->variant == VARIANT_MSE_BYTESTREAM) {
+    /* Do nothing */
   } else {
     gst_flow_combiner_reset (qtdemux->flowcombiner);
     for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
@@ -2571,6 +2597,12 @@ gst_qtdemux_stream_clear (QtDemuxStream * stream)
         gst_structure_free (info->default_properties);
       if (info->crypto_info)
         g_ptr_array_free (info->crypto_info, TRUE);
+      if (info->fragment_group_properties)
+        g_ptr_array_free (info->fragment_group_properties, TRUE);
+      if (info->track_group_properties)
+        g_ptr_array_free (info->track_group_properties, TRUE);
+      if (info->sample_to_group_map)
+        g_ptr_array_free (info->sample_to_group_map, FALSE);
     }
     if (stream->protection_scheme_type == FOURCC_aavd) {
       QtDemuxAavdEncryptionInfo *info =
@@ -3121,7 +3153,7 @@ qtdemux_parse_cstb (GstQTDemux * qtdemux, GstByteReader * data)
 
 /* caller verifies at least 8 bytes in buf */
 static void
-extract_initial_length_and_fourcc (const guint8 * data, guint size,
+extract_initial_length_and_fourcc (const guint8 * data, gsize size,
     guint64 * plength, guint32 * pfourcc)
 {
   guint64 length;
@@ -3235,7 +3267,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
 
   /* even then, above values are better than random ... */
   if (G_UNLIKELY (!stream->parsed_trex)) {
-    GST_WARNING_OBJECT (qtdemux,
+    GST_INFO_OBJECT (qtdemux,
         "failed to find fragment defaults for stream %d", stream->track_id);
     return FALSE;
   }
@@ -3322,7 +3354,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
     QtDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
     guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
     gint64 * base_offset, gint64 * running_offset, gint64 decode_ts,
-    gboolean has_tfdt)
+    gboolean has_tfdt, guint trun_node_total)
 {
   GstClockTime gst_ts = GST_CLOCK_TIME_NONE;
   guint64 timestamp;
@@ -3332,13 +3364,14 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
   gint i;
   guint8 *data;
   guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
+  guint new_n_samples;
   QtDemuxSample *sample;
   gboolean ismv = FALSE;
   gint64 initial_offset;
   gint32 min_ct = 0;
 
-  GST_LOG_OBJECT (qtdemux, "parsing trun track-id %d; "
-      "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
+  GST_LOG_OBJECT (qtdemux, "parsing trun track-id %u; "
+      "default dur %u, size %u, flags 0x%x, base offset %" G_GINT64_FORMAT ", "
       "decode ts %" G_GINT64_FORMAT, stream->track_id, d_sample_duration,
       d_sample_size, d_sample_flags, *base_offset, decode_ts);
 
@@ -3366,7 +3399,18 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
     /* note this is really signed */
     if (!gst_byte_reader_get_int32_be (trun, &data_offset))
       goto fail;
-    GST_LOG_OBJECT (qtdemux, "trun data offset %d", data_offset);
+
+    if (qtdemux->variant == VARIANT_MSS_FRAGMENTED
+        && data_offset <= moof_length
+        && *base_offset == -1 && trun_node_total == 1) {
+      /* MSS spec states that, if only one TrunBox is specified, then the
+       * DataOffset field MUST be the sum of the lengths of the MoofBox and all
+       * the fields in the MdatBox field */
+      GST_WARNING_OBJECT (qtdemux,
+          "trun offset is less than the moof size, assuming offset is after moof");
+      data_offset = moof_length + 8;
+    }
+    GST_LOG_OBJECT (qtdemux, "trun data offset %u", data_offset);
     /* default base offset = first byte of moof */
     if (*base_offset == -1) {
       GST_LOG_OBJECT (qtdemux, "base_offset at moof");
@@ -3388,7 +3432,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
 
   GST_LOG_OBJECT (qtdemux, "running offset now %" G_GINT64_FORMAT,
       *running_offset);
-  GST_LOG_OBJECT (qtdemux, "trun offset %d, flags 0x%x, entries %d",
+  GST_LOG_OBJECT (qtdemux, "trun offset %u, flags 0x%x, entries %u",
       data_offset, flags, samples_count);
 
   if (flags & TR_FIRST_SAMPLE_FLAGS) {
@@ -3432,14 +3476,13 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
     goto fail;
   data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
 
-  if (stream->n_samples + samples_count >=
-      QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
+  if (!g_uint_checked_add (&new_n_samples, stream->n_samples, samples_count) ||
+      new_n_samples >= QTDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (QtDemuxSample))
     goto index_too_big;
 
   GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
-      stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
-      (stream->n_samples + samples_count) *
-      sizeof (QtDemuxSample) / (1024.0 * 1024.0));
+      new_n_samples, (guint) sizeof (QtDemuxSample),
+      (new_n_samples) * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
 
   /* create a new array of samples if it's the first sample parsed */
   if (stream->n_samples == 0) {
@@ -3448,7 +3491,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
     /* or try to reallocate it with space enough to insert the new samples */
   } else
     stream->samples = g_try_renew (QtDemuxSample, stream->samples,
-        stream->n_samples + samples_count);
+        new_n_samples);
   if (stream->samples == NULL)
     goto out_of_memory;
 
@@ -3598,14 +3641,15 @@ fail:
   }
 out_of_memory:
   {
-    GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples",
-        stream->n_samples);
+    GST_WARNING_OBJECT (qtdemux, "failed to allocate %u + %u samples",
+        stream->n_samples, samples_count);
     return FALSE;
   }
 index_too_big:
   {
-    GST_WARNING_OBJECT (qtdemux, "not allocating index of %d samples, would "
-        "be larger than %uMB (broken file?)", stream->n_samples,
+    GST_WARNING_OBJECT (qtdemux,
+        "not allocating index of %u + %u samples, would "
+        "be larger than %uMB (broken file?)", stream->n_samples, samples_count,
         QTDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
     return FALSE;
   }
@@ -3629,7 +3673,7 @@ qtdemux_find_stream (GstQTDemux * qtdemux, guint32 id)
     if (stream->track_id == id)
       return stream;
   }
-  if (qtdemux->mss_mode) {
+  if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
     /* mss should have only 1 stream anyway */
     return QTDEMUX_NTH_STREAM (qtdemux, 0);
   }
@@ -3696,7 +3740,7 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
     (*stream)->stsd_sample_description_id = sample_description_index - 1;
   }
 
-  if (qtdemux->mss_mode) {
+  if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
     /* mss has no stsd entry */
     (*stream)->stsd_sample_description_id = 0;
   }
@@ -3767,6 +3811,7 @@ qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
     QtDemuxStream * stream, guint sample_index)
 {
   QtDemuxCencSampleSetInfo *info = NULL;
+  GstStructure *properties = NULL;
 
   g_return_val_if_fail (stream != NULL, NULL);
   g_return_val_if_fail (stream->protected, NULL);
@@ -3774,9 +3819,264 @@ qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
 
   info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
 
-  /* Currently, cenc properties for groups of samples are not supported, so
-   * simply return a copy of the default sample properties */
-  return gst_structure_copy (info->default_properties);
+  /* First check if the sample is associated with the 'seig' sample group. */
+  if (info->sample_to_group_map
+      && sample_index < info->sample_to_group_map->len)
+    properties = g_ptr_array_index (info->sample_to_group_map, sample_index);
+
+  /* If not, use the default properties for this sample. */
+  if (!properties)
+    properties = info->default_properties;
+
+  return gst_structure_copy (properties);
+}
+
+static gboolean
+qtdemux_parse_sbgp (GstQTDemux * qtdemux, QtDemuxStream * stream,
+    GstByteReader * br, guint32 group, GPtrArray ** sample_to_group_array,
+    GstStructure * default_properties, GPtrArray * track_properties_array,
+    GPtrArray * group_properties_array)
+{
+  guint32 flags = 0;
+  guint8 version = 0;
+  guint32 count = 0;
+  const guint8 *grouping_type_data = NULL;
+  guint32 grouping_type = 0;
+
+  g_return_val_if_fail (qtdemux != NULL, FALSE);
+  g_return_val_if_fail (stream != NULL, FALSE);
+  g_return_val_if_fail (br != NULL, FALSE);
+  g_return_val_if_fail (*sample_to_group_array == NULL, FALSE);
+  g_return_val_if_fail (group_properties_array != NULL, FALSE);
+
+  if (!gst_byte_reader_get_uint32_be (br, &flags))
+    return FALSE;
+
+  if (!gst_byte_reader_get_data (br, 4, &grouping_type_data))
+    return FALSE;
+
+  grouping_type = QT_FOURCC (grouping_type_data);
+  if (grouping_type != group) {
+    /* There may be other groups, so just log this... */
+    GST_DEBUG_OBJECT (qtdemux, "Unsupported grouping type: '%"
+        GST_FOURCC_FORMAT "'", GST_FOURCC_ARGS (grouping_type));
+    return FALSE;
+  }
+
+  version = (flags >> 24);
+  if (version > 0) {
+    GST_WARNING_OBJECT (qtdemux, "Unsupported 'sbgp' box version: %hhu",
+        version);
+    return FALSE;
+  }
+
+  if (!gst_byte_reader_get_uint32_be (br, &count))
+    return FALSE;
+
+  GST_LOG_OBJECT (qtdemux, "flags: %08x, type: '%" GST_FOURCC_FORMAT
+      "', count: %u", flags, GST_FOURCC_ARGS (grouping_type), count);
+
+  if (count > 0)
+    *sample_to_group_array = g_ptr_array_sized_new (count);
+
+  while (count--) {
+    guint32 samples;
+    guint32 index;
+    GstStructure *properties = NULL;
+
+    if (!gst_byte_reader_get_uint32_be (br, &samples))
+      goto error;
+
+    if (!gst_byte_reader_get_uint32_be (br, &index))
+      goto error;
+
+    if (index > 0x10000) {
+      /* Index is referring the current fragment. */
+      index -= 0x10001;
+      if (group_properties_array && index < group_properties_array->len)
+        properties = g_ptr_array_index (group_properties_array, index);
+      else
+        GST_ERROR_OBJECT (qtdemux, "invalid group index %u", index);
+    } else if (index > 0) {
+      /* Index is referring to the whole track. */
+      index--;
+      if (track_properties_array && index < track_properties_array->len)
+        properties = g_ptr_array_index (track_properties_array, index);
+      else
+        GST_ERROR_OBJECT (qtdemux, "invalid group index %u", index);
+    } else {
+      /* If zero, then this range of samples does not belong to this group,
+         perhaps to another one or to none at all. */
+    }
+
+    GST_DEBUG_OBJECT (qtdemux, "assigning group '%" GST_FOURCC_FORMAT
+        "' index %i for the next %i samples: %" GST_PTR_FORMAT,
+        GST_FOURCC_ARGS (grouping_type), index, samples, properties);
+
+    while (samples--)
+      g_ptr_array_add (*sample_to_group_array, properties);
+  }
+
+  return TRUE;
+
+error:
+  g_ptr_array_free (*sample_to_group_array, TRUE);
+  *sample_to_group_array = NULL;
+  return FALSE;
+}
+
+static gboolean
+qtdemux_parse_sgpd (GstQTDemux * qtdemux, QtDemuxStream * stream,
+    GstByteReader * br, guint32 group, GPtrArray ** properties)
+{
+  guint32 flags = 0;
+  guint8 version = 0;
+  guint32 default_length = 0;
+  guint32 count = 0;
+  const guint8 *grouping_type_data = NULL;
+  guint32 grouping_type = 0;
+  const guint32 min_entry_size = 20;
+
+  g_return_val_if_fail (qtdemux != NULL, FALSE);
+  g_return_val_if_fail (stream != NULL, FALSE);
+  g_return_val_if_fail (br != NULL, FALSE);
+  g_return_val_if_fail (*properties == NULL, FALSE);
+
+  if (!gst_byte_reader_get_uint32_be (br, &flags))
+    return FALSE;
+
+  if (!gst_byte_reader_get_data (br, 4, &grouping_type_data))
+    return FALSE;
+
+  grouping_type = QT_FOURCC (grouping_type_data);
+  if (grouping_type != group) {
+    GST_WARNING_OBJECT (qtdemux, "Unhandled grouping type: '%"
+        GST_FOURCC_FORMAT "'", GST_FOURCC_ARGS (grouping_type));
+    return FALSE;
+  }
+
+  version = (flags >> 24);
+  if (version == 1) {
+    if (!gst_byte_reader_get_uint32_be (br, &default_length))
+      return FALSE;
+  } else if (version > 1) {
+    GST_WARNING_OBJECT (qtdemux, "Unsupported 'sgpd' box version: %hhu",
+        version);
+    return FALSE;
+  }
+
+  if (!gst_byte_reader_get_uint32_be (br, &count))
+    return FALSE;
+
+  GST_LOG_OBJECT (qtdemux, "flags: %08x, type: '%" GST_FOURCC_FORMAT
+      "', count: %u", flags, GST_FOURCC_ARGS (grouping_type), count);
+
+  if (count)
+    *properties = g_ptr_array_sized_new (count);
+
+  for (guint32 index = 0; index < count; index++) {
+    GstStructure *props = NULL;
+    guint32 length = default_length;
+    const guint8 *entry_data = NULL;
+    guint8 is_encrypted = 0;
+    guint8 iv_size = 0;
+    guint8 constant_iv_size = 0;
+    const guint8 *kid = NULL;
+    guint8 crypt_byte_block = 0;
+    guint8 skip_byte_block = 0;
+    const guint8 *constant_iv = NULL;
+    GstBuffer *kid_buf;
+
+    if (version == 1 && length == 0) {
+      if (!gst_byte_reader_get_uint32_be (br, &length))
+        goto error;
+    }
+
+    if (G_UNLIKELY (length < min_entry_size)) {
+      GST_ERROR_OBJECT (qtdemux, "Invalid entry size: %u", length);
+      goto error;
+    }
+
+    if (!gst_byte_reader_get_data (br, length, &entry_data))
+      goto error;
+
+    /* Follows tenc format... */
+    is_encrypted = QT_UINT8 (entry_data + 2);
+    iv_size = QT_UINT8 (entry_data + 3);
+    kid = (entry_data + 4);
+
+    if (stream->protection_scheme_type == FOURCC_cbcs) {
+      guint8 possible_pattern_info;
+
+      if (iv_size == 0) {
+        if (G_UNLIKELY (length < min_entry_size + 1)) {
+          GST_ERROR_OBJECT (qtdemux, "Invalid entry size: %u", length);
+          goto error;
+        }
+
+        constant_iv_size = QT_UINT8 (entry_data + 20);
+        if (G_UNLIKELY (constant_iv_size != 8 && constant_iv_size != 16)) {
+          GST_ERROR_OBJECT (qtdemux,
+              "constant IV size should be 8 or 16, not %hhu", constant_iv_size);
+          goto error;
+        }
+
+        if (G_UNLIKELY (length < min_entry_size + 1 + constant_iv_size)) {
+          GST_ERROR_OBJECT (qtdemux, "Invalid entry size: %u", length);
+          goto error;
+        }
+
+        constant_iv = (entry_data + 21);
+      }
+
+      possible_pattern_info = QT_UINT8 (entry_data + 1);
+      crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
+      skip_byte_block = possible_pattern_info & 0x0f;
+    }
+
+    kid_buf = gst_buffer_new_memdup (kid, 16);
+
+    props = gst_structure_new ("application/x-cenc",
+        "iv_size", G_TYPE_UINT, iv_size,
+        "encrypted", G_TYPE_BOOLEAN, is_encrypted == 1,
+        "kid", GST_TYPE_BUFFER, kid_buf, NULL);
+
+    gst_buffer_unref (kid_buf);
+
+    if (stream->protection_scheme_type == FOURCC_cbcs) {
+      if (crypt_byte_block != 0 || skip_byte_block != 0) {
+        gst_structure_set (props,
+            "crypt_byte_block", G_TYPE_UINT, crypt_byte_block,
+            "skip_byte_block", G_TYPE_UINT, skip_byte_block, NULL);
+      }
+
+      if (constant_iv != NULL) {
+        GstBuffer *constant_iv_buf = gst_buffer_new_memdup (
+            (guint8 *) constant_iv, constant_iv_size);
+        gst_structure_set (props,
+            "constant_iv_size", G_TYPE_UINT, constant_iv_size,
+            "iv", GST_TYPE_BUFFER, constant_iv_buf, NULL);
+        gst_buffer_unref (constant_iv_buf);
+      }
+
+      gst_structure_set (props, "cipher-mode", G_TYPE_STRING, "cbcs", NULL);
+    } else {
+      gst_structure_set (props, "cipher-mode", G_TYPE_STRING, "cenc", NULL);
+    }
+
+    GST_INFO_OBJECT (qtdemux, "properties for group '%"
+        GST_FOURCC_FORMAT "' at index %u: %" GST_PTR_FORMAT,
+        GST_FOURCC_ARGS (grouping_type), index, props);
+
+    g_ptr_array_add (*properties, props);
+  }
+
+  return TRUE;
+
+error:
+  g_ptr_array_free (*properties, TRUE);
+  *properties = NULL;
+  return FALSE;
 }
 
 /* Parses the sizes of sample auxiliary information contained within a stream,
@@ -4132,6 +4432,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
   traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
   while (traf_node) {
     guint64 decode_time = 0;
+    guint trun_node_total = 0;
 
     /* Fragment Header node */
     tfhd_node =
@@ -4143,6 +4444,60 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
             &ds_size, &ds_flags, &base_offset))
       goto missing_tfhd;
 
+    /* Sample grouping support */
+    if (stream != NULL && stream->protected
+        && (stream->protection_scheme_type == FOURCC_cenc
+            || stream->protection_scheme_type == FOURCC_cbcs)) {
+      QtDemuxCencSampleSetInfo *info = stream->protection_scheme_info;
+      GNode *sgpd_node;
+      GstByteReader sgpd_data;
+
+      if (!info) {
+        GST_ERROR_OBJECT (qtdemux, "Have no valid protection scheme info");
+        goto fail;
+      }
+
+      if (info->fragment_group_properties) {
+        g_ptr_array_free (info->fragment_group_properties, TRUE);
+        info->fragment_group_properties = NULL;
+      }
+
+      if (info->sample_to_group_map) {
+        g_ptr_array_free (info->sample_to_group_map, FALSE);
+        info->sample_to_group_map = NULL;
+      }
+
+      /* Check if sample grouping information is present for this segment. */
+      /* However look only for 'seig' (CENC encryption) grouping type... */
+      sgpd_node = qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_sgpd,
+          &sgpd_data);
+      while (sgpd_node) {
+        if (qtdemux_parse_sgpd (qtdemux, stream, &sgpd_data, FOURCC_seig,
+                &info->fragment_group_properties)) {
+          /* CENC encryption grouping found, don't look further. */
+          break;
+        }
+        sgpd_node = qtdemux_tree_get_sibling_by_type_full (sgpd_node,
+            FOURCC_sgpd, &sgpd_data);
+      }
+
+      if (info->fragment_group_properties) {
+        GstByteReader sbgp_data;
+        GNode *sbgp_node = qtdemux_tree_get_child_by_type_full (traf_node,
+            FOURCC_sbgp, &sbgp_data);
+        while (sbgp_node) {
+          if (qtdemux_parse_sbgp (qtdemux, stream, &sbgp_data, FOURCC_seig,
+                  &info->sample_to_group_map, info->default_properties,
+                  info->track_group_properties,
+                  info->fragment_group_properties)) {
+            break;
+          }
+          sbgp_node = qtdemux_tree_get_sibling_by_type_full (sbgp_node,
+              FOURCC_sgpd, &sbgp_data);
+        }
+      }
+    }
+
     /* The following code assumes at most a single set of sample auxiliary
      * data in the fragment (consisting of a saiz box and a corresponding saio
      * box); in theory, however, there could be multiple sets of sample
@@ -4251,6 +4606,20 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     stream->duration_last_moof = stream->duration_moof;
     stream->duration_moof = 0;
 
+    /* Count the number of trun nodes */
+    if (qtdemux->variant == VARIANT_MSS_FRAGMENTED) {
+      trun_node =
+          qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
+          &trun_data);
+      while (trun_node) {
+        trun_node_total++;
+        trun_node =
+            qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
+            &trun_data);
+      }
+      GST_LOG_OBJECT (qtdemux, "%u trun node(s) available", trun_node_total);
+    }
+
     /* Track Run node */
     trun_node =
         qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
@@ -4258,7 +4627,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     while (trun_node) {
       qtdemux_parse_trun (qtdemux, &trun_data, stream,
           ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
-          &running_offset, decode_time, (tfdt_node != NULL));
+          &running_offset, decode_time, (tfdt_node != NULL), trun_node_total);
       /* iterate all siblings */
       trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
           &trun_data);
@@ -4299,7 +4668,9 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     pssh_node = qtdemux_tree_get_sibling_by_type (pssh_node, FOURCC_pssh);
   }
 
-  if (!qtdemux->upstream_format_is_time && !qtdemux->first_moof_already_parsed
+  if (!qtdemux->upstream_format_is_time
+      && qtdemux->variant != VARIANT_MSE_BYTESTREAM
+      && !qtdemux->first_moof_already_parsed
       && !qtdemux->received_seek && GST_CLOCK_TIME_IS_VALID (min_dts)
       && min_dts != 0) {
     /* Unless the user has explicitly requested another seek, perform an
@@ -4642,6 +5013,36 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
         goto beach;
       }
 
+      if (length == G_MAXUINT64) {
+        /* Read until the end */
+        gint64 duration;
+        if (!gst_pad_peer_query_duration (qtdemux->sinkpad, GST_FORMAT_BYTES,
+                &duration)) {
+          GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
+              (_("Cannot query file size")),
+              ("Duration query on sink pad failed"));
+          ret = GST_FLOW_ERROR;
+          goto beach;
+        }
+        if (G_UNLIKELY (cur_offset > duration)) {
+          GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
+              (_("Cannot query file size")),
+              ("Duration %" G_GINT64_FORMAT " < current offset %"
+                  G_GUINT64_FORMAT, duration, cur_offset));
+          ret = GST_FLOW_ERROR;
+          goto beach;
+        }
+        length = duration - cur_offset;
+        if (length > QTDEMUX_MAX_ATOM_SIZE) {
+          GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
+              (_("Cannot demux file")),
+              ("Moov atom size %" G_GINT64_FORMAT " > maximum %d", length,
+                  QTDEMUX_MAX_ATOM_SIZE));
+          ret = GST_FLOW_ERROR;
+          goto beach;
+        }
+      }
+
       ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
       if (ret != GST_FLOW_OK)
         goto beach;
@@ -4676,8 +5077,8 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
         GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
             (_("This file is incomplete and cannot be played.")),
             ("We got less than expected (received %" G_GSIZE_FORMAT
-                ", wanted %u, offset %" G_GUINT64_FORMAT ")", map.size,
-                (guint) length, cur_offset));
+                ", wanted %" G_GUINT64_FORMAT ", offset %" G_GUINT64_FORMAT ")",
+                map.size, length, cur_offset));
         gst_buffer_unmap (moov, &map);
         gst_buffer_unref (moov);
         ret = GST_FLOW_ERROR;
@@ -4810,10 +5211,15 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
 beach:
   if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
     /* digested all data, show what we have */
-    qtdemux_prepare_streams (qtdemux);
+    ret = qtdemux_prepare_streams (qtdemux);
+    if (ret != GST_FLOW_OK)
+      return ret;
+
     QTDEMUX_EXPOSE_LOCK (qtdemux);
     ret = qtdemux_expose_streams (qtdemux);
     QTDEMUX_EXPOSE_UNLOCK (qtdemux);
+    if (ret != GST_FLOW_OK)
+      return ret;
 
     qtdemux->state = QTDEMUX_STATE_MOVIE;
     GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
@@ -4978,7 +5384,12 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
     }
 
     /* Remember until where we want to go */
-    str->to_sample = str->from_sample - 1;
+    if (str->from_sample == 0) {
+      GST_LOG_OBJECT (qtdemux, "already at sample 0");
+      str->to_sample = 0;
+    } else {
+      str->to_sample = str->from_sample - 1;
+    }
     /* Define our time position */
     target_ts =
         str->samples[k_index].timestamp + str->samples[k_index].pts_offset;
@@ -5131,6 +5542,14 @@ gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
   stream->segment.time = time;
   stream->segment.position = stream->segment.start;
 
+  /* Gapless audio requires adjustments to the segment
+   * to reflect the actual playtime length. In
+   * particular, this must exclude padding data. */
+  if (qtdemux->gapless_audio_info.type != GAPLESS_AUDIO_INFO_TYPE_NONE) {
+    stream->segment.stop = stream->segment.start +
+        qtdemux->gapless_audio_info.valid_duration;
+  }
+
   GST_DEBUG_OBJECT (stream->pad, "New segment: %" GST_SEGMENT_FORMAT,
       &stream->segment);
 
@@ -5736,6 +6155,11 @@ convert_to_s334_1a (const guint8 * ccpair, guint8 ccpair_size, guint field,
   guint8 *storage;
   gsize i;
 
+  /* Strip off any leftover odd bytes and assume everything before is valid */
+  if (ccpair_size % 2 != 0) {
+    ccpair_size -= 1;
+  }
+
   /* We are converting from pairs to triplets */
   *res = ccpair_size / 2 * 3;
   storage = g_malloc (*res);
@@ -5769,7 +6193,7 @@ extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
     goto invalid_cdat;
   atom_length = QT_UINT32 (data);
   fourcc = QT_FOURCC (data + 4);
-  if (G_UNLIKELY (atom_length > size || atom_length == 8))
+  if (G_UNLIKELY (atom_length > size || atom_length <= 8))
     goto invalid_cdat;
 
   GST_DEBUG_OBJECT (stream->pad, "here");
@@ -5809,7 +6233,7 @@ extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
             else
               GST_WARNING_OBJECT (stream->pad,
                   "Got multiple [cdat] atoms in a c608 sample. This is unsupported for now. Please file a bug");
-          } else {
+          } else if (fourcc == FOURCC_cdt2) {
             if (cdt2 == NULL)
               cdt2 =
                   convert_to_s334_1a (data + atom_length + 8,
@@ -5817,6 +6241,10 @@ extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
             else
               GST_WARNING_OBJECT (stream->pad,
                   "Got multiple [cdt2] atoms in a c608 sample. This is unsupported for now. Please file a bug");
+          } else {
+            GST_WARNING_OBJECT (stream->pad,
+                "Unknown second data atom (%" GST_FOURCC_FORMAT ") for CEA608",
+                GST_FOURCC_ARGS (fourcc));
           }
         }
       }
@@ -6038,6 +6466,83 @@ gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
       GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
   }
 
+  if (qtdemux->gapless_audio_info.type != GAPLESS_AUDIO_INFO_TYPE_NONE) {
+    guint64 num_start_padding_pcm_frames;
+    guint64 audio_sample_offset;
+    guint64 audio_sample_offset_end;
+    guint64 start_of_trailing_padding;
+    guint64 start_clip = 0, end_clip = 0;
+    guint64 total_num_clipped_samples;
+    GstClockTime timestamp_decrement;
+
+    /* Attach GstAudioClippingMeta to exclude padding data. */
+
+    num_start_padding_pcm_frames =
+        qtdemux->gapless_audio_info.num_start_padding_pcm_frames;
+
+    audio_sample_offset = stream->sample_index * stream->stts_duration;
+    audio_sample_offset_end = audio_sample_offset + stream->stts_duration;
+    start_of_trailing_padding = num_start_padding_pcm_frames +
+        qtdemux->gapless_audio_info.num_valid_pcm_frames;
+
+    if (audio_sample_offset < num_start_padding_pcm_frames) {
+      guint64 num_padding_audio_samples =
+          num_start_padding_pcm_frames - audio_sample_offset;
+      start_clip = MIN (num_padding_audio_samples, stream->stts_duration);
+    }
+
+    timestamp_decrement = qtdemux->gapless_audio_info.start_padding_duration;
+
+    if (audio_sample_offset >= start_of_trailing_padding) {
+      /* This case happens when the buffer is located fully past
+       * the beginning of the padding area at the end of the stream.
+       * Add the end padding to the decrement amount to ensure
+       * continuous timestamps when transitioning from gapless
+       * media to gapless media. */
+      end_clip = stream->stts_duration;
+      timestamp_decrement += qtdemux->gapless_audio_info.end_padding_duration;
+    } else if (audio_sample_offset_end >= start_of_trailing_padding) {
+      /* This case happens when the beginning of the padding area that
+       * is located at the end of the stream intersects the buffer. */
+      end_clip = audio_sample_offset_end - start_of_trailing_padding;
+    }
+
+    total_num_clipped_samples = start_clip + end_clip;
+
+    if (total_num_clipped_samples != 0) {
+      GST_DEBUG_OBJECT (qtdemux, "adding audio clipping meta: start / "
+          "end clip: %" G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT,
+          start_clip, end_clip);
+      gst_buffer_add_audio_clipping_meta (buf, GST_FORMAT_DEFAULT,
+          start_clip, end_clip);
+
+      if (total_num_clipped_samples >= stream->stts_duration) {
+        GST_BUFFER_DURATION (buf) = 0;
+        GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY);
+        GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DROPPABLE);
+      } else {
+        guint64 num_valid_samples =
+            stream->stts_duration - total_num_clipped_samples;
+        GST_BUFFER_DURATION (buf) =
+            QTSTREAMTIME_TO_GSTTIME (stream, num_valid_samples);
+      }
+    }
+
+    /* The timestamps need to be shifted to factor in the skipped padding data. */
+
+    if (GST_BUFFER_PTS_IS_VALID (buf)) {
+      GstClockTime ts = GST_BUFFER_PTS (buf);
+      GST_BUFFER_PTS (buf) =
+          (ts >= timestamp_decrement) ? (ts - timestamp_decrement) : 0;
+    }
+
+    if (GST_BUFFER_DTS_IS_VALID (buf)) {
+      GstClockTime ts = GST_BUFFER_DTS (buf);
+      GST_BUFFER_DTS (buf) =
+          (ts >= timestamp_decrement) ? (ts - timestamp_decrement) : 0;
+    }
+  }
+
   if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
           || stream->protection_scheme_type == FOURCC_cbcs)) {
     GstStructure *crypto_info;
@@ -6054,13 +6559,22 @@ gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
 
     if (info->crypto_info == NULL) {
       if (stream->protection_scheme_type == FOURCC_cbcs) {
-        crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
-        if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) {
-          GST_ERROR_OBJECT (qtdemux,
-              "failed to attach cbcs metadata to buffer");
-          qtdemux_gst_structure_free (crypto_info);
+        if (CUR_STREAM (stream)->fourcc == FOURCC_enca ||
+            CUR_STREAM (stream)->fourcc == FOURCC_encs ||
+            CUR_STREAM (stream)->fourcc == FOURCC_enct ||
+            CUR_STREAM (stream)->fourcc == FOURCC_encv) {
+          crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
+          if (!crypto_info
+              || !gst_buffer_add_protection_meta (buf, crypto_info)) {
+            GST_ERROR_OBJECT (qtdemux,
+                "failed to attach cbcs metadata to buffer");
+            qtdemux_gst_structure_free (crypto_info);
+          } else {
+            GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
+          }
         } else {
-          GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
+          GST_TRACE_OBJECT (qtdemux,
+              "cbcs stream is not encrypted yet, not adding protection metadata");
         }
       } else {
         GST_DEBUG_OBJECT (qtdemux,
@@ -6509,16 +7023,8 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
     stream = QTDEMUX_NTH_STREAM (qtdemux, i);
     position = stream->time_position;
 
-    if (!GST_CLOCK_TIME_IS_VALID (position))
-      continue;
-
-    if (stream->segment_index != -1) {
-      QtDemuxSegment *segment = &stream->segments[stream->segment_index];
-      position += segment->media_start;
-    }
-
     /* position of -1 is EOS */
-    if (position < min_time) {
+    if (position != GST_CLOCK_TIME_NONE && position < min_time) {
       min_time = position;
       target_stream = stream;
     }
@@ -6897,6 +7403,21 @@ pause:
           GstMessage *message;
           GstEvent *event;
 
+          /* segment.position will still be at the last timestamp and won't always
+           * include the duration of the last packet. Expand that to the segment
+           * duration so that segment.base is increased correctly to include the
+           * length of the last packet when doing segment seeks. We need to do
+           * this before the segment-done event goes out so everything's ready
+           * for the next seek request coming in. */
+          if (GST_CLOCK_TIME_IS_VALID (stop)) {
+            GST_DEBUG_OBJECT (qtdemux,
+                "End of segment, updating segment.position from %"
+                GST_TIME_FORMAT " to stop %" GST_TIME_FORMAT,
+                GST_TIME_ARGS (qtdemux->segment.position),
+                GST_TIME_ARGS (stop));
+            qtdemux->segment.position = stop;
+          }
+
           GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
           message = gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
               GST_FORMAT_TIME, stop);
@@ -7189,6 +7710,129 @@ gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
   }
 }
 
+static void
+qtdemux_check_if_is_gapless_audio (GstQTDemux * qtdemux)
+{
+  QtDemuxStream *stream;
+
+  if (QTDEMUX_N_STREAMS (qtdemux) != 1)
+    goto incompatible_stream;
+
+  stream = QTDEMUX_NTH_STREAM (qtdemux, 0);
+
+  if (stream->subtype != FOURCC_soun || stream->n_segments != 1)
+    goto incompatible_stream;
+
+  /* Gapless audio info from revdns tags (most notably iTunSMPB) is
+   * detected in the main udta node. If it isn't present, try as
+   * fallback to recognize the encoder name, and apply known priming
+   * and padding quantities specific to the encoder. */
+  if (qtdemux->gapless_audio_info.type == GAPLESS_AUDIO_INFO_TYPE_NONE) {
+    const gchar *orig_encoder_name = NULL;
+
+    if (gst_tag_list_peek_string_index (qtdemux->tag_list, GST_TAG_ENCODER, 0,
+            &orig_encoder_name) && orig_encoder_name != NULL) {
+      gchar *lowercase_encoder_name = g_ascii_strdown (orig_encoder_name, -1);
+
+      if (strstr (lowercase_encoder_name, "nero") != NULL)
+        qtdemux->gapless_audio_info.type = GAPLESS_AUDIO_INFO_TYPE_NERO;
+
+      g_free (lowercase_encoder_name);
+
+      switch (qtdemux->gapless_audio_info.type) {
+        case GAPLESS_AUDIO_INFO_TYPE_NERO:{
+          guint64 total_length;
+          guint64 valid_length;
+          guint64 start_padding;
+
+          /* The Nero AAC encoder always uses a lead-in of 1600 PCM frames.
+           * Also, in Nero AAC's case, stream->duration contains the number
+           * of PCM frames with start padding but without end padding.
+           * The decoder delay equals 1 frame length, which is covered by
+           * factoring stream->stts_duration into the start padding. */
+          start_padding = 1600 + stream->stts_duration;
+
+          if (G_UNLIKELY (stream->duration < start_padding)) {
+            GST_ERROR_OBJECT (qtdemux, "stream duration is %" G_GUINT64_FORMAT
+                " but start_padding is %" G_GUINT64_FORMAT, stream->duration,
+                start_padding);
+            goto invalid_gapless_audio_info;
+          }
+          valid_length = stream->duration - start_padding;
+
+          qtdemux->gapless_audio_info.num_start_padding_pcm_frames =
+              start_padding;
+          qtdemux->gapless_audio_info.num_valid_pcm_frames = valid_length;
+
+          total_length = stream->n_samples * stream->stts_duration;
+
+          if (G_LIKELY (total_length >= valid_length)) {
+            guint64 total_padding = total_length - valid_length;
+            if (G_UNLIKELY (total_padding < start_padding)) {
+              GST_ERROR_OBJECT (qtdemux, "total_padding is %" G_GUINT64_FORMAT
+                  " but start_padding is %" G_GUINT64_FORMAT, total_padding,
+                  start_padding);
+              goto invalid_gapless_audio_info;
+            }
+
+            qtdemux->gapless_audio_info.num_end_padding_pcm_frames =
+                total_padding - start_padding;
+          } else {
+            qtdemux->gapless_audio_info.num_end_padding_pcm_frames = 0;
+          }
+
+          GST_DEBUG_OBJECT (qtdemux, "media was encoded with Nero AAC encoder; "
+              "using encoder specific lead-in and padding figures");
+        }
+
+        default:
+          break;
+      }
+    }
+  }
+
+  if (qtdemux->gapless_audio_info.type != GAPLESS_AUDIO_INFO_TYPE_NONE) {
+    qtdemux->gapless_audio_info.start_padding_duration =
+        QTSTREAMTIME_TO_GSTTIME (stream,
+        qtdemux->gapless_audio_info.num_start_padding_pcm_frames);
+    qtdemux->gapless_audio_info.end_padding_duration =
+        QTSTREAMTIME_TO_GSTTIME (stream,
+        qtdemux->gapless_audio_info.num_end_padding_pcm_frames);
+    qtdemux->gapless_audio_info.valid_duration =
+        QTSTREAMTIME_TO_GSTTIME (stream,
+        qtdemux->gapless_audio_info.num_valid_pcm_frames);
+  }
+
+  GST_DEBUG_OBJECT (qtdemux, "found valid gapless audio info: num start / end "
+      "PCM padding frames: %" G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "; "
+      "start / end padding durations: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT
+      "; num valid PCM frames: %" G_GUINT64_FORMAT "; valid duration: %"
+      GST_TIME_FORMAT, qtdemux->gapless_audio_info.num_start_padding_pcm_frames,
+      qtdemux->gapless_audio_info.num_end_padding_pcm_frames,
+      GST_TIME_ARGS (qtdemux->gapless_audio_info.start_padding_duration),
+      GST_TIME_ARGS (qtdemux->gapless_audio_info.end_padding_duration),
+      qtdemux->gapless_audio_info.num_valid_pcm_frames,
+      GST_TIME_ARGS (qtdemux->gapless_audio_info.valid_duration));
+
+  return;
+
+incompatible_stream:
+  if (G_UNLIKELY (qtdemux->gapless_audio_info.type !=
+          GAPLESS_AUDIO_INFO_TYPE_NONE)) {
+    GST_WARNING_OBJECT (qtdemux,
+        "media contains gapless audio info, but it is not suitable for "
+        "gapless audio playback (media must be audio-only, single-stream, "
+        "single-segment; ignoring unusable gapless info");
+    qtdemux->gapless_audio_info.type = GAPLESS_AUDIO_INFO_TYPE_NONE;
+  }
+  return;
+
+invalid_gapless_audio_info:
+  GST_WARNING_OBJECT (qtdemux,
+      "media contains invalid/unusable gapless audio info");
+  return;
+}
+
 static GstFlowReturn
 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
 {
@@ -7282,6 +7926,42 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
   return gst_qtdemux_process_adapter (demux, FALSE);
 }
 
+static guint64
+gst_segment_to_stream_time_clamped (const GstSegment * segment,
+    guint64 position)
+{
+  guint64 segment_stream_time_start;
+  guint64 segment_stream_time_stop = GST_CLOCK_TIME_NONE;
+  guint64 stream_pts_unsigned;
+  int ret;
+
+  g_return_val_if_fail (segment != NULL, GST_CLOCK_TIME_NONE);
+  g_return_val_if_fail (segment->format == GST_FORMAT_TIME,
+      GST_CLOCK_TIME_NONE);
+
+  segment_stream_time_start = segment->time;
+  if (segment->stop != GST_CLOCK_TIME_NONE)
+    segment_stream_time_stop =
+        gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop);
+
+  ret =
+      gst_segment_to_stream_time_full (segment, GST_FORMAT_TIME, position,
+      &stream_pts_unsigned);
+  /* ret == 0 if the segment is invalid (either position, segment->time or the segment start are -1). */
+  g_return_val_if_fail (ret != 0, GST_CLOCK_TIME_NONE);
+
+  if (ret == -1 || stream_pts_unsigned < segment_stream_time_start) {
+    /* Negative or prior to segment start stream time, clamp to segment start. */
+    return segment_stream_time_start;
+  } else if (segment_stream_time_stop != GST_CLOCK_TIME_NONE
+      && stream_pts_unsigned > segment_stream_time_stop) {
+    /* Clamp to segment end. */
+    return segment_stream_time_stop;
+  } else {
+    return stream_pts_unsigned;
+  }
+}
+
 static GstFlowReturn
 gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
 {
@@ -7458,13 +8138,21 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
             gst_qtdemux_stream_concat (demux,
                 demux->old_streams, demux->active_streams);
 
-            qtdemux_parse_moov (demux, data, demux->neededbytes);
+            if (!qtdemux_parse_moov (demux, data, demux->neededbytes)) {
+              ret = GST_FLOW_ERROR;
+              break;
+            }
             qtdemux_node_dump (demux, demux->moov_node);
             qtdemux_parse_tree (demux);
-            qtdemux_prepare_streams (demux);
+            ret = qtdemux_prepare_streams (demux);
+            if (ret != GST_FLOW_OK)
+              break;
+
             QTDEMUX_EXPOSE_LOCK (demux);
-            qtdemux_expose_streams (demux);
+            ret = qtdemux_expose_streams (demux);
             QTDEMUX_EXPOSE_UNLOCK (demux);
+            if (ret != GST_FLOW_OK)
+              break;
 
             demux->got_moov = TRUE;
 
@@ -7553,10 +8241,12 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
             }
 
             /* in MSS we need to expose the pads after the first moof as we won't get a moov */
-            if (demux->mss_mode && !demux->exposed) {
+            if (demux->variant == VARIANT_MSS_FRAGMENTED && !demux->exposed) {
               QTDEMUX_EXPOSE_LOCK (demux);
-              qtdemux_expose_streams (demux);
+              ret = qtdemux_expose_streams (demux);
               QTDEMUX_EXPOSE_UNLOCK (demux);
+              if (ret != GST_FLOW_OK)
+                goto done;
             }
 
             gst_qtdemux_check_send_pending_segment (demux);
@@ -7690,7 +8380,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
       case QTDEMUX_STATE_MOVIE:{
         QtDemuxStream *stream = NULL;
         QtDemuxSample *sample;
-        GstClockTime dts, pts, duration;
+        GstClockTime dts, pts, stream_pts, duration;
         gboolean keyframe;
         gint i;
 
@@ -7802,28 +8492,22 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
 
           dts = QTSAMPLE_DTS (stream, sample);
           pts = QTSAMPLE_PTS (stream, sample);
+          stream_pts =
+              gst_segment_to_stream_time_clamped (&stream->segment, pts);
           duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
           keyframe = QTSAMPLE_KEYFRAME (stream, sample);
 
           /* check for segment end */
-          if (G_UNLIKELY (demux->segment.stop != -1
-                  && demux->segment.stop <= pts && stream->on_keyframe)
-              && !(demux->upstream_format_is_time && demux->segment.rate < 0)) {
+          if (G_UNLIKELY (stream->segment.stop != -1
+                  && stream->segment.stop <= stream_pts && keyframe)
+              && !(demux->upstream_format_is_time && stream->segment.rate < 0)) {
             GST_DEBUG_OBJECT (demux, "we reached the end of our segment.");
             stream->time_position = GST_CLOCK_TIME_NONE;        /* this means EOS */
 
             /* skip this data, stream is EOS */
             gst_adapter_flush (demux->adapter, demux->neededbytes);
-            demux->offset += demux->neededbytes;
 
-            /* check if all streams are eos */
             ret = GST_FLOW_EOS;
-            for (i = 0; i < QTDEMUX_N_STREAMS (demux); i++) {
-              if (!STREAM_IS_EOS (QTDEMUX_NTH_STREAM (demux, i))) {
-                ret = GST_FLOW_OK;
-                break;
-              }
-            }
           } else {
             GstBuffer *outbuf;
 
@@ -8172,7 +8856,7 @@ qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
   end -= 8;
 
   while (buf < end) {
-    gint size;
+    guint32 size;
     guint32 type;
 
     size = QT_UINT32 (buf);
@@ -8180,7 +8864,7 @@ qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream,
 
     GST_LOG_OBJECT (qtdemux, "%p %p", buf, end);
 
-    if (buf + size > end || size <= 0)
+    if (end - buf < size || size < 8)
       break;
 
     buf += 8;
@@ -8271,6 +8955,7 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
       case FOURCC_alac:
       case FOURCC_fLaC:
       case FOURCC_aavd:
+      case FOURCC_opus:
       {
         guint32 version;
         guint32 offset;
@@ -8282,6 +8967,8 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
           min_size = 20;
         else if (fourcc == FOURCC_fLaC)
           min_size = 86;
+        else if (fourcc == FOURCC_opus)
+          min_size = 55;
         else
           min_size = 40;
 
@@ -8941,13 +9628,8 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
       if (CUR_STREAM (stream)->n_channels > 0)
         gst_caps_set_simple (CUR_STREAM (stream)->caps,
             "channels", G_TYPE_INT, CUR_STREAM (stream)->n_channels, NULL);
-      if (CUR_STREAM (stream)->n_channels > 2) {
-        /* FIXME: Need to parse the 'chan' atom to get channel layouts
-         * correctly; this is just the minimum we can do - assume
-         * we don't actually have any channel positions. */
-        gst_caps_set_simple (CUR_STREAM (stream)->caps,
-            "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL);
-      }
+      /* FIXME: Need to parse the 'chan' atom to get channel layouts
+       * correctly. */
     }
   }
 
@@ -9391,6 +10073,21 @@ qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
     return;
   }
 
+  if (gst_byte_reader_get_remaining (&stream->stts) < 8) {
+    GST_DEBUG_OBJECT (qtdemux, "Too small stts");
+    return;
+  }
+
+  if (stream->stco.size < 8) {
+    GST_DEBUG_OBJECT (qtdemux, "Too small stco");
+    return;
+  }
+
+  if (stream->n_samples_per_chunk == 0) {
+    GST_DEBUG_OBJECT (qtdemux, "No samples per chunk");
+    return;
+  }
+
   /* Parse the stts to get the sample duration and number of samples */
   gst_byte_reader_skip_unchecked (&stream->stts, 4);
   stts_duration = gst_byte_reader_get_uint32_be_unchecked (&stream->stts);
@@ -9402,6 +10099,13 @@ qtdemux_merge_sample_table (GstQTDemux * qtdemux, QtDemuxStream * stream)
   GST_DEBUG_OBJECT (qtdemux, "sample_duration %d, num_chunks %u", stts_duration,
       num_chunks);
 
+  if (gst_byte_reader_get_remaining (&stream->stsc) <
+      stream->n_samples_per_chunk * 3 * 4 +
+      (stream->n_samples_per_chunk - 1) * 4) {
+    GST_DEBUG_OBJECT (qtdemux, "Too small stsc");
+    return;
+  }
+
   /* Now parse stsc, convert chunks into single samples and generate a
    * new stsc, stts and stsz from this information */
   gst_byte_writer_init (&stsc);
@@ -9511,7 +10215,7 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
   /* sync sample atom */
   stream->stps_present = FALSE;
   if ((stream->stss_present =
-          ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
+          !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
               &stream->stss) ? TRUE : FALSE) == TRUE) {
     /* copy atom data into a new buffer for later use */
     stream->stss.data = g_memdup2 (stream->stss.data, stream->stss.size);
@@ -9529,7 +10233,7 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
 
     /* partial sync sample atom */
     if ((stream->stps_present =
-            ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
+            !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
                 &stream->stps) ? TRUE : FALSE) == TRUE) {
       /* copy atom data into a new buffer for later use */
       stream->stps.data = g_memdup2 (stream->stps.data, stream->stps.size);
@@ -9629,7 +10333,7 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
 
   /* composition time-to-sample */
   if ((stream->ctts_present =
-          ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
+          !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
               &stream->ctts) ? TRUE : FALSE) == TRUE) {
     GstByteReader cslg = GST_BYTE_READER_INIT (NULL, 0);
     guint8 ctts_version;
@@ -9954,9 +10658,9 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
           goto done;
         }
 
-        cur->offset =
-            qt_atom_parser_get_offset_unchecked (&stream->co_chunk,
-            stream->co_size);
+        if (!qt_atom_parser_get_offset (&stream->co_chunk,
+                stream->co_size, &cur->offset))
+          goto corrupt_file;
 
         GST_LOG_OBJECT (qtdemux, "Created entry %d with offset "
             "%" G_GUINT64_FORMAT, j, cur->offset);
@@ -10502,8 +11206,9 @@ qtdemux_parse_svq3_stsd_data (GstQTDemux * qtdemux,
                 GST_WARNING_OBJECT (qtdemux, "Unexpected second SEQH SMI atom "
                     " found, ignoring");
               } else {
+                /* Note: The size does *not* include the fourcc and the size field itself */
                 seqh_size = QT_UINT32 (data + 4);
-                if (seqh_size > 0) {
+                if (seqh_size > 0 && seqh_size <= size - 8) {
                   _seqh = gst_buffer_new_and_alloc (seqh_size);
                   gst_buffer_fill (_seqh, 0, data + 8, seqh_size);
                 }
@@ -10719,20 +11424,77 @@ qtdemux_parse_transformation_matrix (GstQTDemux * qtdemux,
   matrix[7] = gst_byte_reader_get_uint32_be_unchecked (reader);
   matrix[8] = gst_byte_reader_get_uint32_be_unchecked (reader);
 
+  /* The 2.30 value conversion does not work for negative values */
   GST_DEBUG_OBJECT (qtdemux, "Transformation matrix from atom %s", atom);
-  GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[0] >> 16,
-      matrix[0] & 0xFFFF, matrix[1] >> 16, matrix[1] & 0xFF, matrix[2] >> 16,
-      matrix[2] & 0xFF);
-  GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[3] >> 16,
-      matrix[3] & 0xFFFF, matrix[4] >> 16, matrix[4] & 0xFF, matrix[5] >> 16,
-      matrix[5] & 0xFF);
-  GST_DEBUG_OBJECT (qtdemux, "%u.%u %u.%u %u.%u", matrix[6] >> 16,
-      matrix[6] & 0xFFFF, matrix[7] >> 16, matrix[7] & 0xFF, matrix[8] >> 16,
-      matrix[8] & 0xFF);
+  GST_DEBUG_OBJECT (qtdemux, "%i.%u %i.%u %u.%u", (gint16) (matrix[0] >> 16),
+      matrix[0] & 0xFFFF, (gint16) (matrix[1] >> 16), matrix[1] & 0xFFFF,
+      matrix[2] >> 30, matrix[2] & 0x3FFFFFFF);
+  GST_DEBUG_OBJECT (qtdemux, "%i.%u %i.%u %u.%u", (gint16) (matrix[3] >> 16),
+      matrix[3] & 0xFFFF, (gint16) (matrix[4] >> 16), matrix[4] & 0xFFFF,
+      matrix[5] >> 30, matrix[5] & 0x3FFFFFFF);
+  GST_DEBUG_OBJECT (qtdemux, "%i.%u %i.%u %u.%u", (gint16) (matrix[6] >> 16),
+      matrix[6] & 0xFFFF, (gint16) (matrix[7] >> 16), matrix[7] & 0xFFFF,
+      matrix[8] >> 30, matrix[8] & 0x3FFFFFFF);
+
+  return TRUE;
+}
+
+/* Check if all matrix elements are either 0, 1 or -1 */
+static gboolean
+qtdemux_transformation_matrix_is_simple (guint32 * m)
+{
+  int i;
+
+  for (i = 0; i < 9; i++) {
+    switch (i) {
+      case 2:
+      case 5:
+      case 8:
+        /* 2.30 */
+        if (m[i] != 0U && m[i] != (1U << 30) && m[i] != (3U << 30))
+          return FALSE;
+        break;
+      default:
+        /* 16.16 */
+        if (m[i] != 0U && m[i] != (1U << 16) && m[i] != (G_MAXUINT16 << 16))
+          return FALSE;
+        break;
+    }
+  }
 
   return TRUE;
 }
 
+static void
+qtdemux_mul_transformation_matrix (GstQTDemux * qtdemux,
+    guint32 * a, guint32 * b, guint32 * c)
+{
+#define QTMUL_MATRIX(_a,_b) (((_a) == 0 || (_b) == 0) ? 0 : \
+      ((_a) == (_b) ? 1 : -1))
+#define QTADD_MATRIX(_a,_b) ((_a) + (_b) > 0 ? (1U << 16) : \
+      ((_a) + (_b) < 0) ? (G_MAXUINT16 << 16) : 0u)
+
+  if (!qtdemux_transformation_matrix_is_simple (a) ||
+      !qtdemux_transformation_matrix_is_simple (b)) {
+    GST_WARNING_OBJECT (qtdemux,
+        "Cannot handle transform matrix with element values other than 0, 1 or -1");
+    /* Pretend to have an identity matrix in this case */
+    c[1] = c[2] = c[3] = c[5] = c[6] = c[7] = 0;
+    c[0] = c[4] = (1U << 16);
+    c[8] = (1 << 30);
+  } else {
+    c[2] = c[5] = c[6] = c[7] = 0;
+    c[0] = QTADD_MATRIX (QTMUL_MATRIX (a[0], b[0]), QTMUL_MATRIX (a[1], b[3]));
+    c[1] = QTADD_MATRIX (QTMUL_MATRIX (a[0], b[1]), QTMUL_MATRIX (a[1], b[4]));
+    c[3] = QTADD_MATRIX (QTMUL_MATRIX (a[3], b[0]), QTMUL_MATRIX (a[4], b[3]));
+    c[4] = QTADD_MATRIX (QTMUL_MATRIX (a[3], b[1]), QTMUL_MATRIX (a[4], b[4]));
+    c[8] = a[8];
+  }
+
+#undef QTMUL_MATRIX
+#undef QTADD_MATRIX
+}
+
 static void
 qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
     QtDemuxStream * stream, guint32 * matrix, GstTagList ** taglist)
@@ -10742,7 +11504,7 @@ qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
  * [d e f]
  * [g h i]
  *
- * This macro will only compare value abdegh, it expects cfi to have already
+ * This macro will only compare value abde, it expects cfi to have already
  * been checked
  */
 #define QTCHECK_MATRIX(m,a,b,d,e) ((m)[0] == (a << 16) && (m)[1] == (b << 16) && \
@@ -10761,6 +11523,14 @@ qtdemux_inspect_transformation_matrix (GstQTDemux * qtdemux,
       rotation_tag = "rotate-180";
     } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, 1, 0)) {
       rotation_tag = "rotate-270";
+    } else if (QTCHECK_MATRIX (matrix, G_MAXUINT16, 0, 0, 1)) {
+      rotation_tag = "flip-rotate-0";
+    } else if (QTCHECK_MATRIX (matrix, 0, G_MAXUINT16, G_MAXUINT16, 0)) {
+      rotation_tag = "flip-rotate-90";
+    } else if (QTCHECK_MATRIX (matrix, 1, 0, 0, G_MAXUINT16)) {
+      rotation_tag = "flip-rotate-180";
+    } else if (QTCHECK_MATRIX (matrix, 0, 1, 1, 0)) {
+      rotation_tag = "flip-rotate-270";
     } else {
       GST_FIXME_OBJECT (qtdemux, "Unhandled transformation matrix values");
     }
@@ -11047,7 +11817,7 @@ qtdemux_parse_stereo_svmi_atom (GstQTDemux * qtdemux, QtDemuxStream * stream,
  * traks that do not decode to something (like strm traks) will not have a pad.
  */
 static gboolean
-qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
+qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak, guint32 * mvhd_matrix)
 {
   GstByteReader tkhd;
   int offset;
@@ -11219,15 +11989,21 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
 
   /* parse rest of tkhd */
   if (stream->subtype == FOURCC_vide) {
+    guint32 tkhd_matrix[9];
     guint32 matrix[9];
 
     /* version 1 uses some 64-bit ints */
     if (!gst_byte_reader_skip (&tkhd, 20 + value_size))
       goto corrupt_file;
 
-    if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, matrix, "tkhd"))
+    if (!qtdemux_parse_transformation_matrix (qtdemux, &tkhd, tkhd_matrix,
+            "tkhd"))
       goto corrupt_file;
 
+    /* calculate the final matrix from the mvhd_matrix and the tkhd matrix */
+    qtdemux_mul_transformation_matrix (qtdemux, mvhd_matrix, tkhd_matrix,
+        matrix);
+
     if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
         || !gst_byte_reader_get_uint32_be (&tkhd, &h))
       goto corrupt_file;
@@ -11288,12 +12064,15 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
       if (stream->subtype != FOURCC_soun) {
         GST_ERROR_OBJECT (qtdemux,
             "Unexpeced stsd type 'aavd' outside 'soun' track");
+        goto corrupt_file;
       } else {
         /* encrypted audio with sound sample description v0 */
         GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
         stream->protected = TRUE;
-        if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc))
+        if (!qtdemux_parse_protection_aavd (qtdemux, stream, enc, &fourcc)) {
           GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
+          goto corrupt_file;
+        }
       }
     }
 
@@ -11302,8 +12081,10 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
        * with the same type */
       GNode *enc = qtdemux_tree_get_child_by_type (stsd, fourcc);
       stream->protected = TRUE;
-      if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc))
+      if (!qtdemux_parse_protection_scheme_info (qtdemux, stream, enc, &fourcc)) {
         GST_ERROR_OBJECT (qtdemux, "Failed to parse protection scheme info");
+        goto corrupt_file;
+      }
     }
 
     if (stream->subtype == FOURCC_vide) {
@@ -11539,7 +12320,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
             guint16 primaries = GST_READ_UINT16_BE (colr_data + 12);
             guint16 transfer_function = GST_READ_UINT16_BE (colr_data + 14);
             guint16 matrix = GST_READ_UINT16_BE (colr_data + 16);
-            gboolean full_range = len == 19 ? colr_data[17] >> 7 : FALSE;
+            gboolean full_range = len == 19 ? colr_data[18] >> 7 : FALSE;
 
             CUR_STREAM (stream)->colorimetry.primaries =
                 gst_video_color_primaries_from_iso (primaries);
@@ -11567,40 +12348,35 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           case FOURCC_avc1:
           case FOURCC_avc3:
           {
-            guint len = QT_UINT32 (stsd_entry_data);
+            guint32 len = QT_UINT32 (stsd_entry_data);
             len = len <= 0x56 ? 0 : len - 0x56;
             const guint8 *avc_data = stsd_entry_data + 0x56;
 
             /* find avcC */
-            while (len >= 0x8) {
-              guint size;
-
-              if (QT_UINT32 (avc_data) <= 0x8)
-                size = 0;
-              else if (QT_UINT32 (avc_data) <= len)
-                size = QT_UINT32 (avc_data) - 0x8;
-              else
-                size = len - 0x8;
+            while (len >= 8) {
+              guint32 size = QT_UINT32 (avc_data);
 
-              if (size < 1)
-                /* No real data, so break out */
+              if (size < 8 || size > len)
                 break;
 
-              switch (QT_FOURCC (avc_data + 0x4)) {
+              switch (QT_FOURCC (avc_data + 4)) {
                 case FOURCC_avcC:
                 {
                   /* parse, if found */
                   GstBuffer *buf;
 
+                  if (size < 8 + 1)
+                    break;
+
                   GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd");
 
                   /* First 4 bytes are the length of the atom, the next 4 bytes
                    * are the fourcc, the next 1 byte is the version, and the
                    * subsequent bytes are profile_tier_level structure like data. */
                   gst_codec_utils_h264_caps_set_level_and_profile (entry->caps,
-                      avc_data + 8 + 1, size - 1);
-                  buf = gst_buffer_new_and_alloc (size);
-                  gst_buffer_fill (buf, 0, avc_data + 0x8, size);
+                      avc_data + 8 + 1, size - 8 - 1);
+                  buf = gst_buffer_new_and_alloc (size - 8);
+                  gst_buffer_fill (buf, 0, avc_data + 8, size - 8);
                   gst_caps_set_simple (entry->caps,
                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
                   gst_buffer_unref (buf);
@@ -11611,6 +12387,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                 {
                   GstBuffer *buf;
 
+                  if (size < 8 + 40 + 1)
+                    break;
+
                   GST_DEBUG_OBJECT (qtdemux, "found strf codec_data in stsd");
 
                   /* First 4 bytes are the length of the atom, the next 4 bytes
@@ -11618,17 +12397,14 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                    * next 1 byte is the version, and the
                    * subsequent bytes are sequence parameter set like data. */
 
-                  size -= 40;   /* we'll be skipping BITMAPINFOHEADER */
-                  if (size > 1) {
-                    gst_codec_utils_h264_caps_set_level_and_profile
-                        (entry->caps, avc_data + 8 + 40 + 1, size - 1);
+                  gst_codec_utils_h264_caps_set_level_and_profile
+                      (entry->caps, avc_data + 8 + 40 + 1, size - 8 - 40 - 1);
 
-                    buf = gst_buffer_new_and_alloc (size);
-                    gst_buffer_fill (buf, 0, avc_data + 8 + 40, size);
-                    gst_caps_set_simple (entry->caps,
-                        "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                    gst_buffer_unref (buf);
-                  }
+                  buf = gst_buffer_new_and_alloc (size - 8 - 40);
+                  gst_buffer_fill (buf, 0, avc_data + 8 + 40, size - 8 - 40);
+                  gst_caps_set_simple (entry->caps,
+                      "codec_data", GST_TYPE_BUFFER, buf, NULL);
+                  gst_buffer_unref (buf);
                   break;
                 }
                 case FOURCC_btrt:
@@ -11636,11 +12412,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                   guint avg_bitrate, max_bitrate;
 
                   /* bufferSizeDB, maxBitrate and avgBitrate - 4 bytes each */
-                  if (size < 12)
+                  if (size < 8 + 12)
                     break;
 
-                  max_bitrate = QT_UINT32 (avc_data + 0xc);
-                  avg_bitrate = QT_UINT32 (avc_data + 0x10);
+                  max_bitrate = QT_UINT32 (avc_data + 8 + 4);
+                  avg_bitrate = QT_UINT32 (avc_data + 8 + 8);
 
                   if (!max_bitrate && !avg_bitrate)
                     break;
@@ -11672,8 +12448,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                   break;
               }
 
-              len -= size + 8;
-              avc_data += size + 8;
+              len -= size;
+              avc_data += size;
             }
 
             break;
@@ -11684,41 +12460,36 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           case FOURCC_dvh1:
           case FOURCC_dvhe:
           {
-            guint len = QT_UINT32 (stsd_entry_data);
+            guint32 len = QT_UINT32 (stsd_entry_data);
             len = len <= 0x56 ? 0 : len - 0x56;
             const guint8 *hevc_data = stsd_entry_data + 0x56;
 
             /* find hevc */
-            while (len >= 0x8) {
-              guint size;
-
-              if (QT_UINT32 (hevc_data) <= 0x8)
-                size = 0;
-              else if (QT_UINT32 (hevc_data) <= len)
-                size = QT_UINT32 (hevc_data) - 0x8;
-              else
-                size = len - 0x8;
+            while (len >= 8) {
+              guint32 size = QT_UINT32 (hevc_data);
 
-              if (size < 1)
-                /* No real data, so break out */
+              if (size < 8 || size > len)
                 break;
 
-              switch (QT_FOURCC (hevc_data + 0x4)) {
+              switch (QT_FOURCC (hevc_data + 4)) {
                 case FOURCC_hvcC:
                 {
                   /* parse, if found */
                   GstBuffer *buf;
 
+                  if (size < 8 + 1)
+                    break;
+
                   GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
 
                   /* First 4 bytes are the length of the atom, the next 4 bytes
                    * are the fourcc, the next 1 byte is the version, and the
                    * subsequent bytes are sequence parameter set like data. */
                   gst_codec_utils_h265_caps_set_level_tier_and_profile
-                      (entry->caps, hevc_data + 8 + 1, size - 1);
+                      (entry->caps, hevc_data + 8 + 1, size - 8 - 1);
 
-                  buf = gst_buffer_new_and_alloc (size);
-                  gst_buffer_fill (buf, 0, hevc_data + 0x8, size);
+                  buf = gst_buffer_new_and_alloc (size - 8);
+                  gst_buffer_fill (buf, 0, hevc_data + 8, size - 8);
                   gst_caps_set_simple (entry->caps,
                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
                   gst_buffer_unref (buf);
@@ -11727,8 +12498,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                 default:
                   break;
               }
-              len -= size + 8;
-              hevc_data += size + 8;
+              len -= size;
+              hevc_data += size;
             }
             break;
           }
@@ -12108,33 +12879,25 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           }
           case FOURCC_vc_1:
           {
-            guint len = QT_UINT32 (stsd_entry_data);
+            guint32 len = QT_UINT32 (stsd_entry_data);
             len = len <= 0x56 ? 0 : len - 0x56;
             const guint8 *vc1_data = stsd_entry_data + 0x56;
 
             /* find dvc1 */
             while (len >= 8) {
-              guint size;
-
-              if (QT_UINT32 (vc1_data) <= 8)
-                size = 0;
-              else if (QT_UINT32 (vc1_data) <= len)
-                size = QT_UINT32 (vc1_data) - 8;
-              else
-                size = len - 8;
+              guint32 size = QT_UINT32 (vc1_data);
 
-              if (size < 1)
-                /* No real data, so break out */
+              if (size < 8 || size > len)
                 break;
 
-              switch (QT_FOURCC (vc1_data + 0x4)) {
+              switch (QT_FOURCC (vc1_data + 4)) {
                 case GST_MAKE_FOURCC ('d', 'v', 'c', '1'):
                 {
                   GstBuffer *buf;
 
                   GST_DEBUG_OBJECT (qtdemux, "found dvc1 codec_data in stsd");
-                  buf = gst_buffer_new_and_alloc (size);
-                  gst_buffer_fill (buf, 0, vc1_data + 8, size);
+                  buf = gst_buffer_new_and_alloc (size - 8);
+                  gst_buffer_fill (buf, 0, vc1_data + 8, size - 8);
                   gst_caps_set_simple (entry->caps,
                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
                   gst_buffer_unref (buf);
@@ -12143,85 +12906,105 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                 default:
                   break;
               }
-              len -= size + 8;
-              vc1_data += size + 8;
+              len -= size;
+              vc1_data += size;
             }
             break;
           }
           case FOURCC_av01:
           {
-            guint len = QT_UINT32 (stsd_entry_data);
+            guint32 len = QT_UINT32 (stsd_entry_data);
             len = len <= 0x56 ? 0 : len - 0x56;
             const guint8 *av1_data = stsd_entry_data + 0x56;
 
             /* find av1C */
-            while (len >= 0x8) {
-              guint size;
-
-              if (QT_UINT32 (av1_data) <= 0x8)
-                size = 0;
-              else if (QT_UINT32 (av1_data) <= len)
-                size = QT_UINT32 (av1_data) - 0x8;
-              else
-                size = len - 0x8;
+            while (len >= 8) {
+              guint32 size = QT_UINT32 (av1_data);
 
-              if (size < 1)
-                /* No real data, so break out */
+              if (size < 8 || size > len)
                 break;
 
-              switch (QT_FOURCC (av1_data + 0x4)) {
+              switch (QT_FOURCC (av1_data + 4)) {
                 case FOURCC_av1C:
                 {
                   /* parse, if found */
                   GstBuffer *buf;
-                  guint8 pres_delay_field;
 
                   GST_DEBUG_OBJECT (qtdemux,
                       "found av1C codec_data in stsd of size %d", size);
 
                   /* not enough data, just ignore and hope for the best */
-                  if (size < 5)
+                  if (size < 8 + 4)
                     break;
 
                   /* Content is:
                    * 4 bytes: atom length
                    * 4 bytes: fourcc
-                   * 1 byte: version
-                   * 3 bytes: flags
-                   * 3 bits: reserved
-                   * 1 bits:  initial_presentation_delay_present
-                   * 4 bits: initial_presentation_delay (if present else reserved
+                   *
+                   * version 1 (marker=1):
+                   *
+                   *  unsigned int (1) marker = 1;
+                   *  unsigned int (7) version = 1;
+                   *  unsigned int (3) seq_profile;
+                   *  unsigned int (5) seq_level_idx_0;
+                   *  unsigned int (1) seq_tier_0;
+                   *  unsigned int (1) high_bitdepth;
+                   *  unsigned int (1) twelve_bit;
+                   *  unsigned int (1) monochrome;
+                   *  unsigned int (1) chroma_subsampling_x;
+                   *  unsigned int (1) chroma_subsampling_y;
+                   *  unsigned int (2) chroma_sample_position;
+                   *  unsigned int (3) reserved = 0;
+                   *
+                   *  unsigned int (1) initial_presentation_delay_present;
+                   *  if (initial_presentation_delay_present) {
+                   *    unsigned int (4) initial_presentation_delay_minus_one;
+                   *  } else {
+                   *    unsigned int (4) reserved = 0;
+                   *  }
+                   *
+                   *  unsigned int (8) configOBUs[];
+                   *
                    * rest: OBUs.
                    */
 
-                  if (av1_data[9] != 0) {
-                    GST_WARNING ("Unknown version %d of av1C box", av1_data[9]);
-                    break;
-                  }
+                  switch (av1_data[8]) {
+                    case 0x81:{
+                      guint8 pres_delay_field;
+
+                      /* We let profile and the other parts be figured out by
+                       * av1parse and only include the presentation delay here
+                       * if present */
+                      /* We skip initial_presentation_delay* for now */
+                      pres_delay_field = *(av1_data + 11);
+                      if (pres_delay_field & (1 << 5)) {
+                        gst_caps_set_simple (entry->caps,
+                            "presentation-delay", G_TYPE_INT,
+                            (gint) (pres_delay_field & 0x0F) + 1, NULL);
+                      }
 
-                  /* We skip initial_presentation_delay* for now */
-                  pres_delay_field = *(av1_data + 12);
-                  if (pres_delay_field & (1 << 5)) {
-                    gst_caps_set_simple (entry->caps,
-                        "presentation-delay", G_TYPE_INT,
-                        (gint) (pres_delay_field & 0x0F) + 1, NULL);
-                  }
-                  if (size > 5) {
-                    buf = gst_buffer_new_and_alloc (size - 5);
-                    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
-                    gst_buffer_fill (buf, 0, av1_data + 13, size - 5);
-                    gst_caps_set_simple (entry->caps,
-                        "codec_data", GST_TYPE_BUFFER, buf, NULL);
-                    gst_buffer_unref (buf);
+                      buf = gst_buffer_new_and_alloc (size - 8);
+                      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
+                      gst_buffer_fill (buf, 0, av1_data + 8, size - 8);
+                      gst_caps_set_simple (entry->caps,
+                          "codec_data", GST_TYPE_BUFFER, buf, NULL);
+                      gst_buffer_unref (buf);
+                      break;
+                    }
+                    default:
+                      GST_WARNING ("Unknown version 0x%02x of av1C box",
+                          av1_data[8]);
+                      break;
                   }
+
                   break;
                 }
                 default:
                   break;
               }
 
-              len -= size + 8;
-              av1_data += size + 8;
+              len -= size;
+              av1_data += size;
             }
 
             break;
@@ -12232,26 +13015,18 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
              * vp08, vp09, and vp10 fourcc. */
           case FOURCC_vp09:
           {
-            guint len = QT_UINT32 (stsd_entry_data);
+            guint32 len = QT_UINT32 (stsd_entry_data);
             len = len <= 0x56 ? 0 : len - 0x56;
             const guint8 *vpcc_data = stsd_entry_data + 0x56;
 
             /* find vpcC */
-            while (len >= 0x8) {
-              guint size;
-
-              if (QT_UINT32 (vpcc_data) <= 0x8)
-                size = 0;
-              else if (QT_UINT32 (vpcc_data) <= len)
-                size = QT_UINT32 (vpcc_data) - 0x8;
-              else
-                size = len - 0x8;
+            while (len >= 8) {
+              guint32 size = QT_UINT32 (vpcc_data);
 
-              if (size < 1)
-                /* No real data, so break out */
+              if (size < 8 || size > len)
                 break;
 
-              switch (QT_FOURCC (vpcc_data + 0x4)) {
+              switch (QT_FOURCC (vpcc_data + 4)) {
                 case FOURCC_vpcC:
                 {
                   const gchar *profile_str = NULL;
@@ -12267,7 +13042,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
 
                   /* the meaning of "size" is length of the atom body, excluding
                    * atom length and fourcc fields */
-                  if (size < 12)
+                  if (size < 8 + 12)
                     break;
 
                   /* Content is:
@@ -12373,8 +13148,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                   break;
               }
 
-              len -= size + 8;
-              vpcc_data += size + 8;
+              len -= size;
+              vpcc_data += size;
             }
 
             break;
@@ -12589,6 +13364,16 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           entry->bytes_per_packet = entry->bytes_per_sample;
           break;
         }
+
+          /* According to TS 102 366, the channel count in
+           * a (E)AC3SampleEntry box is to be ignored */
+        case 0x20736d:
+        case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
+        case GST_MAKE_FOURCC ('s', 'a', 'c', '3'):     // Nero Recode
+        case FOURCC_ac_3:
+          entry->n_channels = 0;
+          break;
+
         default:
           break;
       }
@@ -12705,7 +13490,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         }
         case FOURCC_wma_:
         {
-          guint len = QT_UINT32 (stsd_entry_data);
+          guint32 len = QT_UINT32 (stsd_entry_data);
           len = len <= offset ? 0 : len - offset;
           const guint8 *wfex_data = stsd_entry_data + offset;
           const gchar *codec_name = NULL;
@@ -12730,17 +13515,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
 
           /* find wfex */
           while (len >= 8) {
-            guint size;
+            guint32 size = QT_UINT32 (wfex_data);
 
-            if (QT_UINT32 (wfex_data) <= 0x8)
-              size = 0;
-            else if (QT_UINT32 (wfex_data) <= len)
-              size = QT_UINT32 (wfex_data) - 8;
-            else
-              size = len - 8;
-
-            if (size < 1)
-              /* No real data, so break out */
+            if (size < 8 || size > len)
               break;
 
             switch (QT_FOURCC (wfex_data + 4)) {
@@ -12786,12 +13563,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
                     "width", G_TYPE_INT, wfex.wBitsPerSample,
                     "depth", G_TYPE_INT, wfex.wBitsPerSample, NULL);
 
-                if (size > wfex.cbSize) {
+                if (size > 8 + wfex.cbSize) {
                   GstBuffer *buf;
 
-                  buf = gst_buffer_new_and_alloc (size - wfex.cbSize);
+                  buf = gst_buffer_new_and_alloc (size - 8 - wfex.cbSize);
                   gst_buffer_fill (buf, 0, wfex_data + 8 + wfex.cbSize,
-                      size - wfex.cbSize);
+                      size - 8 - wfex.cbSize);
                   gst_caps_set_simple (entry->caps,
                       "codec_data", GST_TYPE_BUFFER, buf, NULL);
                   gst_buffer_unref (buf);
@@ -12808,44 +13585,108 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
               default:
                 break;
             }
-            len -= size + 8;
-            wfex_data += size + 8;
+            len -= size;
+            wfex_data += size;
           }
           break;
         }
         case FOURCC_opus:
         {
-          const guint8 *dops_data;
           guint8 *channel_mapping = NULL;
-          guint32 rate;
-          guint8 channels;
+          guint32 dops_len, rate;
+          guint8 n_channels;
           guint8 channel_mapping_family;
           guint8 stream_count;
           guint8 coupled_count;
           guint8 i;
 
-          version = GST_READ_UINT16_BE (stsd_entry_data + 16);
-          if (version == 1)
-            dops_data = stsd_entry_data + 51;
-          else
-            dops_data = stsd_entry_data + 35;
-
-          channels = GST_READ_UINT8 (dops_data + 10);
-          rate = GST_READ_UINT32_LE (dops_data + 13);
-          channel_mapping_family = GST_READ_UINT8 (dops_data + 19);
-          stream_count = GST_READ_UINT8 (dops_data + 20);
-          coupled_count = GST_READ_UINT8 (dops_data + 21);
-
-          if (channels > 0) {
-            channel_mapping = g_malloc (channels * sizeof (guint8));
-            for (i = 0; i < channels; i++)
-              channel_mapping[i] = GST_READ_UINT8 (dops_data + i + 22);
+          GNode *opus;
+          GNode *dops;
+
+          opus = qtdemux_tree_get_child_by_type (stsd, FOURCC_opus);
+          if (opus == NULL) {
+            GST_WARNING_OBJECT (qtdemux, "Opus Sample Entry not found");
+            goto corrupt_file;
+          }
+
+          dops = qtdemux_tree_get_child_by_type (opus, FOURCC_dops);
+          if (dops == NULL) {
+            GST_WARNING_OBJECT (qtdemux, "Opus Specific Box not found");
+            goto corrupt_file;
           }
 
-          entry->caps = gst_codec_utils_opus_create_caps (rate, channels,
+          /* Opus Specific Box content:
+           * 4 bytes: length
+           * 4 bytes: "dOps"
+           * 1 byte: Version;
+           * 1 byte: OutputChannelCount;
+           * 2 bytes: PreSkip (big-endians);
+           * 4 bytes: InputSampleRate (big-endians);
+           * 2 bytes: OutputGain (big-endians);
+           * 1 byte: ChannelMappingFamily;
+           * if (ChannelMappingFamily != 0) {
+           *   1 byte: StreamCount;
+           *   1 byte: CoupledCount;
+           *   for (OutputChannel in 0..OutputChannelCount) {
+           *     1 byte: ChannelMapping;
+           *   }
+           * }
+           */
+
+          dops_len = QT_UINT32 ((guint8 *) dops->data);
+          if (len < offset + dops_len) {
+            GST_WARNING_OBJECT (qtdemux,
+                "Opus Sample Entry has bogus size %" G_GUINT32_FORMAT, len);
+            goto corrupt_file;
+          }
+          if (dops_len < 19) {
+            GST_WARNING_OBJECT (qtdemux,
+                "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
+                dops_len);
+            goto corrupt_file;
+          }
+
+          n_channels = GST_READ_UINT8 ((guint8 *) dops->data + 9);
+          rate = GST_READ_UINT32_BE ((guint8 *) dops->data + 12);
+          channel_mapping_family = GST_READ_UINT8 ((guint8 *) dops->data + 18);
+
+          if (channel_mapping_family != 0) {
+            if (dops_len < 21 + n_channels) {
+              GST_WARNING_OBJECT (qtdemux,
+                  "Opus Specific Box has bogus size %" G_GUINT32_FORMAT,
+                  dops_len);
+              goto corrupt_file;
+            }
+
+            stream_count = GST_READ_UINT8 ((guint8 *) dops->data + 19);
+            coupled_count = GST_READ_UINT8 ((guint8 *) dops->data + 20);
+
+            if (n_channels > 0) {
+              channel_mapping = g_malloc (n_channels * sizeof (guint8));
+              for (i = 0; i < n_channels; i++)
+                channel_mapping[i] =
+                    GST_READ_UINT8 ((guint8 *) dops->data + i + 21);
+            }
+          } else if (n_channels == 1) {
+            stream_count = 1;
+            coupled_count = 0;
+          } else if (n_channels == 2) {
+            stream_count = 1;
+            coupled_count = 1;
+          } else {
+            GST_WARNING_OBJECT (qtdemux,
+                "Opus unexpected nb of channels %d without channel mapping",
+                n_channels);
+            goto corrupt_file;
+          }
+
+          entry->caps = gst_codec_utils_opus_create_caps (rate, n_channels,
               channel_mapping_family, stream_count, coupled_count,
               channel_mapping);
           g_free (channel_mapping);
+
+          entry->sampled = TRUE;
+
           break;
         }
         default:
@@ -12907,47 +13748,53 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         } else {
           guint32 datalen = QT_UINT32 (stsd_entry_data + offset + 16);
           const guint8 *data = stsd_entry_data + offset + 16;
-          GNode *wavenode;
-          GNode *waveheadernode;
-
-          wavenode = g_node_new ((guint8 *) data);
-          if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
-            const guint8 *waveheader;
-            guint32 headerlen;
-
-            waveheadernode = qtdemux_tree_get_child_by_type (wavenode, fourcc);
-            if (waveheadernode) {
-              waveheader = (const guint8 *) waveheadernode->data;
-              headerlen = QT_UINT32 (waveheader);
-
-              if (headerlen > 8) {
-                gst_riff_strf_auds *header = NULL;
-                GstBuffer *headerbuf;
-                GstBuffer *extra;
-
-                waveheader += 8;
-                headerlen -= 8;
-
-                headerbuf = gst_buffer_new_and_alloc (headerlen);
-                gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
-
-                if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
-                        headerbuf, &header, &extra)) {
-                  gst_caps_unref (entry->caps);
-                  /* FIXME: Need to do something with the channel reorder map */
-                  entry->caps =
-                      gst_riff_create_audio_caps (header->format, NULL, header,
-                      extra, NULL, NULL, NULL);
-
-                  if (extra)
-                    gst_buffer_unref (extra);
-                  g_free (header);
+
+          if (len < datalen || len - datalen < offset + 16) {
+            GST_WARNING_OBJECT (qtdemux, "Not enough data for waveheadernode");
+          } else {
+            GNode *wavenode;
+            GNode *waveheadernode;
+
+            wavenode = g_node_new ((guint8 *) data);
+            if (qtdemux_parse_node (qtdemux, wavenode, data, datalen)) {
+              const guint8 *waveheader;
+              guint32 headerlen;
+
+              waveheadernode =
+                  qtdemux_tree_get_child_by_type (wavenode, fourcc);
+              if (waveheadernode) {
+                waveheader = (const guint8 *) waveheadernode->data;
+                headerlen = QT_UINT32 (waveheader);
+
+                if (headerlen > 8) {
+                  gst_riff_strf_auds *header = NULL;
+                  GstBuffer *headerbuf;
+                  GstBuffer *extra;
+
+                  waveheader += 8;
+                  headerlen -= 8;
+
+                  headerbuf = gst_buffer_new_and_alloc (headerlen);
+                  gst_buffer_fill (headerbuf, 0, waveheader, headerlen);
+
+                  if (gst_riff_parse_strf_auds (GST_ELEMENT_CAST (qtdemux),
+                          headerbuf, &header, &extra)) {
+                    gst_caps_unref (entry->caps);
+                    /* FIXME: Need to do something with the channel reorder map */
+                    entry->caps =
+                        gst_riff_create_audio_caps (header->format, NULL,
+                        header, extra, NULL, NULL, NULL);
+
+                    if (extra)
+                      gst_buffer_unref (extra);
+                    g_free (header);
+                  }
                 }
-              }
-            } else
-              GST_DEBUG ("Didn't find waveheadernode for this codec");
+              } else
+                GST_DEBUG ("Didn't find waveheadernode for this codec");
+            }
+            g_node_destroy (wavenode);
           }
-          g_node_destroy (wavenode);
         }
       } else if (esds) {
         gst_qtdemux_handle_esds (qtdemux, stream, entry, esds,
@@ -13198,6 +14045,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
             }
             break;
           }
+          case FOURCC_opus:
           case FOURCC_lpcm:
           case FOURCC_in24:
           case FOURCC_in32:
@@ -13325,7 +14173,33 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
 
     stsd_entry_data += len;
     remaining_stsd_len -= len;
+  }
+
+  /* Sample grouping support */
+  if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
+          || stream->protection_scheme_type == FOURCC_cbcs)) {
+    QtDemuxCencSampleSetInfo *info = stream->protection_scheme_info;
+    GNode *sgpd_node;
+    GstByteReader sgpd_data;
 
+    if (!info)
+      goto corrupt_file;
+
+    if (info->track_group_properties) {
+      g_ptr_array_free (info->fragment_group_properties, TRUE);
+      info->fragment_group_properties = NULL;
+    }
+
+    sgpd_node = qtdemux_tree_get_child_by_type_full (stbl, FOURCC_sgpd,
+        &sgpd_data);
+    while (sgpd_node) {
+      if (qtdemux_parse_sgpd (qtdemux, stream, &sgpd_data, FOURCC_seig,
+              &info->track_group_properties)) {
+        break;
+      }
+      sgpd_node = qtdemux_tree_get_sibling_by_type_full (sgpd_node,
+          FOURCC_sgpd, &sgpd_data);
+    }
   }
 
   /* collect sample information */
@@ -13367,6 +14241,18 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
   }
 
+  /* https://dev.w3.org/html5/html-sourcing-inband-tracks/#mpeg4
+   * FIXME: For CEA 608 and CEA 708 we should use the channel_number and
+   * service_number respectively.
+   */
+  if (stream->track_id) {
+    gchar *track_id_str =
+        g_strdup_printf ("%" G_GUINT32_FORMAT, stream->track_id);
+    gst_tag_list_add (stream->stream_tags, GST_TAG_MERGE_REPLACE,
+        GST_TAG_CONTAINER_SPECIFIC_TRACK_ID, track_id_str, NULL);
+    g_free (track_id_str);
+  }
+
   /* Check for UDTA tags */
   if ((udta = qtdemux_tree_get_child_by_type (trak, FOURCC_udta))) {
     qtdemux_parse_udta (qtdemux, stream->stream_tags, udta);
@@ -13576,12 +14462,16 @@ qtdemux_prepare_streams (GstQTDemux * qtdemux)
 
     /* parse the initial sample for use in setting the frame rate cap */
     while (sample_num == 0 && sample_num < stream->n_samples) {
-      if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
+      if (!qtdemux_parse_samples (qtdemux, stream, sample_num)) {
+        ret = GST_FLOW_ERROR;
         break;
+      }
       ++sample_num;
     }
   }
 
+  qtdemux_check_if_is_gapless_audio (qtdemux);
+
   return ret;
 }
 
@@ -13723,8 +14613,6 @@ qtdemux_expose_streams (GstQTDemux * qtdemux)
 
   gst_qtdemux_guess_bitrate (qtdemux);
 
-  gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
-
   /* If we have still old_streams, it's no more used stream */
   for (i = 0; i < qtdemux->old_streams->len; i++) {
     QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
@@ -13742,6 +14630,8 @@ qtdemux_expose_streams (GstQTDemux * qtdemux)
 
   g_ptr_array_set_size (qtdemux->old_streams, 0);
 
+  gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
+
   /* check if we should post a redirect in case there is a single trak
    * and it is a redirecting trak */
   if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
@@ -13996,11 +14886,14 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
   guint64 creation_time;
   GstDateTime *datetime = NULL;
   gint version;
+  GstByteReader mvhd_reader;
+  guint32 matrix[9];
 
   /* make sure we have a usable taglist */
   qtdemux->tag_list = gst_tag_list_make_writable (qtdemux->tag_list);
 
-  mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
+  mvhd = qtdemux_tree_get_child_by_type_full (qtdemux->moov_node,
+      FOURCC_mvhd, &mvhd_reader);
   if (mvhd == NULL) {
     GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects.");
     return qtdemux_parse_redirects (qtdemux);
@@ -14011,15 +14904,26 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
     creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
     qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
     qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
+    if (!gst_byte_reader_skip (&mvhd_reader, 4 + 8 + 8 + 4 + 8))
+      return FALSE;
   } else if (version == 0) {
     creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
     qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
     qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
+    if (!gst_byte_reader_skip (&mvhd_reader, 4 + 4 + 4 + 4 + 4))
+      return FALSE;
   } else {
     GST_WARNING_OBJECT (qtdemux, "Unhandled mvhd version %d", version);
     return FALSE;
   }
 
+  if (!gst_byte_reader_skip (&mvhd_reader, 4 + 2 + 2 + 2 * 4))
+    return FALSE;
+
+  if (!qtdemux_parse_transformation_matrix (qtdemux, &mvhd_reader, matrix,
+          "mvhd"))
+    return FALSE;
+
   /* Moving qt creation time (secs since 1904) to unix time */
   if (creation_time != 0) {
     /* Try to use epoch first as it should be faster and more commonly found */
@@ -14088,7 +14992,7 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
   /* parse all traks */
   trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
   while (trak) {
-    qtdemux_parse_trak (qtdemux, trak);
+    qtdemux_parse_trak (qtdemux, trak, matrix);
     /* iterate all siblings */
     trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
   }
@@ -15021,6 +15925,25 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
           "stream-format", G_TYPE_STRING, "obu-stream",
           "alignment", G_TYPE_STRING, "tu", NULL);
       break;
+    case FOURCC_SHQ0:
+    case FOURCC_SHQ1:
+    case FOURCC_SHQ2:
+    case FOURCC_SHQ3:
+    case FOURCC_SHQ4:
+    case FOURCC_SHQ5:
+    case FOURCC_SHQ6:
+    case FOURCC_SHQ7:
+    case FOURCC_SHQ8:
+    case FOURCC_SHQ9:{
+      gchar *format =
+          g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+      _codec ("SpeedHQ");
+      caps =
+          gst_caps_new_simple ("video/x-speedhq", "variant", G_TYPE_STRING,
+          format, NULL);
+      g_free (format);
+      break;
+    }
     case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
     default:
     {
@@ -15393,7 +16316,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
   if (g_str_has_prefix (name, "audio/x-raw")) {
     stream->need_clip = TRUE;
     stream->min_buffer_size = 1024 * entry->bytes_per_frame;
-    stream->max_buffer_size = 4096 * entry->bytes_per_frame;
+    stream->max_buffer_size = entry->rate * entry->bytes_per_frame;
     GST_DEBUG ("setting min/max buffer sizes to %d/%d", stream->min_buffer_size,
         stream->max_buffer_size);
   }
diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h
index ad150727c045fa13500d5a38cde5d2d9daf4e0c2..6e7f64b91c5baeb02bd69f06f989ca523e914dcd 100644
--- a/gst/isomp4/qtdemux.h
+++ b/gst/isomp4/qtdemux.h
@@ -54,6 +54,7 @@ typedef struct _QtDemuxSample QtDemuxSample;
 typedef struct _QtDemuxSegment QtDemuxSegment;
 typedef struct _QtDemuxRandomAccessEntry QtDemuxRandomAccessEntry;
 typedef struct _QtDemuxStreamStsdEntry QtDemuxStreamStsdEntry;
+typedef struct _QtDemuxGaplessAudioInfo QtDemuxGaplessAudioInfo;
 
 typedef GstBuffer * (*QtDemuxProcessFunc)(GstQTDemux * qtdemux, QtDemuxStream * stream, GstBuffer * buf);
 
@@ -65,6 +66,49 @@ enum QtDemuxState
   QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
 };
 
+typedef enum {
+  /* Regular behaviour */
+  VARIANT_NONE,
+
+  /* We're working with a MediaSource Extensions ISO BMFF Bytestream. */
+  VARIANT_MSE_BYTESTREAM,
+
+  /* We're working with a smoothstreaming fragment.
+   * Mss doesn't have 'moov' or any information about the streams format,
+   * requiring qtdemux to expose and create the streams */
+  VARIANT_MSS_FRAGMENTED,
+} Variant;
+
+typedef enum {
+  /* No valid gapless audio info present. Types other than this one
+   * are used only if all of these apply:
+   *
+   * 1. There is embedded gapless audio information available
+   * 2. Only one stream exists
+   * 3. Said stream has only one segment
+   * 4. Said stream is an audio stream
+   */
+  GAPLESS_AUDIO_INFO_TYPE_NONE,
+  /* Using information from the iTunes iTunSMPB revdns tag. */
+  GAPLESS_AUDIO_INFO_TYPE_ITUNES,
+  /* Using known Nero encoder delay information. */
+  GAPLESS_AUDIO_INFO_TYPE_NERO
+} QtDemuxGaplessAudioInfoType;
+
+/* Gapless audio information, only used for single-stream audio-only media. */
+struct _QtDemuxGaplessAudioInfo {
+  QtDemuxGaplessAudioInfoType type;
+
+  guint64 num_start_padding_pcm_frames;
+  guint64 num_end_padding_pcm_frames;
+  guint64 num_valid_pcm_frames;
+
+  /* PCM frame amounts converted to nanoseconds. */
+  GstClockTime start_padding_duration;
+  GstClockTime end_padding_duration;
+  GstClockTime valid_duration;
+};
+
 struct _GstQTDemux {
   GstElement element;
 
@@ -142,10 +186,7 @@ struct _GstQTDemux {
 
   guint32 segment_seqnum;
 
-  /* flag to indicate that we're working with a smoothstreaming fragment
-   * Mss doesn't have 'moov' or any information about the streams format,
-   * requiring qtdemux to expose and create the streams */
-  gboolean mss_mode;
+  Variant variant;
 
   /* Set to TRUE if the incoming stream is either a MSS stream or
    * a Fragmented MP4 (containing the [mvex] atom in the header) */
@@ -167,6 +208,8 @@ struct _GstQTDemux {
 
   gint64 chapters_track_id;
 
+  QtDemuxGaplessAudioInfo gapless_audio_info;
+
   /* protection support */
   GPtrArray *protection_system_ids; /* Holds identifiers of all content protection systems for all tracks */
   GQueue protection_event_queue; /* holds copy of upstream protection events */
diff --git a/gst/isomp4/qtdemux_dump.c b/gst/isomp4/qtdemux_dump.c
index 45296c266e64dceeef8dc2a5e75e9966c1845b18..297b580ef0389ebd96349949c093b34806097959 100644
--- a/gst/isomp4/qtdemux_dump.c
+++ b/gst/isomp4/qtdemux_dump.c
@@ -836,6 +836,11 @@ qtdemux_dump_trun (GstQTDemux * qtdemux, GstByteReader * data, int depth)
     GST_LOG ("%*s    first-sample-flags: %u", depth, "", first_sample_flags);
   }
 
+  /* Nothing to print below */
+  if ((flags & (TR_SAMPLE_DURATION | TR_SAMPLE_SIZE | TR_SAMPLE_FLAGS |
+              TR_COMPOSITION_TIME_OFFSETS)) == 0)
+    return TRUE;
+
   for (i = 0; i < samples_count; i++) {
     if (flags & TR_SAMPLE_DURATION) {
       if (!gst_byte_reader_get_uint32_be (data, &sample_duration))
@@ -1053,6 +1058,88 @@ qtdemux_dump_fLaC (GstQTDemux * qtdemux, GstByteReader * data, int depth)
   return TRUE;
 }
 
+gboolean
+qtdemux_dump_opus (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint16 version, data_ref_id, n_channels, sample_size;
+  guint32 sample_rate;
+
+  if (!gst_byte_reader_skip (data, 6) ||
+      !gst_byte_reader_get_uint16_be (data, &data_ref_id) ||
+      !gst_byte_reader_get_uint16_be (data, &version) ||
+      !gst_byte_reader_skip (data, 6) ||
+      !gst_byte_reader_get_uint16_be (data, &n_channels) ||
+      !gst_byte_reader_get_uint16_be (data, &sample_size) ||
+      !gst_byte_reader_skip (data, 4) ||
+      !gst_byte_reader_get_uint32_be (data, &sample_rate))
+    return FALSE;
+
+  GST_LOG ("%*s  data reference: %d", depth, "", data_ref_id);
+  GST_LOG ("%*s  version:        %d", depth, "", version);
+  GST_LOG ("%*s  channel count:  %d", depth, "", n_channels);
+  GST_LOG ("%*s  sample size:    %d", depth, "", sample_size);
+  GST_LOG ("%*s  sample rate:    %d", depth, "", sample_rate >> 16);
+
+  return TRUE;
+}
+
+gboolean
+qtdemux_dump_dops (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint8 version, n_channels, channel_mapping_family;
+  guint8 stream_count = 1, coupled_count = 0, i = 0;
+  guint8 *channel_mapping = NULL;
+  guint16 pre_skip, output_gain;
+  guint32 sample_rate;
+
+  if (!gst_byte_reader_get_uint8 (data, &version) ||
+      !gst_byte_reader_get_uint8 (data, &n_channels) ||
+      !gst_byte_reader_get_uint16_be (data, &pre_skip) ||
+      !gst_byte_reader_get_uint32_be (data, &sample_rate) ||
+      !gst_byte_reader_get_uint16_be (data, &output_gain) ||
+      !gst_byte_reader_get_uint8 (data, &channel_mapping_family))
+    return FALSE;
+
+  if (channel_mapping_family != 0) {
+    if (!gst_byte_reader_get_uint8 (data, &stream_count) ||
+        !gst_byte_reader_get_uint8 (data, &coupled_count))
+      return FALSE;
+
+    if (n_channels > 0) {
+      channel_mapping = g_malloc (n_channels * sizeof (guint8));
+
+      for (i = 0; i < n_channels; i++)
+        if (!gst_byte_reader_get_uint8 (data, &channel_mapping[i])) {
+          g_free (channel_mapping);
+          return FALSE;
+        }
+    }
+  }
+
+  GST_LOG ("%*s  version:                %d", depth, "", version);
+  GST_LOG ("%*s  channel count:          %d", depth, "", n_channels);
+  GST_LOG ("%*s  pre skip:               %d", depth, "", pre_skip);
+  GST_LOG ("%*s  sample rate:            %d", depth, "", sample_rate);
+  GST_LOG ("%*s  output gain:            %d", depth, "", output_gain);
+  GST_LOG ("%*s  channel mapping family: %d", depth, "",
+      channel_mapping_family);
+
+  if (channel_mapping_family != 0) {
+    GST_LOG ("%*s  stream count:           %d", depth, "", stream_count);
+    GST_LOG ("%*s  coupled count:          %d", depth, "", coupled_count);
+
+    if (n_channels > 0) {
+      for (i = 0; i < n_channels; i++)
+        GST_LOG ("%*s  channel mapping: %d -> %d", depth, "", i,
+            channel_mapping[i]);
+
+      g_free (channel_mapping);
+    }
+  }
+
+  return TRUE;
+}
+
 gboolean
 qtdemux_dump_gmin (GstQTDemux * qtdemux, GstByteReader * data, int depth)
 {
diff --git a/gst/isomp4/qtdemux_dump.h b/gst/isomp4/qtdemux_dump.h
index 45dcd3f0819a40dfb9c60ff5c42f263a54b8fff9..de58fd48fb3402a452a96c30036962050da5a5fe 100644
--- a/gst/isomp4/qtdemux_dump.h
+++ b/gst/isomp4/qtdemux_dump.h
@@ -89,6 +89,10 @@ gboolean qtdemux_dump_dfLa (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
 gboolean qtdemux_dump_fLaC (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
+gboolean qtdemux_dump_opus (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_dops (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
 gboolean qtdemux_dump_gmin (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
 
diff --git a/gst/isomp4/qtdemux_tags.c b/gst/isomp4/qtdemux_tags.c
index f2b384d69272f6794505ff4ef23bb0d0160d6a78..7500748c7265c10e9cdadfac823a4c1dca4c95a0 100644
--- a/gst/isomp4/qtdemux_tags.c
+++ b/gst/isomp4/qtdemux_tags.c
@@ -40,8 +40,11 @@
 #include "qtdemux_tags.h"
 #include "qtdemux_tree.h"
 #include "qtdemux_types.h"
+#include "qtdemux_debug.h"
 #include "fourcc.h"
 
+#define GST_CAT_DEFAULT qtdemux_debug
+
 static GstBuffer *
 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
 {
@@ -716,14 +719,14 @@ qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
       const gchar tag[28];
     } tags[] = {
       {
-      "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
-      "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
-      "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
-      "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
-      "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
-      "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
-      "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
-      "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
+          "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
+          "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
+          "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
+          "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
+          "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
+          "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
+          "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
+          "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
     };
     int i;
 
@@ -744,12 +747,111 @@ qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
         break;
       }
     }
-    if (i == G_N_ELEMENTS (tags))
-      goto unknown_tag;
+
+    /* Some tags might not actually be used for metadata about the media,
+     * but for other purposes. One such tag is iTunSMPB, which contains
+     * padding information for gapless playback. Scan these separately. */
+    if (i == G_N_ELEMENTS (tags)) {
+      if (!g_ascii_strncasecmp ("iTunSMPB", namestr, 8)) {
+        /* iTunSMPB tag format goes as follows:
+         *
+         * " 00000000 xxxxxxxx yyyyyyyy zzzzzzzzzzzzzzzz 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000"
+         *
+         * The data is actually an ASCII string containing these hex fields.
+         * The description above is _not_ a description of a binary format!
+         * These need to be parsed with g_ascii_strtoull() and base 16.
+         *
+         * (The quotes are not part of it; they just emphasize the
+         * whitespace at the beginning of the string).
+         *
+         * Only the fields marked with x/y/z are of interest here.
+         *
+         * The x field is the priming, in samples.
+         * These are the padding samples at the beginning of the stream.
+         *
+         * The y field is the remainder, in samples.
+         * These are the padding samples at the end of the stream.
+         *
+         * The z field is the number of valid PCM frames, excluding the
+         * priming and remainder. (In other words, the number of PCM
+         * frames that make up the actual audio, without the padding.)
+         *
+         * The data starts at offset 16. All access to it must therefore skip
+         * the first 16 bytes.
+         */
+
+        const gsize start_offset = 16;
+        const gsize priming_offset = start_offset + 10;
+        const gsize remainder_offset = start_offset + 19;
+        const gsize num_valid_pcm_frames_offset = start_offset + 28;
+        const gsize total_length = 44;
+        const gchar *str;
+        guint64 priming;
+        guint64 remainder;
+        guint64 num_valid_pcm_frames;
+        /* Temporary buffer for g_ascii_strtoull() calls.
+         * Add extra +1 space for nullbyte. */
+        gchar tmp[16 + 1];
+
+        /* Use the iTunSMPB info if no other info has been found yet. */
+        if (demux->gapless_audio_info.type != GAPLESS_AUDIO_INFO_TYPE_NONE) {
+          GST_DEBUG_OBJECT (demux, "iTunSMPB information found, "
+              "but other gapless audio info was already read");
+          goto finish;
+        }
+
+        if (G_UNLIKELY (datasize < (start_offset + total_length))) {
+          GST_WARNING_OBJECT (demux,
+              "iTunSMPB tag data size too small - not parsing");
+          goto finish;
+        }
+
+        str = (gchar *) ((guint8 *) data->data);
+
+#define PARSE_ITUNSMPB_FIELD(FIELD_NAME, NUM_DIGITS) \
+        G_STMT_START \
+        { \
+          gint str_idx; \
+\
+          for (str_idx = 0; str_idx < (NUM_DIGITS); ++str_idx) { \
+            gchar ch = str[FIELD_NAME ## _offset + str_idx]; \
+            if (!g_ascii_isxdigit (ch)) { \
+              GST_WARNING_OBJECT (demux, #FIELD_NAME " field in iTunSMPB " \
+                  "tag data has invalid character '%c'", ch); \
+              goto finish; \
+            } \
+            tmp[str_idx] = ch; \
+          } \
+          tmp[NUM_DIGITS] = 0; \
+\
+          FIELD_NAME = g_ascii_strtoull (tmp, NULL, 16); \
+        } \
+        G_STMT_END
+
+        PARSE_ITUNSMPB_FIELD (priming, 8);
+        PARSE_ITUNSMPB_FIELD (remainder, 8);
+        PARSE_ITUNSMPB_FIELD (num_valid_pcm_frames, 16);
+
+#undef PARSE_ITUNSMPB_FIELD
+
+        GST_DEBUG_OBJECT (demux, "iTunSMPB information: priming %"
+            G_GUINT64_FORMAT " remainder %" G_GUINT64_FORMAT
+            " num valid PCM frames %" G_GUINT64_FORMAT, priming, remainder,
+            num_valid_pcm_frames);
+
+        demux->gapless_audio_info.type = GAPLESS_AUDIO_INFO_TYPE_ITUNES;
+        demux->gapless_audio_info.num_start_padding_pcm_frames = priming;
+        demux->gapless_audio_info.num_end_padding_pcm_frames = remainder;
+        demux->gapless_audio_info.num_valid_pcm_frames = num_valid_pcm_frames;
+      } else {
+        goto unknown_tag;
+      }
+    }
   } else {
     goto unknown_tag;
   }
 
+finish:
   return;
 
 /* errors */
@@ -763,7 +865,8 @@ unknown_tag:
     namestr_dbg = g_strndup (namestr, namesize);
 
     GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
-        "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
+        "file a bug at %s", meanstr_dbg, namestr_dbg, datatype,
+        PACKAGE_BUGREPORT);
 
     g_free (namestr_dbg);
     g_free (meanstr_dbg);
@@ -823,63 +926,64 @@ static const struct
   const GstQTDemuxAddTagFunc func;
 } add_funcs[] = {
   {
-  FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
-  FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
-  FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
-  FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
-  FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
-  FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
-  FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
-  FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
-  FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
-  FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
-  FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
-  FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
-  FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
-  FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
-  FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
-  FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
-  FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
-  FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
-  FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
-  FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
-  FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
-  FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
-  FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
-        qtdemux_tag_add_num}, {
-  FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
-        qtdemux_tag_add_num}, {
-  FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
-  FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
-  FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
-  FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
-  FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
-  FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
-  FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
-  FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
-  FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
-  FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
-  FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
-  FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
-        qtdemux_tag_add_classification}, {
-  FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
-  FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
-  FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
-
-    /* This is a special case, some tags are stored in this
-     * 'reverse dns naming', according to:
-     * http://atomicparsley.sourceforge.net/mpeg-4files.html and
-     * bug #614471
-     */
-  FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
-    /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
-  FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
+      FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
+      FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
+      FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
+      FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
+      FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
+      FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
+      FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
+      FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
+      FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
+      FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
+      FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
+      FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
+      FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
+      FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
+      FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
+      FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
+      FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
+      FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
+      FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
+      FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
+      FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
+        FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT,
+      qtdemux_tag_add_num}, {
+        FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
+      qtdemux_tag_add_num}, {
+        FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
+      qtdemux_tag_add_num}, {
+      FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
+      FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
+      FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
+      FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
+      FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
+      FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
+      FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
+      FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
+      FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
+      FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
+      FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
+        FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
+      qtdemux_tag_add_classification}, {
+      FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
+      FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
+      FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
+
+        /* This is a special case, some tags are stored in this
+         * 'reverse dns naming', according to:
+         * http://atomicparsley.sourceforge.net/mpeg-4files.html and
+         * bug #614471
+         */
+      FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
+        /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
+      FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
 };
 
 struct _GstQtDemuxTagList
diff --git a/gst/isomp4/qtdemux_types.c b/gst/isomp4/qtdemux_types.c
index 5e6d735c96d6f346fd7e2cae857cc700afc1adc6..f6dc28f36fecc954d481cd3abe73dbb81b17c545 100644
--- a/gst/isomp4/qtdemux_types.c
+++ b/gst/isomp4/qtdemux_types.c
@@ -97,6 +97,8 @@ static const QtNodeType qt_node_types[] = {
   {FOURCC_alac, "alac", 0,},
   {FOURCC_fLaC, "fLaC", 0, qtdemux_dump_fLaC},
   {FOURCC_dfLa, "dfLa", 0, qtdemux_dump_dfLa},
+  {FOURCC_opus, "opus", 0, qtdemux_dump_opus},
+  {FOURCC_dops, "dOps", 0, qtdemux_dump_dops},
   {FOURCC_wave, "wave", QT_FLAG_CONTAINER},
   {FOURCC_appl, "appl", QT_FLAG_CONTAINER},
   {FOURCC_cfhd, "cfhd", QT_FLAG_CONTAINER},
@@ -231,6 +233,8 @@ static const QtNodeType qt_node_types[] = {
   {FOURCC_schi, "scheme information", QT_FLAG_CONTAINER},
   {FOURCC_pssh, "protection system specific header", 0},
   {FOURCC_tenc, "track encryption", 0},
+  {FOURCC_sgpd, "sample group description", 0},
+  {FOURCC_sbgp, "sample to group", 0},
   {FOURCC_stpp, "XML subtitle sample entry", 0},
   {FOURCC_wvtt, "WebVTT subtitle sample entry", 0},
   {FOURCC_clcp, "Closed Caption", 0},
@@ -261,7 +265,7 @@ qtdemux_type_get (guint32 fourcc)
       return qt_node_types + i;
   }
 
-  GST_WARNING ("unknown QuickTime node type %" GST_FOURCC_FORMAT,
+  GST_FIXME ("unknown QuickTime node type %" GST_FOURCC_FORMAT,
       GST_FOURCC_ARGS (fourcc));
 
   return qt_node_types + n_qt_node_types - 1;
diff --git a/gst/level/gstlevel.c b/gst/level/gstlevel.c
index 968fd4fe726a6ff8a74ed18985ed5c13a1350cb2..b12a6221f9e05517b0eae28d4b50a894eedd80b3 100644
--- a/gst/level/gstlevel.c
+++ b/gst/level/gstlevel.c
@@ -731,8 +731,18 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
   if (filter->audio_level_meta) {
     gdouble RMS = sqrt (CS_tot / num_int_samples);
     gdouble RMSdB = 20 * log10 (RMS + EPSILON);
-
-    gst_level_rtp_audio_level_meta (filter, in, -RMSdB);
+    guint8 level;
+
+    /* -127db is considered silent in audio level meta, clip anything below and
+     * avoid possible integer overflow */
+    if (RMSdB < -127.0)
+      level = 127;
+    else if (RMSdB > 0.0)
+      level = 0;
+    else
+      level = -RMSdB;
+
+    gst_level_rtp_audio_level_meta (filter, in, level);
   }
 
   GST_OBJECT_UNLOCK (filter);
@@ -747,6 +757,9 @@ gst_level_post_message (GstLevel * filter)
   gint channels, rate, frames = filter->num_frames;
   GstClockTime duration;
 
+  if (!GST_AUDIO_INFO_IS_VALID (&filter->info))
+    return;
+
   channels = GST_AUDIO_INFO_CHANNELS (&filter->info);
   rate = GST_AUDIO_INFO_RATE (&filter->info);
   duration = GST_FRAMES_TO_CLOCK_TIME (frames, rate);
diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c
index 1e771b04d0dbf9ebbf3c5e8c3589bf9942346f30..afde4ee62897b6b2c8cf7d4a03a87dd39da2e02d 100644
--- a/gst/matroska/matroska-demux.c
+++ b/gst/matroska/matroska-demux.c
@@ -771,11 +771,8 @@ gst_matroska_demux_parse_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml,
         if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
           break;
 
-        if (num == 0) {
-          GST_ERROR_OBJECT (demux, "Invalid TrackUID 0");
-          ret = GST_FLOW_ERROR;
-          break;
-        }
+        if (num == 0)
+          GST_WARNING_OBJECT (demux, "Invalid TrackUID 0");
 
         GST_DEBUG_OBJECT (demux, "TrackUID: %" G_GUINT64_FORMAT, num);
         context->uid = num;
@@ -1599,6 +1596,15 @@ gst_matroska_demux_parse_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml,
     context->tags_changed = TRUE;
   }
 
+  /* https://dev.w3.org/html5/html-sourcing-inband-tracks/#webm  */
+  if (context->num) {
+    gchar *track_id_str = g_strdup_printf ("%" G_GUINT64_FORMAT, context->num);
+    gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE,
+        GST_TAG_CONTAINER_SPECIFIC_TRACK_ID, track_id_str, NULL);
+    g_free (track_id_str);
+    context->tags_changed = TRUE;
+  }
+
   if (caps == NULL) {
     GST_WARNING_OBJECT (demux, "could not determine caps for stream with "
         "codec_id='%s'", context->codec_id);
@@ -2811,12 +2817,12 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
 
   GST_DEBUG_OBJECT (demux, "configuring seek");
 
-  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
-  keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
-  after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
-  before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
-  accurate = ! !(flags & GST_SEEK_FLAG_ACCURATE);
-  instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
+  flush = !!(flags & GST_SEEK_FLAG_FLUSH);
+  keyunit = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
+  after = !!(flags & GST_SEEK_FLAG_SNAP_AFTER);
+  before = !!(flags & GST_SEEK_FLAG_SNAP_BEFORE);
+  accurate = !!(flags & GST_SEEK_FLAG_ACCURATE);
+  instant_rate_change = !!(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
 
   /* Directly send the instant-rate-change event here before taking the
    * stream-lock so that it can be applied as soon as possible */
@@ -3885,10 +3891,14 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
   GstMatroskaTrackAudioContext *audiocontext =
       (GstMatroskaTrackAudioContext *) stream;
   GstBuffer *newbuf = NULL;
-  GstMapInfo map, outmap;
   guint8 *buf_data, *data;
   Wavpack4Header wvh;
 
+  if (!stream->codec_priv || stream->codec_priv_size < 2) {
+    GST_ERROR_OBJECT (element, "No or too small wavpack codec private data");
+    return GST_FLOW_ERROR;
+  }
+
   wvh.ck_id[0] = 'w';
   wvh.ck_id[1] = 'v';
   wvh.ck_id[2] = 'p';
@@ -3902,11 +3912,11 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
 
   if (audiocontext->channels <= 2) {
     guint32 block_samples, tmp;
+    GstMapInfo outmap;
     gsize size = gst_buffer_get_size (*buf);
 
     if (size < 4) {
       GST_ERROR_OBJECT (element, "Too small wavpack buffer");
-      gst_buffer_unmap (*buf, &map);
       return GST_FLOW_ERROR;
     }
 
@@ -3944,6 +3954,7 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
     *buf = newbuf;
     audiocontext->wvpk_block_index += block_samples;
   } else {
+    GstMapInfo map, outmap;
     guint8 *outdata = NULL;
     gsize buf_size, size;
     guint32 block_samples, flags, crc;
@@ -3970,7 +3981,7 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
     data += 4;
     size -= 4;
 
-    while (size > 12) {
+    while (size >= 12) {
       flags = GST_READ_UINT32_LE (data);
       data += 4;
       size -= 4;
@@ -4031,11 +4042,16 @@ gst_matroska_demux_add_wvpk_header (GstElement * element,
     }
     gst_buffer_unmap (*buf, &map);
 
-    newbuf = gst_adapter_take_buffer (adapter, gst_adapter_available (adapter));
+    size = gst_adapter_available (adapter);
+    if (size > 0) {
+      newbuf = gst_adapter_take_buffer (adapter, size);
+      gst_buffer_copy_into (newbuf, *buf,
+          GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS, 0, -1);
+    } else {
+      newbuf = NULL;
+    }
     g_object_unref (adapter);
 
-    gst_buffer_copy_into (newbuf, *buf,
-        GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS, 0, -1);
     gst_buffer_unref (*buf);
     *buf = newbuf;
 
@@ -4972,6 +4988,18 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
       if (stream->postprocess_frame) {
         GST_LOG_OBJECT (demux, "running post process");
         ret = stream->postprocess_frame (GST_ELEMENT (demux), stream, &sub);
+        if (ret != GST_FLOW_OK) {
+          gst_clear_buffer (&sub);
+          goto next_lace;
+        }
+
+        if (sub == NULL) {
+          GST_WARNING_OBJECT (demux,
+              "Postprocessing buffer with timestamp %" GST_TIME_FORMAT
+              " for stream %d failed", GST_TIME_ARGS (buffer_timestamp),
+              stream_num);
+          goto next_lace;
+        }
       }
 
       /* At this point, we have a sub-buffer pointing at data within a larger
@@ -6076,6 +6104,20 @@ pause:
         if ((stop = demux->common.segment.stop) == -1)
           stop = demux->last_stop_end;
 
+        /* segment.position will still be at the last timestamp and won't always
+         * include the duration of the last packet. Expand that to the segment
+         * duration so that segment.base is increased correctly to include the
+         * length of the last packet when doing segment seeks. We need to do
+         * this before the segment-done event goes out so everything's ready
+         * for the next seek request coming in. */
+        if (GST_CLOCK_TIME_IS_VALID (stop)) {
+          GST_DEBUG_OBJECT (demux, "End of segment, updating segment.position "
+              "from %" GST_TIME_FORMAT " to stop %" GST_TIME_FORMAT,
+              GST_TIME_ARGS (demux->common.segment.position),
+              GST_TIME_ARGS (stop));
+          demux->common.segment.position = stop;
+        }
+
         GST_LOG_OBJECT (demux, "Sending segment done, at end of segment");
         msg = gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME,
             stop);
@@ -6231,11 +6273,6 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
           "received format %d segment %" GST_SEGMENT_FORMAT, segment->format,
           segment);
 
-      if (demux->common.state < GST_MATROSKA_READ_STATE_DATA) {
-        GST_DEBUG_OBJECT (demux, "still starting");
-        goto exit;
-      }
-
       if (segment->format == GST_FORMAT_TIME) {
         demux->upstream_format_is_time = TRUE;
         demux->segment_seqnum = gst_event_get_seqnum (event);
@@ -6245,6 +6282,11 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
         goto exit;
       }
 
+      if (demux->common.state < GST_MATROSKA_READ_STATE_DATA) {
+        GST_DEBUG_OBJECT (demux, "still starting");
+        goto exit;
+      }
+
       demux->upstream_format_is_time = FALSE;
 
       /* we only expect a BYTE segment, e.g. following a seek */
@@ -6490,12 +6532,24 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
       case GST_MAKE_FOURCC ('Y', '8', ' ', ' '):
         format = GST_VIDEO_FORMAT_GRAY8;
         break;
+      case GST_MAKE_FOURCC ('Y', '1', 0, 10):
+        format = GST_VIDEO_FORMAT_GRAY10_LE32;
+        break;
+      case GST_MAKE_FOURCC ('Y', '1', 0, 16):
+        format = GST_VIDEO_FORMAT_GRAY16_LE;
+        break;
       case GST_MAKE_FOURCC ('R', 'G', 'B', 24):
         format = GST_VIDEO_FORMAT_RGB;
         break;
       case GST_MAKE_FOURCC ('B', 'G', 'R', 24):
         format = GST_VIDEO_FORMAT_BGR;
         break;
+      case GST_MAKE_FOURCC ('R', 'B', 'A', 64):
+        format = GST_VIDEO_FORMAT_RGBA64_LE;
+        break;
+      case GST_MAKE_FOURCC ('B', 'R', 'A', 64):
+        format = GST_VIDEO_FORMAT_BGRA64_LE;
+        break;
       default:
         GST_DEBUG ("Unknown fourcc %" GST_FOURCC_FORMAT,
             GST_FOURCC_ARGS (videocontext->fourcc));
@@ -7129,8 +7183,7 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
 
       /* 18 is the waveformatex size */
       if (size > 18) {
-        codec_data = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
-            data + 18, size - 18, 0, size - 18, NULL, NULL);
+        codec_data = gst_buffer_new_memdup (data + 18, size - 18);
       }
 
       if (riff_audio_fmt)
diff --git a/gst/matroska/matroska-ids.c b/gst/matroska/matroska-ids.c
index f11b7c2ce31f7e5296b93c19dc8cb96042672330..ba645f7306d94a6d95db908b68c96a9095897fe4 100644
--- a/gst/matroska/matroska-ids.c
+++ b/gst/matroska/matroska-ids.c
@@ -189,8 +189,10 @@ gst_matroska_parse_xiph_stream_headers (gpointer codec_data,
     if (offset + length[i] > codec_data_size)
       goto error;
 
-    hdr = gst_buffer_new_memdup (p + offset, length[i]);
-    gst_buffer_list_add (list, hdr);
+    if (length[i] > 0) {
+      hdr = gst_buffer_new_memdup (p + offset, length[i]);
+      gst_buffer_list_add (list, hdr);
+    }
 
     offset += length[i];
   }
diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c
index ca90b6ac35f927b8a224d8a94ce001b65b9315d8..0fccd55a5fcf6bfaa004156564395b5dcf8f90a2 100644
--- a/gst/matroska/matroska-mux.c
+++ b/gst/matroska/matroska-mux.c
@@ -141,7 +141,8 @@ static GstStaticPadTemplate videosink_templ =
         "video/x-vp9, "
         COMMON_VIDEO_CAPS "; "
         "video/x-raw, "
-        "format = (string) { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }, "
+        "format = (string) { YUY2, I420, YV12, UYVY, AYUV, GRAY8, GRAY10_LE32,"
+        " GRAY16_LE, BGR, RGB, RGBA64_LE, BGRA64_LE }, "
         COMMON_VIDEO_CAPS "; "
         "video/x-prores, "
         COMMON_VIDEO_CAPS "; "
@@ -180,7 +181,9 @@ static GstStaticPadTemplate audiosink_templ =
         COMMON_AUDIO_CAPS "; "
         "audio/x-flac, "
         COMMON_AUDIO_CAPS "; "
-        "audio/x-opus; "
+        "audio/x-opus, "
+        "channels = (int) [ 1, 8 ], "
+        "rate = (int) { 8000, 16000, 24000, 32000, 48000 }; "
         "audio/x-speex, "
         COMMON_AUDIO_CAPS "; "
         "audio/x-raw, "
@@ -1254,10 +1257,18 @@ skip_details:
         videocontext->fourcc = GST_STR_FOURCC (fstr);
       else if (!strcmp (fstr, "GRAY8"))
         videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
+      else if (!strcmp (fstr, "GRAY10_LE32"))
+        videocontext->fourcc = GST_MAKE_FOURCC ('Y', '1', 0, 10);
+      else if (!strcmp (fstr, "GRAY16_LE"))
+        videocontext->fourcc = GST_MAKE_FOURCC ('Y', '1', 0, 16);
       else if (!strcmp (fstr, "BGR"))
         videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
       else if (!strcmp (fstr, "RGB"))
         videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
+      else if (!strcmp (fstr, "RGBA64_LE"))
+        videocontext->fourcc = GST_MAKE_FOURCC ('R', 'B', 'A', 64);
+      else if (!strcmp (fstr, "BGRA64_LE"))
+        videocontext->fourcc = GST_MAKE_FOURCC ('B', 'R', 'A', 64);
     }
   } else if (!strcmp (mimetype, "video/x-huffyuv")      /* MS/VfW compatibility cases */
       ||!strcmp (mimetype, "video/x-divx")
@@ -3131,6 +3142,21 @@ gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
   return internal_edition;
 }
 
+static gboolean
+gst_matroska_pads_is_audio_only (GstMatroskaMux * mux)
+{
+  for (GSList * collected = mux->collect->data; collected;
+      collected = g_slist_next (collected)) {
+    GstMatroskaPad *collect_pad = (GstMatroskaPad *) collected->data;
+
+    if (collect_pad->track->type != GST_MATROSKA_TRACK_TYPE_AUDIO) {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
 /**
  * gst_matroska_mux_start:
  * @mux: #GstMatroskaMux
@@ -3192,7 +3218,7 @@ gst_matroska_mux_start (GstMatroskaMux * mux, GstMatroskaPad * first_pad,
   gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
 
   /* output caps */
-  audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
+  audio_only = gst_matroska_pads_is_audio_only (mux);
   if (mux->is_webm) {
     media_type = (audio_only) ? "audio/webm" : "video/webm";
   } else {
@@ -3444,21 +3470,21 @@ static const struct
 }
 gst_matroska_tag_conv[] = {
   {
-  GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
-  GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
-  GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
-  GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
-  GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
-  GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
-  GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
-  GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
-  GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
-  GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
-  GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
-  GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
-  GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
-  GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
-  GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
+      GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
+      GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
+      GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
+      GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
+      GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
+      GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
+      GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
+      GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
+      GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
+      GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
+      GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
+      GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
+      GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
+      GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
+      GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
 };
 
 /* Every stagefright implementation on android up to and including 6.0.1 is using
@@ -3997,7 +4023,7 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
   guint64 block_duration, duration_diff = 0;
   gboolean is_video_keyframe = FALSE;
   gboolean is_video_invisible = FALSE;
-  gboolean is_audio_only = FALSE;
+  gboolean is_audio_only = FALSE, is_audio = FALSE;
   gboolean is_min_duration_reached = FALSE;
   gboolean is_max_duration_exceeded = FALSE;
   GstMatroskamuxPad *pad;
@@ -4083,8 +4109,9 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
    * related arithmetic, so apply the timestamp offset if we have one */
   buffer_timestamp += mux->cluster_timestamp_offset;
 
-  is_audio_only = (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
-      (mux->num_streams == 1);
+  is_audio_only = gst_matroska_pads_is_audio_only (mux);
+  is_audio = collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO;
+
   is_min_duration_reached = (mux->min_cluster_duration == 0
       || (buffer_timestamp > mux->cluster_time
           && (buffer_timestamp - mux->cluster_time) >=
@@ -4192,6 +4219,15 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
     }
   }
 
+  if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
+    cmeta = gst_buffer_get_audio_clipping_meta (buf);
+    g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
+
+    /* Start clipping is done via header and CodecDelay */
+    if (cmeta && !cmeta->end)
+      cmeta = NULL;
+  }
+
   /* Check if the duration differs from the default duration. */
   write_duration = FALSE;
   block_duration = 0;
@@ -4199,6 +4235,15 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
     block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
     block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
 
+    /* Padding should be considered in the block duration and is clipped off
+     * again during playback. Specifically, firefox considers it a fatal error
+     * if there is more padding than the block duration */
+    if (cmeta) {
+      guint64 end = gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
+      end = gst_util_uint64_scale (end, 1, mux->time_scale);
+      block_duration += end;
+    }
+
     /* small difference should be ok. */
     if (block_duration > collect_pad->default_duration_scaled + 1 ||
         block_duration < collect_pad->default_duration_scaled - 1) {
@@ -4226,17 +4271,8 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
   if (is_video_invisible)
     flags |= 0x08;
 
-  if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
-    cmeta = gst_buffer_get_audio_clipping_meta (buf);
-    g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
-
-    /* Start clipping is done via header and CodecDelay */
-    if (cmeta && !cmeta->end)
-      cmeta = NULL;
-  }
-
   if (mux->doctype_version > 1 && !write_duration && !cmeta) {
-    if (is_video_keyframe)
+    if (is_video_keyframe || is_audio)
       flags |= 0x80;
 
     hdr =
diff --git a/gst/matroska/matroska-parse.c b/gst/matroska/matroska-parse.c
index 3fcb5c55e803925a38b8416d248975b1fff6118e..06805d1708afb7b23244a6f4e9d93f124eb06cb1 100644
--- a/gst/matroska/matroska-parse.c
+++ b/gst/matroska/matroska-parse.c
@@ -346,11 +346,8 @@ gst_matroska_parse_add_stream (GstMatroskaParse * parse, GstEbmlRead * ebml)
         if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
           break;
 
-        if (num == 0) {
-          GST_ERROR_OBJECT (parse, "Invalid TrackUID 0");
-          ret = GST_FLOW_ERROR;
-          break;
-        }
+        if (num == 0)
+          GST_WARNING_OBJECT (parse, "Invalid TrackUID 0");
 
         GST_DEBUG_OBJECT (parse, "TrackUID: %" G_GUINT64_FORMAT, num);
         context->uid = num;
diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c
index 6fadbba95675921f06f66462eb21b549be298fd8..6c9155178fa3182b550e0d1b71fc2a767fa10256 100644
--- a/gst/matroska/matroska-read-common.c
+++ b/gst/matroska/matroska-read-common.c
@@ -448,6 +448,8 @@ gst_matroska_parse_protection_meta (gpointer * data_out, gsize * size_out,
 
   /* Unencrypted buffer */
   if (!(signal_byte & GST_MATROSKA_BLOCK_ENCRYPTED)) {
+    *size_out = gst_byte_reader_get_remaining (&reader);
+    gst_byte_reader_get_data (&reader, *size_out, (const guint8 **) data_out);
     return TRUE;
   }
 
@@ -592,8 +594,9 @@ gst_matroska_parse_protection_meta (gpointer * data_out, gsize * size_out,
     gst_structure_set (info_protect, "subsample_count", G_TYPE_UINT, 0, NULL);
   }
 
-  gst_byte_reader_get_data (&reader, 0, (const guint8 **) data_out);
   *size_out = gst_byte_reader_get_remaining (&reader);
+  gst_byte_reader_get_data (&reader, *size_out, (const guint8 **) data_out);
+
   return TRUE;
 
 release_err:
@@ -756,7 +759,7 @@ gst_matroska_read_common_parse_skip (GstMatroskaReadCommon * common,
   } else if (id == GST_EBML_ID_CRC32) {
     GST_DEBUG_OBJECT (common->sinkpad, "Skipping EBML CRC32 element");
   } else {
-    GST_WARNING_OBJECT (common->sinkpad,
+    GST_FIXME_OBJECT (common->sinkpad,
         "Unknown %s subelement 0x%x - ignoring", parent_name, id);
   }
 
@@ -2756,7 +2759,7 @@ gst_matroska_read_common_parse_metadata (GstMatroskaReadCommon * common,
   }
 
   common->tags_parsed =
-      g_list_prepend (common->tags_parsed, g_slice_new (guint64));
+      g_list_prepend (common->tags_parsed, g_new (guint64, 1));
   *((guint64 *) common->tags_parsed->data) = curpos;
   /* fall-through */
 
@@ -3278,7 +3281,7 @@ gst_matroska_read_common_read_track_encodings (GstMatroskaReadCommon * common,
 void
 gst_matroska_read_common_free_parsed_el (gpointer mem, gpointer user_data)
 {
-  g_slice_free (guint64, mem);
+  g_free (mem);
 }
 
 void
diff --git a/gst/monoscope/gstmonoscope.c b/gst/monoscope/gstmonoscope.c
index 867e7ec0e0564d53275a608a985b17876c42df84..5ddae268806e3857688f4a2c94cda15e363806a2 100644
--- a/gst/monoscope/gstmonoscope.c
+++ b/gst/monoscope/gstmonoscope.c
@@ -534,7 +534,7 @@ gst_monoscope_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
         /* we're late, this is a good estimate for next displayable
          * frame (see part-qos.txt) */
         monoscope->earliest_time =
-            timestamp + 2 * diff + monoscope->frame_duration;
+            timestamp + MIN (2 * diff, GST_SECOND) + monoscope->frame_duration;
       else
         monoscope->earliest_time = timestamp + diff;
       GST_OBJECT_UNLOCK (monoscope);
diff --git a/gst/multifile/gstimagesequencesrc.c b/gst/multifile/gstimagesequencesrc.c
index 8581e73e3a94f8c11a3e0a539f73e03c96e374bf..b60f73b8825f89e918e60ca36b8afb4cdf85cf12 100644
--- a/gst/multifile/gstimagesequencesrc.c
+++ b/gst/multifile/gstimagesequencesrc.c
@@ -46,6 +46,7 @@
 
 #include <gst/gst.h>
 #include <gst/base/gsttypefindhelper.h>
+#include <glib/gi18n-lib.h>
 
 #include "gstimagesequencesrc.h"
 
@@ -337,7 +338,7 @@ gst_image_sequence_src_class_init (GstImageSequenceSrcClass * klass)
   gst_element_class_set_static_metadata (gstelement_class,
       "Image Sequence Source", "Source/File/Video",
       "Create a video stream from a sequence of image files",
-      "Cesar Fabian Orccon Chipana <cfoch.fabian@gmail.com>\n"
+      "Cesar Fabian Orccon Chipana <cfoch.fabian@gmail.com>, "
       "Thibault Saunier <tsaunier@igalia.com>");
 }
 
@@ -356,7 +357,7 @@ gst_image_sequence_src_init (GstImageSequenceSrc * self)
   self->start_index = DEFAULT_START_INDEX;
   self->index = 0;
   self->stop_index = DEFAULT_STOP_INDEX;
-  self->path = NULL;
+  self->path = g_strdup (DEFAULT_LOCATION);
   self->caps = NULL;
   self->n_frames = 0;
   self->fps_n = 30;
@@ -488,24 +489,25 @@ static gint
 gst_image_sequence_src_count_frames (GstImageSequenceSrc * self,
     gboolean can_read)
 {
+  gchar *previous_filename = NULL;
   if (can_read && self->stop_index < 0 && self->path) {
     gint i;
 
     for (i = self->start_index;; i++) {
       gchar *filename = g_strdup_printf (self->path, i);
-
-      if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+      if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)
+          || !g_strcmp0 (previous_filename, filename)) {
         i--;
         g_free (filename);
         break;
       }
-
-      g_free (filename);
+      g_free (previous_filename);
+      previous_filename = filename;
     }
     if (i > self->start_index)
       self->stop_index = i;
   }
-
+  g_free (previous_filename);
   if (self->stop_index >= self->start_index)
     self->n_frames = self->stop_index - self->start_index + 1;
   return self->n_frames;
@@ -564,7 +566,12 @@ gst_image_sequence_src_get_filename (GstImageSequenceSrc * self)
   gchar *filename;
 
   GST_DEBUG ("Reading filename at index %d.", self->index);
-  filename = g_strdup_printf (self->path, self->index);
+  if (self->path != NULL) {
+    filename = g_strdup_printf (self->path, self->index);
+  } else {
+    GST_WARNING_OBJECT (self, "No filename location set!");
+    filename = NULL;
+  }
 
   return filename;
 }
@@ -604,7 +611,7 @@ gst_image_sequence_src_create (GstPushSrc * src, GstBuffer ** buffer)
   UNLOCK (self);
 
   if (!filename)
-    goto handle_error;
+    goto error_no_filename;
 
   ret = g_file_get_contents (filename, &data, &size, &error);
   if (!ret)
@@ -645,6 +652,12 @@ gst_image_sequence_src_create (GstPushSrc * src, GstBuffer ** buffer)
   self->index += self->reverse ? -1 : 1;
   return GST_FLOW_OK;
 
+error_no_filename:
+  {
+    GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
+        (_("No file name specified for reading.")), (NULL));
+    return GST_FLOW_ERROR;
+  }
 handle_error:
   {
     if (error != NULL) {
diff --git a/gst/multifile/gstmultifilesrc.c b/gst/multifile/gstmultifilesrc.c
index 48221b9ada72fe19fd4bb64037555863216a3d38..e984e85dd4c0b85e2b2814a200be62af6e7320cd 100644
--- a/gst/multifile/gstmultifilesrc.c
+++ b/gst/multifile/gstmultifilesrc.c
@@ -46,7 +46,7 @@
 #endif
 
 #include "gstmultifilesrc.h"
-
+#include <glib/gi18n-lib.h>
 
 static GstFlowReturn gst_multi_file_src_create (GstPushSrc * src,
     GstBuffer ** buffer);
@@ -392,7 +392,12 @@ gst_multi_file_src_get_filename (GstMultiFileSrc * multifilesrc)
   gchar *filename;
 
   GST_DEBUG ("%d", multifilesrc->index);
-  filename = g_strdup_printf (multifilesrc->filename, multifilesrc->index);
+  if (multifilesrc->filename != NULL) {
+    filename = g_strdup_printf (multifilesrc->filename, multifilesrc->index);
+  } else {
+    GST_WARNING_OBJECT (multifilesrc, "No filename location set!");
+    filename = NULL;
+  }
 
   return filename;
 }
@@ -424,6 +429,9 @@ gst_multi_file_src_create (GstPushSrc * src, GstBuffer ** buffer)
 
   filename = gst_multi_file_src_get_filename (multifilesrc);
 
+  if (!filename)
+    goto error_no_filename;
+
   GST_DEBUG_OBJECT (multifilesrc, "reading from file \"%s\".", filename);
 
   ret = g_file_get_contents (filename, &data, &size, &error);
@@ -472,6 +480,12 @@ gst_multi_file_src_create (GstPushSrc * src, GstBuffer ** buffer)
   *buffer = buf;
   return GST_FLOW_OK;
 
+error_no_filename:
+  {
+    GST_ELEMENT_ERROR (multifilesrc, RESOURCE, NOT_FOUND,
+        (_("No file name specified for reading.")), (NULL));
+    return GST_FLOW_ERROR;
+  }
 handle_error:
   {
     if (error != NULL) {
diff --git a/gst/multifile/gstsplitmuxpartreader.c b/gst/multifile/gstsplitmuxpartreader.c
index 7ae526fe001e5d1a6aa836a9cfb10d913661ac0c..13ab57a5fdb262d4492252540fde48a8fbde5bc6 100644
--- a/gst/multifile/gstsplitmuxpartreader.c
+++ b/gst/multifile/gstsplitmuxpartreader.c
@@ -158,12 +158,12 @@ handle_buffer_measuring (GstSplitMuxPartReader * reader,
    * not to generate output timestamps */
 
   /* Update the stored max duration on the pad,
-   * always preferring making DTS contiguous
+   * always preferring making reordered PTS contiguous
    * where possible */
-  if (GST_BUFFER_DTS_IS_VALID (buf))
-    ts = GST_BUFFER_DTS (buf) + offset;
-  else if (GST_BUFFER_PTS_IS_VALID (buf))
+  if (GST_BUFFER_PTS_IS_VALID (buf))
     ts = GST_BUFFER_PTS (buf) + offset;
+  else if (GST_BUFFER_DTS_IS_VALID (buf))
+    ts = GST_BUFFER_DTS (buf) + offset;
 
   GST_DEBUG_OBJECT (reader, "Pad %" GST_PTR_FORMAT
       " incoming DTS %" GST_TIME_FORMAT
@@ -203,7 +203,7 @@ static void
 splitmux_part_free_queue_item (GstDataQueueItem * item)
 {
   gst_mini_object_unref (item->object);
-  g_slice_free (GstDataQueueItem, item);
+  g_free (item);
 }
 
 static GstFlowReturn
@@ -251,7 +251,7 @@ splitmux_part_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
   /* We are active, and one queue is empty, place this buffer in
    * the dataqueue */
   GST_LOG_OBJECT (reader, "Enqueueing buffer %" GST_PTR_FORMAT, buf);
-  item = g_slice_new (GstDataQueueItem);
+  item = g_new (GstDataQueueItem, 1);
   item->destroy = (GDestroyNotify) splitmux_part_free_queue_item;
   item->object = GST_MINI_OBJECT (buf);
   item->size = gst_buffer_get_size (buf);
@@ -341,7 +341,7 @@ enqueue_event (GstSplitMuxPartReader * reader, GstSplitMuxPartPad * part_pad,
   GstDataQueueItem *item;
 
   GST_LOG_OBJECT (reader, "Enqueueing event %" GST_PTR_FORMAT, event);
-  item = g_slice_new (GstDataQueueItem);
+  item = g_new (GstDataQueueItem, 1);
   item->destroy = (GDestroyNotify) splitmux_part_free_queue_item;
   item->object = GST_MINI_OBJECT (event);
   item->size = 0;
@@ -650,8 +650,7 @@ static void splitmux_part_reader_finalize (GObject * object);
 static void splitmux_part_reader_reset (GstSplitMuxPartReader * reader);
 
 #define gst_splitmux_part_reader_parent_class parent_class
-G_DEFINE_TYPE (GstSplitMuxPartReader, gst_splitmux_part_reader,
-    GST_TYPE_PIPELINE);
+G_DEFINE_TYPE (GstSplitMuxPartReader, gst_splitmux_part_reader, GST_TYPE_BIN);
 
 static void
 gst_splitmux_part_reader_class_init (GstSplitMuxPartReaderClass * klass)
@@ -676,6 +675,7 @@ static void
 gst_splitmux_part_reader_init (GstSplitMuxPartReader * reader)
 {
   GstElement *typefind;
+  GstBus *bus;
 
   reader->active = FALSE;
   reader->duration = GST_CLOCK_TIME_NONE;
@@ -685,6 +685,10 @@ gst_splitmux_part_reader_init (GstSplitMuxPartReader * reader)
   g_mutex_init (&reader->type_lock);
   g_mutex_init (&reader->msg_lock);
 
+  bus = g_object_new (GST_TYPE_BUS, "enable-async", FALSE, NULL);
+  gst_element_set_bus (GST_ELEMENT_CAST (reader), bus);
+  gst_object_unref (bus);
+
   /* FIXME: Create elements on a state change */
   reader->src = gst_element_factory_make ("filesrc", NULL);
   if (reader->src == NULL) {
diff --git a/gst/multifile/gstsplitmuxpartreader.h b/gst/multifile/gstsplitmuxpartreader.h
index 78ecc6eb5a778716a62b452949084d55789fd252..13fd66419c3202778adcbec8e537e4cdb478dcb6 100644
--- a/gst/multifile/gstsplitmuxpartreader.h
+++ b/gst/multifile/gstsplitmuxpartreader.h
@@ -54,7 +54,7 @@ typedef GstPad *(*GstSplitMuxPartReaderPadCb)(GstSplitMuxPartReader *reader, Gst
 
 struct _GstSplitMuxPartReader
 {
-  GstPipeline parent;
+  GstBin parent;
 
   GstSplitMuxPartState prep_state;
 
@@ -88,7 +88,7 @@ struct _GstSplitMuxPartReader
 
 struct _GstSplitMuxPartReaderClass
 {
-  GstPipelineClass parent_class;
+  GstBinClass parent_class;
 
   void (*prepared)  (GstSplitMuxPartReader *reader);
   void (*end_of_part) (GstSplitMuxPartReader *reader);
diff --git a/gst/multifile/gstsplitmuxsink.c b/gst/multifile/gstsplitmuxsink.c
index ad6c3d37827a1a0eb666fcf797d4fa2f3d8ddf60..818a5216930507d00125605cd6156639a26bfc6a 100644
--- a/gst/multifile/gstsplitmuxsink.c
+++ b/gst/multifile/gstsplitmuxsink.c
@@ -252,32 +252,32 @@ static GstClockTime calculate_next_max_timecode (GstSplitMuxSink * splitmux,
 static MqStreamBuf *
 mq_stream_buf_new (void)
 {
-  return g_slice_new0 (MqStreamBuf);
+  return g_new0 (MqStreamBuf, 1);
 }
 
 static void
 mq_stream_buf_free (MqStreamBuf * data)
 {
-  g_slice_free (MqStreamBuf, data);
+  g_free (data);
 }
 
 static SplitMuxOutputCommand *
 out_cmd_buf_new (void)
 {
-  return g_slice_new0 (SplitMuxOutputCommand);
+  return g_new0 (SplitMuxOutputCommand, 1);
 }
 
 static void
 out_cmd_buf_free (SplitMuxOutputCommand * data)
 {
-  g_slice_free (SplitMuxOutputCommand, data);
+  g_free (data);
 }
 
 static void
 input_gop_free (InputGop * gop)
 {
   g_clear_pointer (&gop->start_tc, gst_video_time_code_free);
-  g_slice_free (InputGop, gop);
+  g_free (gop);
 }
 
 static void
@@ -569,7 +569,7 @@ gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass)
    * @splitmux: the #GstSplitMuxSink
    * @muxer: the newly added muxer element
    *
-   * Since: 1.14
+   * Since: 1.16
    */
   signals[SIGNAL_MUXER_ADDED] =
       g_signal_new ("muxer-added", G_TYPE_FROM_CLASS (klass),
@@ -580,7 +580,7 @@ gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass)
    * @splitmux: the #GstSplitMuxSink
    * @sink: the newly added sink element
    *
-   * Since: 1.14
+   * Since: 1.16
    */
   signals[SIGNAL_SINK_ADDED] =
       g_signal_new ("sink-added", G_TYPE_FROM_CLASS (klass),
@@ -2409,7 +2409,7 @@ need_new_fragment (GstSplitMuxSink * splitmux,
   }
 
   if (splitmux->tc_interval) {
-    GstClockTime next_gop_start_time =
+    GstClockTimeDiff next_gop_start_time =
         next_gop ? next_gop->start_time : splitmux->max_in_running_time;
 
     if (GST_CLOCK_TIME_IS_VALID (splitmux->next_fragment_start_tc_time) &&
@@ -2878,7 +2878,7 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
             splitmux->fragment_start_time_pts = rtime;
 
           if (g_queue_is_empty (&splitmux->pending_input_gops)) {
-            InputGop *gop = g_slice_new0 (InputGop);
+            InputGop *gop = g_new0 (InputGop, 1);
 
             gop->from_gap = TRUE;
             gop->start_time = rtime;
@@ -2902,6 +2902,25 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
       default:
         return GST_PAD_PROBE_PASS;
     }
+  } else if (info->type & GST_PAD_PROBE_TYPE_QUERY_UPSTREAM) {
+    switch (GST_QUERY_TYPE (GST_QUERY (info->data))) {
+      case GST_QUERY_LATENCY:
+        // Override the latency query to pretend that everything downstream
+        // of the sink pads is actually not live. splitmuxsink doesn't know
+        // how much latency it will possibly introduce.
+        if (info->type & GST_PAD_PROBE_TYPE_PUSH) {
+          GST_DEBUG_OBJECT (pad,
+              "Overriding latency query to pretend we're not live");
+          gst_query_set_latency (info->data, FALSE, 0, GST_CLOCK_TIME_NONE);
+          return GST_PAD_PROBE_HANDLED;
+        } else {
+          // Should not happen as we already handled it above.
+          g_warn_if_reached ();
+          return GST_PAD_PROBE_PASS;
+        }
+      default:
+        return GST_PAD_PROBE_PASS;
+    }
   }
 
   buf = gst_pad_probe_info_get_buffer (info);
@@ -2947,10 +2966,16 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
   else
     running_time_pts = GST_CLOCK_STIME_NONE;
 
-  if (GST_CLOCK_TIME_IS_VALID (dts))
+  if (GST_CLOCK_TIME_IS_VALID (dts)) {
     running_time_dts = my_segment_to_running_time (&ctx->in_segment, dts);
-  else
-    running_time_dts = GST_CLOCK_STIME_NONE;
+
+    /* DTS > PTS makes conceptually no sense so catch such invalid DTS here
+     * by clamping to the PTS */
+    running_time_dts = MIN (running_time_pts, running_time_dts);
+  } else {
+    /* If there is no DTS then assume PTS=DTS */
+    running_time_dts = running_time_pts;
+  }
 
   /* Try to make sure we have a valid running time */
   if (!GST_CLOCK_STIME_IS_VALID (ctx->in_running_time)) {
@@ -3026,7 +3051,7 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
 
     if (!gop || (!gop->from_gap
             && !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))) {
-      gop = g_slice_new0 (InputGop);
+      gop = g_new0 (InputGop, 1);
 
       gop->start_time = running_time;
       gop->start_time_pts = running_time_pts;
@@ -3178,13 +3203,11 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
         }
         break;
       case SPLITMUX_INPUT_STATE_WAITING_GOP_COLLECT:{
-        /* We're collecting a GOP, this is normally only called for non-reference
+        /* We're collecting a GOP, this is only ever called for non-reference
          * contexts as the reference context would be waiting inside
          * check_completed_gop() */
-        if (G_UNLIKELY (ctx->is_reference)) {
-          check_completed_gop (splitmux, ctx);
-          break;
-        }
+
+        g_assert (!ctx->is_reference);
 
         /* If we overran the target timestamp, it might be time to process
          * the GOP, otherwise bail out for more data. */
@@ -3555,7 +3578,7 @@ gst_splitmux_sink_request_new_pad (GstElement * element,
   ctx->sink_pad_block_id =
       gst_pad_add_probe (q_sink,
       GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH |
-      GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM,
+      GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_UPSTREAM,
       (GstPadProbeCallback) handle_mq_input, ctx, NULL);
 
   GST_DEBUG_OBJECT (splitmux, "splitmuxsink pad %" GST_PTR_FORMAT
diff --git a/gst/multifile/gstsplitmuxsrc.c b/gst/multifile/gstsplitmuxsrc.c
index c626f79f371205dbb8450db692e31110b44bf9ff..48ea888c686c46a3e81a393a65f566964c3c31bd 100644
--- a/gst/multifile/gstsplitmuxsrc.c
+++ b/gst/multifile/gstsplitmuxsrc.c
@@ -810,7 +810,7 @@ gst_splitmux_pad_loop (GstPad * pad)
       }
     }
   }
-  g_slice_free (GstDataQueueItem, item);
+  g_free (item);
 
   gst_object_unref (reader);
   gst_object_unref (part_pad);
@@ -1177,7 +1177,6 @@ gst_splitmux_push_flush_stop (GstSplitMuxSrc * splitmux, guint32 seqnum)
     gst_event_ref (e);
     gst_pad_push_event (GST_PAD_CAST (target), e);
     target->sent_caps = FALSE;
-    target->sent_stream_start = FALSE;
     target->sent_segment = FALSE;
   }
   SPLITMUX_SRC_PADS_RUNLOCK (splitmux);
@@ -1445,6 +1444,7 @@ splitmux_src_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
 
       ret = gst_splitmux_src_activate_part (splitmux, i, flags);
       SPLITMUX_SRC_UNLOCK (splitmux);
+      break;
     }
     case GST_EVENT_RECONFIGURE:{
       GST_DEBUG_OBJECT (splitmux, "reconfigure event on pad %" GST_PTR_FORMAT,
@@ -1480,7 +1480,8 @@ splitmux_src_pad_query (GstPad * pad, GstObject * parent, GstQuery * query)
       " on %" GST_PTR_FORMAT, query, pad);
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_CAPS:
-    case GST_QUERY_POSITION:{
+    case GST_QUERY_POSITION:
+    case GST_QUERY_LATENCY:{
       GstSplitMuxPartReader *part;
       SplitMuxSrcPad *anypad;
 
diff --git a/gst/multifile/gstsplitutils.c b/gst/multifile/gstsplitutils.c
index 9b088a5f77818c6586d955de2ced5e9de4ecc517..2649a229bb0a490f9b46291a6c90a281dd2cfb91 100644
--- a/gst/multifile/gstsplitutils.c
+++ b/gst/multifile/gstsplitutils.c
@@ -30,7 +30,7 @@
 static int
 gst_split_util_array_sortfunc (gchar ** a, gchar ** b)
 {
-  return strcmp (*a, *b);
+  return gst_util_filename_compare (*a, *b);
 }
 
 gchar **
diff --git a/gst/multifile/meson.build b/gst/multifile/meson.build
index 7f7c53fd118591c4739f5c1f2adfb66c782f90a4..ad0559bc65a14ef963e18431aea7ba5b247cbac6 100644
--- a/gst/multifile/meson.build
+++ b/gst/multifile/meson.build
@@ -22,6 +22,10 @@ gstmultifile = library('gstmultifile',
 )
 plugins += [gstmultifile]
 
+if get_option('tests').disabled() or static_build
+  subdir_done()
+endif
+
 test_splitmuxpartreader_sources = [
   'test-splitmuxpartreader.c',
   'gstsplitmuxpartreader.c',
diff --git a/gst/rtp/dboolhuff.c b/gst/rtp/dboolhuff.c
index 3d0fc8f0402fdcb3530bf9b49edbb84ca7cfc503..118bd0b978cfaad00a7dbd441432c42e3ab077db 100644
--- a/gst/rtp/dboolhuff.c
+++ b/gst/rtp/dboolhuff.c
@@ -14,24 +14,25 @@
 __declspec (align (16))
      const unsigned char vp8_norm[256] = {
 #else
-const unsigned char vp8_norm[256] __attribute__ ((aligned (16))) = {
+const unsigned char vp8_norm[256] __attribute__((aligned (16))) = {
 #endif
-0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
-      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
 
 int
 vp8dx_start_decode (BOOL_DECODER * br,
diff --git a/gst/rtp/gstrtp.c b/gst/rtp/gstrtp.c
index 9528ffb10cf3df5763df16b39e5bf58ba6f4261b..180a3f634dc3e7f03e8c6fc6f9234bc0476c8866 100644
--- a/gst/rtp/gstrtp.c
+++ b/gst/rtp/gstrtp.c
@@ -66,6 +66,7 @@ plugin_init (GstPlugin * plugin)
   ret |= GST_ELEMENT_REGISTER (rtpmpvpay, plugin);
   ret |= GST_ELEMENT_REGISTER (rtpopusdepay, plugin);
   ret |= GST_ELEMENT_REGISTER (rtpopuspay, plugin);
+  ret |= GST_ELEMENT_REGISTER (rtppassthroughpay, plugin);
   ret |= GST_ELEMENT_REGISTER (rtph261pay, plugin);
   ret |= GST_ELEMENT_REGISTER (rtph261depay, plugin);
   ret |= GST_ELEMENT_REGISTER (rtph263ppay, plugin);
diff --git a/gst/rtp/gstrtpac3depay.c b/gst/rtp/gstrtpac3depay.c
index 68c23e2768267623986c6b85a268ff1248e92398..5bb607bdc45603832a2857285587bba1e9f209f0 100644
--- a/gst/rtp/gstrtpac3depay.c
+++ b/gst/rtp/gstrtpac3depay.c
@@ -52,7 +52,7 @@ static GstStaticPadTemplate gst_rtp_ac3_depay_src_template =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/ac3")
+    GST_STATIC_CAPS ("audio/x-ac3")
     );
 
 static GstStaticPadTemplate gst_rtp_ac3_depay_sink_template =
@@ -120,7 +120,7 @@ gst_rtp_ac3_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
     clock_rate = 90000;         /* default */
   depayload->clock_rate = clock_rate;
 
-  srccaps = gst_caps_new_empty_simple ("audio/ac3");
+  srccaps = gst_caps_new_empty_simple ("audio/x-ac3");
   res = gst_pad_set_caps (depayload->srcpad, srccaps);
   gst_caps_unref (srccaps);
 
diff --git a/gst/rtp/gstrtpchannels.c b/gst/rtp/gstrtpchannels.c
index 9921293fd4ceadb33117479b48beba796a488d14..d0694cb821fa04edb5ec883a4a69986a40e23bde 100644
--- a/gst/rtp/gstrtpchannels.c
+++ b/gst/rtp/gstrtpchannels.c
@@ -250,7 +250,7 @@ gst_rtp_channels_get_by_order (gint channels, const gchar * order)
     }
 
     /* compare names */
-    if (g_ascii_strcasecmp (channel_orders[i].name, order)) {
+    if (!g_ascii_strcasecmp (channel_orders[i].name, order)) {
       res = &channel_orders[i];
       break;
     }
diff --git a/gst/rtp/gstrtpelements.h b/gst/rtp/gstrtpelements.h
index 9321d3530a157d450da6acafd2b0559137c184ba..ec9663730e7e6ab447f59f97bdec880b26c6ba61 100644
--- a/gst/rtp/gstrtpelements.h
+++ b/gst/rtp/gstrtpelements.h
@@ -66,6 +66,7 @@ GST_ELEMENT_REGISTER_DECLARE (rtpmpvdepay);
 GST_ELEMENT_REGISTER_DECLARE (rtpmpvpay);
 GST_ELEMENT_REGISTER_DECLARE (rtpopusdepay);
 GST_ELEMENT_REGISTER_DECLARE (rtpopuspay);
+GST_ELEMENT_REGISTER_DECLARE (rtppassthroughpay);
 GST_ELEMENT_REGISTER_DECLARE (rtph261pay);
 GST_ELEMENT_REGISTER_DECLARE (rtph261depay);
 GST_ELEMENT_REGISTER_DECLARE (rtph263ppay);
diff --git a/gst/rtp/gstrtpgstdepay.c b/gst/rtp/gstrtpgstdepay.c
index ebf838296007510c7e9b9c5685d317e394097c7f..7a65a7b614b78e913c8b794b7177f1ba5cfa65ca 100644
--- a/gst/rtp/gstrtpgstdepay.c
+++ b/gst/rtp/gstrtpgstdepay.c
@@ -103,6 +103,9 @@ gst_rtp_gst_depay_class_init (GstRtpGSTDepayClass * klass)
 static void
 gst_rtp_gst_depay_init (GstRtpGSTDepay * rtpgstdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpgstdepay), TRUE);
+
   rtpgstdepay->adapter = gst_adapter_new ();
 }
 
@@ -409,6 +412,7 @@ gst_rtp_gst_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
   if (GST_BUFFER_IS_DISCONT (rtp->buffer)) {
     GST_WARNING_OBJECT (rtpgstdepay, "DISCONT, clear adapter");
     gst_adapter_clear (rtpgstdepay->adapter);
+    gst_rtp_base_depayload_flush (depayload, TRUE);
   }
 
   payload = gst_rtp_buffer_get_payload (rtp);
@@ -525,24 +529,28 @@ empty_packet:
   {
     GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE,
         ("Empty Payload."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 wrong_frag:
   {
     gst_adapter_clear (rtpgstdepay->adapter);
     GST_LOG_OBJECT (rtpgstdepay, "wrong fragment, skipping");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 no_caps:
   {
     GST_WARNING_OBJECT (rtpgstdepay, "failed to parse caps");
     gst_buffer_unref (outbuf);
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 no_event:
   {
     GST_WARNING_OBJECT (rtpgstdepay, "failed to parse event");
     gst_buffer_unref (outbuf);
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 missing_caps:
@@ -554,6 +562,8 @@ missing_caps:
         gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
             TRUE, 0));
 
+    gst_rtp_base_depayload_dropped (depayload);
+
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtpgstpay.c b/gst/rtp/gstrtpgstpay.c
index 5dba040bf01c931c4ccf5110d33d09375eef8a6c..13fb7d1b1b2cba2bd705ec77d40ad285de12944f 100644
--- a/gst/rtp/gstrtpgstpay.c
+++ b/gst/rtp/gstrtpgstpay.c
@@ -182,7 +182,6 @@ gst_rtp_gst_pay_reset (GstRtpGSTPay * rtpgstpay, gboolean full)
     rtpgstpay->current_CV = 0;
     rtpgstpay->next_CV = 0;
   }
-  rtpgstpay->received_buffer = FALSE;
 }
 
 static void
@@ -366,6 +365,16 @@ gst_rtp_gst_pay_create_from_adapter (GstRtpGSTPay * rtpgstpay,
   return TRUE;
 }
 
+static gboolean
+retimestamp_buffer (GstBuffer ** buffer, guint idx, gpointer user_data)
+{
+  GstClockTime *timestamp = user_data;
+
+  GST_BUFFER_PTS (*buffer) = *timestamp;
+
+  return TRUE;
+}
+
 static GstFlowReturn
 gst_rtp_gst_pay_flush (GstRtpGSTPay * rtpgstpay, GstClockTime timestamp)
 {
@@ -373,13 +382,14 @@ gst_rtp_gst_pay_flush (GstRtpGSTPay * rtpgstpay, GstClockTime timestamp)
 
   gst_rtp_gst_pay_create_from_adapter (rtpgstpay, timestamp);
 
-  if (!rtpgstpay->received_buffer) {
-    GST_DEBUG_OBJECT (rtpgstpay,
-        "Can't flush without having received a buffer yet");
-    return GST_FLOW_OK;
-  }
-
   if (rtpgstpay->pending_buffers) {
+    // make sure all buffers in the buffer list have the correct timestamp.
+    // If we created packets based on an event they would have
+    // GST_CLOCK_TIME_NONE as PTS.
+
+    gst_buffer_list_foreach (rtpgstpay->pending_buffers, retimestamp_buffer,
+        &timestamp);
+
     /* push the whole buffer list at once */
     ret = gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpgstpay),
         rtpgstpay->pending_buffers);
@@ -529,6 +539,11 @@ gst_rtp_gst_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
     g_atomic_int_set (&rtpgstpay->force_config, TRUE);
   }
 
+  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+    // We must flush at this point, as no next input frame is expected
+    gst_rtp_gst_pay_flush (rtpgstpay, GST_CLOCK_TIME_NONE);
+  }
+
   ret =
       GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload,
       gst_event_ref (event));
@@ -584,12 +599,10 @@ gst_rtp_gst_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
     GST_DEBUG_OBJECT (rtpgstpay, "make event type %d for %s",
         etype, GST_EVENT_TYPE_NAME (event));
     gst_rtp_gst_pay_send_event (rtpgstpay, etype, event);
-    /* Do not send stream-start right away since caps/new-segment were not yet
-       sent, so our data would be considered invalid */
-    if (etype != 4) {
-      /* flush the adapter immediately */
-      gst_rtp_gst_pay_flush (rtpgstpay, GST_CLOCK_TIME_NONE);
-    }
+    // do not flush events here yet as they would get no timestamp at all or
+    // the timestamp of the previous buffer, both of which are bogus. We need
+    // to wait until the next actual input frame to know the timestamp that
+    // applies to the event.
   }
 
   gst_event_unref (event);
@@ -654,8 +667,6 @@ gst_rtp_gst_pay_handle_buffer (GstRTPBasePayload * basepayload,
 
   rtpgstpay = GST_RTP_GST_PAY (basepayload);
 
-  rtpgstpay->received_buffer = TRUE;
-
   timestamp = GST_BUFFER_PTS (buffer);
   running_time =
       gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
diff --git a/gst/rtp/gstrtpgstpay.h b/gst/rtp/gstrtpgstpay.h
index 671186bf703dd0015dc7555abe0776d9a5da4461..3db3d61694eb8f2b5bbbb6964bbcda88bcc1756c 100644
--- a/gst/rtp/gstrtpgstpay.h
+++ b/gst/rtp/gstrtpgstpay.h
@@ -57,8 +57,6 @@ struct _GstRtpGSTPay
   guint config_interval;
   GstClockTime last_config;
   gboolean force_config;
-
-  gboolean received_buffer;
 };
 
 struct _GstRtpGSTPayClass
diff --git a/gst/rtp/gstrtph261depay.c b/gst/rtp/gstrtph261depay.c
index 23a888a4eacd176823ce11f8b496165549485296..0dd7a0a853c449384fad47414fba452275e084a6 100644
--- a/gst/rtp/gstrtph261depay.c
+++ b/gst/rtp/gstrtph261depay.c
@@ -110,6 +110,7 @@ gst_rtp_h261_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
   if (payload_len < header_len + 1) {
     /* Must have at least one byte payload */
     GST_WARNING_OBJECT (depay, "Dropping packet with invalid payload length");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 
@@ -286,4 +287,7 @@ gst_rtp_h261_depay_init (GstRtpH261Depay * depay)
 {
   depay->adapter = gst_adapter_new ();
   depay->leftover = NO_LEFTOVER;
+
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (depay), TRUE);
 }
diff --git a/gst/rtp/gstrtph263depay.c b/gst/rtp/gstrtph263depay.c
index f6b41a5bc9d4c334a550cf488436225aeaae9f4e..ba4701c623035d323b2500faf0c0a126ce92e512 100644
--- a/gst/rtp/gstrtph263depay.c
+++ b/gst/rtp/gstrtph263depay.c
@@ -126,6 +126,9 @@ gst_rtp_h263_depay_init (GstRtpH263Depay * rtph263depay)
 
   rtph263depay->offset = 0;
   rtph263depay->leftover = 0;
+
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtph263depay), TRUE);
 }
 
 static void
@@ -318,7 +321,7 @@ gst_rtp_h263_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
     if (payload_len > 4 && (GST_READ_UINT32_BE (payload) >> 10 == 0x20)) {
       GST_DEBUG ("Mode %c with PSC => frame start", "ABC"[F + P]);
       rtph263depay->start = TRUE;
-      if ((! !(payload[4] & 0x02)) != I) {
+      if ((!!(payload[4] & 0x02)) != I) {
         GST_DEBUG ("Wrong Picture Coding Type Flag in rtp header");
         I = !I;
       }
@@ -407,6 +410,7 @@ too_small:
   {
     GST_ELEMENT_WARNING (rtph263depay, STREAM, DECODE,
         ("Packet payload was too small"), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtph263pdepay.c b/gst/rtp/gstrtph263pdepay.c
index 8b371ba92f70d159dc550b0389bf2ea02dc6ce3a..bee74f8822806aa8fa6a3392ce00b60dacb58bb2 100644
--- a/gst/rtp/gstrtph263pdepay.c
+++ b/gst/rtp/gstrtph263pdepay.c
@@ -134,6 +134,9 @@ static void
 gst_rtp_h263p_depay_init (GstRtpH263PDepay * rtph263pdepay)
 {
   rtph263pdepay->adapter = gst_adapter_new ();
+
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtph263pdepay), TRUE);
 }
 
 static void
@@ -447,16 +450,19 @@ too_small:
   {
     GST_ELEMENT_WARNING (rtph263pdepay, STREAM, DECODE,
         ("Packet payload was too small"), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 waiting_start:
   {
     GST_DEBUG_OBJECT (rtph263pdepay, "waiting for picture start");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 empty_frame:
   {
     GST_WARNING_OBJECT (rtph263pdepay, "Depayloaded frame is empty, dropping");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c
index 9cef347c21f4b4a0a69c0f73a7b9cc050fa85992..008631cb4b6a903fafa1fe4ad6f260232aca5281 100644
--- a/gst/rtp/gstrtph264depay.c
+++ b/gst/rtp/gstrtph264depay.c
@@ -213,6 +213,9 @@ gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass)
 static void
 gst_rtp_h264_depay_init (GstRtpH264Depay * rtph264depay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtph264depay), TRUE);
+
   rtph264depay->adapter = gst_adapter_new ();
   rtph264depay->picture_adapter = gst_adapter_new ();
   rtph264depay->byte_stream = DEFAULT_BYTE_STREAM;
@@ -646,6 +649,9 @@ gst_rtp_h264_add_sps_pps (GstElement * rtph264, GPtrArray * sps_array,
 
   gst_buffer_map (nal, &map, GST_MAP_READ);
 
+  if (map.size == 0)
+    goto drop;
+
   type = map.data[0] & 0x1f;
 
   if (type == 7) {
@@ -788,7 +794,12 @@ gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
      * front of the params. */
     len = 0;
     for (i = 0; params[i]; i++) {
-      len += strlen (params[i]);
+      gsize nal_len = strlen (params[i]);
+      if (nal_len == 0) {
+        GST_WARNING_OBJECT (depayload, "empty param (#%d)", i);
+        continue;
+      }
+      len += nal_len;
       len += sizeof (sync_bytes);
     }
     /* we seriously overshoot the length, but it's fine. */
@@ -800,13 +811,20 @@ gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
     for (i = 0; params[i]; i++) {
       guint save = 0;
       gint state = 0;
+      gsize nal_len = strlen (params[i]);
+
+      if (nal_len == 0)
+        continue;
 
       GST_DEBUG_OBJECT (depayload, "decoding param %d (%s)", i, params[i]);
       memcpy (ptr, sync_bytes, sizeof (sync_bytes));
       ptr += sizeof (sync_bytes);
-      len =
-          g_base64_decode_step (params[i], strlen (params[i]), ptr, &state,
-          &save);
+      len = g_base64_decode_step (params[i], nal_len, ptr, &state, &save);
+      if (len == 0) {
+        GST_WARNING_OBJECT (depayload, "failed decoding param %d", i);
+        ptr -= sizeof (sync_bytes);
+        continue;
+      }
       GST_DEBUG_OBJECT (depayload, "decoded %d bytes", len);
       total += len + sizeof (sync_bytes);
       ptr += len;
@@ -815,12 +833,16 @@ gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
     gst_buffer_resize (codec_data, 0, total);
     g_strfreev (params);
 
-    /* keep the codec_data, we need to send it as the first buffer. We cannot
-     * push it in the adapter because the adapter might be flushed on discont.
-     */
-    if (rtph264depay->codec_data)
-      gst_buffer_unref (rtph264depay->codec_data);
-    rtph264depay->codec_data = codec_data;
+    if (total > 0) {
+      /* keep the codec_data, we need to send it as the first buffer. We cannot
+       * push it in the adapter because the adapter might be flushed on discont.
+       */
+      if (rtph264depay->codec_data)
+        gst_buffer_unref (rtph264depay->codec_data);
+      rtph264depay->codec_data = codec_data;
+    } else {
+      gst_buffer_unref (codec_data);
+    }
   } else if (!rtph264depay->byte_stream) {
     gchar **params;
     gint i;
@@ -842,7 +864,7 @@ gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
 
       nal_len = strlen (params[i]);
       if (nal_len == 0) {
-        GST_WARNING_OBJECT (depayload, "empty param '%s' (#%d)", params[i], i);
+        GST_WARNING_OBJECT (depayload, "empty param (#%d)", i);
         continue;
       }
       nal = gst_buffer_new_and_alloc (nal_len);
@@ -851,13 +873,20 @@ gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
       nal_len =
           g_base64_decode_step (params[i], nal_len, nalmap.data, &state, &save);
 
-      GST_DEBUG_OBJECT (depayload, "adding param %d as %s", i,
-          ((nalmap.data[0] & 0x1f) == 7) ? "SPS" : "PPS");
+      if (nal_len > 0) {
+        GST_DEBUG_OBJECT (depayload, "adding param %d as %s", i,
+            ((nalmap.data[0] & 0x1f) == 7) ? "SPS" : "PPS");
+      }
 
       gst_buffer_unmap (nal, &nalmap);
       gst_buffer_set_size (nal, nal_len);
 
-      gst_rtp_h264_depay_add_sps_pps (rtph264depay, nal);
+      if (nal_len > 0) {
+        gst_rtp_h264_depay_add_sps_pps (rtph264depay, nal);
+      } else {
+        GST_WARNING_OBJECT (depayload, "failed decoding param %d", i);
+        gst_buffer_unref (nal);
+      }
     }
     g_strfreev (params);
 
@@ -1096,6 +1125,7 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
       GST_LOG_OBJECT (depayload,
           "Dropping %" GST_PTR_FORMAT ", we are waiting for a keyframe",
           outbuf);
+      gst_rtp_base_depayload_flush (depayload, FALSE);
       gst_buffer_unref (outbuf);
     }
   }
@@ -1214,8 +1244,10 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
      * type.  Assume that the remote payloader is buggy (didn't set the end bit
      * when the FU ended) and send out what we gathered thusfar */
     if (G_UNLIKELY (rtph264depay->current_fu_type != 0 &&
-            nal_unit_type != rtph264depay->current_fu_type))
+            nal_unit_type != rtph264depay->current_fu_type)) {
+      gst_rtp_base_depayload_delayed (depayload);
       gst_rtp_h264_finish_fragmentation_unit (rtph264depay);
+    }
 
     switch (nal_unit_type) {
       case 0:
@@ -1324,8 +1356,10 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
           /* If a new FU unit started, while still processing an older one.
            * Assume that the remote payloader is buggy (doesn't set the end
            * bit) and send out what we've gathered thusfar */
-          if (G_UNLIKELY (rtph264depay->current_fu_type != 0))
+          if (G_UNLIKELY (rtph264depay->current_fu_type != 0)) {
+            gst_rtp_base_depayload_delayed (depayload);
             gst_rtp_h264_finish_fragmentation_unit (rtph264depay);
+          }
 
           rtph264depay->current_fu_type = nal_unit_type;
           rtph264depay->fu_timestamp = timestamp;
@@ -1361,6 +1395,7 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
             /* previous FU packet missing start bit? */
             GST_WARNING_OBJECT (rtph264depay, "missing FU start bit on an "
                 "earlier packet. Dropping.");
+            gst_rtp_base_depayload_flush (depayload, FALSE);
             gst_adapter_clear (rtph264depay->adapter);
             return NULL;
           }
@@ -1371,6 +1406,7 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
                 "%u to %u within Fragmentation Unit. Data was lost, dropping "
                 "stored.", rtph264depay->last_fu_seqnum,
                 gst_rtp_buffer_get_seq (rtp));
+            gst_rtp_base_depayload_flush (depayload, FALSE);
             gst_adapter_clear (rtph264depay->adapter);
             return NULL;
           }
@@ -1435,23 +1471,27 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
 empty_packet:
   {
     GST_DEBUG_OBJECT (rtph264depay, "empty packet");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 undefined_type:
   {
     GST_ELEMENT_WARNING (rtph264depay, STREAM, DECODE,
         (NULL), ("Undefined packet type"));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 waiting_start:
   {
     GST_DEBUG_OBJECT (rtph264depay, "waiting for start");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 not_implemented:
   {
     GST_ELEMENT_ERROR (rtph264depay, STREAM, FORMAT,
         (NULL), ("NAL unit type %d not supported yet", nal_unit_type));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c
index de476e64e934d4d27240fee466d5ab6f2b688a72..3d51076bc84f752d241385c75a65921dcbc1824f 100644
--- a/gst/rtp/gstrtph264pay.c
+++ b/gst/rtp/gstrtph264pay.c
@@ -924,6 +924,7 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload,
 {
   GstRtpH264Pay *rtph264pay;
   guint8 nal_header, nal_type;
+  gboolean first_slice = FALSE;
   gboolean send_spspps;
   guint size;
 
@@ -960,8 +961,17 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload,
 
   send_spspps = FALSE;
 
+  if (nal_type == IDR_TYPE_ID) {
+    guint8 first_mb_in_slice;
+    gst_buffer_extract (paybuf, 1, &first_mb_in_slice, 1);
+    /* 'first_mb_in_slice' specifies the address of the first macroblock
+     * in the slice. if 'first_mb_in_slice' is 0 (note that it's exp golomb
+     * code), the current slice is the first slice of the frame */
+    first_slice = ((first_mb_in_slice >> 7) & 0x01) == 1;
+  }
+
   /* check if we need to emit an SPS/PPS now */
-  if (nal_type == IDR_TYPE_ID && rtph264pay->spspps_interval > 0) {
+  if (first_slice && nal_type == IDR_TYPE_ID && rtph264pay->spspps_interval > 0) {
     if (rtph264pay->last_spspps != -1) {
       guint64 diff;
       GstClockTime running_time =
@@ -993,7 +1003,8 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload,
       GST_DEBUG_OBJECT (rtph264pay, "no previous SPS/PPS time, send now");
       send_spspps = TRUE;
     }
-  } else if (nal_type == IDR_TYPE_ID && rtph264pay->spspps_interval == -1) {
+  } else if (first_slice && nal_type == IDR_TYPE_ID
+      && rtph264pay->spspps_interval == -1) {
     GST_DEBUG_OBJECT (rtph264pay, "sending SPS/PPS before current IDR frame");
     /* send SPS/PPS before every IDR frame */
     send_spspps = TRUE;
@@ -1254,7 +1265,7 @@ gst_rtp_h264_pay_send_bundle (GstRtpH264Pay * rtph264pay, gboolean end_of_au)
       end_of_au, delta, discont);
 }
 
-static gboolean
+static GstFlowReturn
 gst_rtp_h264_pay_payload_nal_bundle (GstRTPBasePayload * basepayload,
     GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au,
     gboolean delta_unit, gboolean discont, guint8 nal_header)
diff --git a/gst/rtp/gstrtph265depay.c b/gst/rtp/gstrtph265depay.c
index 41d2762ffeabb8832a0c743e2417a752d36f4111..4aba961b790b7b7187c034a5f88cbb87bf8efd24 100644
--- a/gst/rtp/gstrtph265depay.c
+++ b/gst/rtp/gstrtph265depay.c
@@ -152,6 +152,9 @@ gst_rtp_h265_depay_class_init (GstRtpH265DepayClass * klass)
 static void
 gst_rtp_h265_depay_init (GstRtpH265Depay * rtph265depay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtph265depay), TRUE);
+
   rtph265depay->adapter = gst_adapter_new ();
   rtph265depay->picture_adapter = gst_adapter_new ();
   rtph265depay->output_format = DEFAULT_STREAM_FORMAT;
@@ -674,9 +677,18 @@ gst_rtp_h265_add_vps_sps_pps (GstElement * rtph265, GPtrArray * vps_array,
 
   gst_buffer_map (nal, &map, GST_MAP_READ);
 
+  if (map.size == 0)
+    goto drop;
+
   type = (map.data[0] >> 1) & 0x3f;
 
   if (type == GST_H265_VPS_NUT) {
+    if (map.size < 3) {
+      GST_WARNING_OBJECT (rtph265, "Invalid VPS,"
+          " can't parse seq_parameter_set_id");
+      goto drop;
+    }
+
     guint32 vps_id = (map.data[2] >> 4) & 0x0f;
 
     for (i = 0; i < vps_array->len; i++) {
@@ -852,7 +864,12 @@ gst_rtp_h265_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
      * front of the params. */
     len = 0;
     for (i = 0; params[i]; i++) {
-      len += strlen (params[i]);
+      gsize nal_len = strlen (params[i]);
+      if (nal_len == 0) {
+        GST_WARNING_OBJECT (depayload, "empty param (#%d)", i);
+        continue;
+      }
+      len += nal_len;
       len += sizeof (sync_bytes);
     }
     /* we seriously overshoot the length, but it's fine. */
@@ -864,13 +881,20 @@ gst_rtp_h265_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
     for (i = 0; params[i]; i++) {
       guint save = 0;
       gint state = 0;
+      gsize nal_len = strlen (params[i]);
+
+      if (nal_len == 0)
+        continue;
 
       GST_DEBUG_OBJECT (depayload, "decoding param %d (%s)", i, params[i]);
       memcpy (ptr, sync_bytes, sizeof (sync_bytes));
       ptr += sizeof (sync_bytes);
-      len =
-          g_base64_decode_step (params[i], strlen (params[i]), ptr, &state,
-          &save);
+      len = g_base64_decode_step (params[i], nal_len, ptr, &state, &save);
+      if (len == 0) {
+        GST_WARNING_OBJECT (depayload, "failed decoding param %d", i);
+        ptr -= sizeof (sync_bytes);
+        continue;
+      }
       GST_DEBUG_OBJECT (depayload, "decoded %d bytes", len);
       total += len + sizeof (sync_bytes);
       ptr += len;
@@ -879,12 +903,16 @@ gst_rtp_h265_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
     gst_buffer_resize (codec_data, 0, total);
     g_strfreev (params);
 
-    /* keep the codec_data, we need to send it as the first buffer. We cannot
-     * push it in the adapter because the adapter might be flushed on discont.
-     */
-    if (rtph265depay->codec_data)
-      gst_buffer_unref (rtph265depay->codec_data);
-    rtph265depay->codec_data = codec_data;
+    if (total > 0) {
+      /* keep the codec_data, we need to send it as the first buffer. We cannot
+       * push it in the adapter because the adapter might be flushed on discont.
+       */
+      if (rtph265depay->codec_data)
+        gst_buffer_unref (rtph265depay->codec_data);
+      rtph265depay->codec_data = codec_data;
+    } else {
+      gst_buffer_unref (codec_data);
+    }
   } else if (!rtph265depay->byte_stream) {
     gchar **params;
     gint i;
@@ -906,7 +934,7 @@ gst_rtp_h265_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
 
       nal_len = strlen (params[i]);
       if (nal_len == 0) {
-        GST_WARNING_OBJECT (depayload, "empty param '%s' (#%d)", params[i], i);
+        GST_WARNING_OBJECT (depayload, "empty param (#%d)", i);
         continue;
       }
       nal = gst_buffer_new_and_alloc (nal_len);
@@ -915,15 +943,22 @@ gst_rtp_h265_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
       nal_len =
           g_base64_decode_step (params[i], nal_len, nalmap.data, &state, &save);
 
-      GST_DEBUG_OBJECT (depayload, "adding param %d as %s", i,
-          (((nalmap.data[0] >> 1) & 0x3f) ==
-              32) ? "VPS" : (((nalmap.data[0] >> 1) & 0x3f) ==
-              33) ? "SPS" : "PPS");
+      if (nal_len > 0) {
+        GST_DEBUG_OBJECT (depayload, "adding param %d as %s", i,
+            (((nalmap.data[0] >> 1) & 0x3f) ==
+                32) ? "VPS" : (((nalmap.data[0] >> 1) & 0x3f) ==
+                33) ? "SPS" : "PPS");
+      }
 
       gst_buffer_unmap (nal, &nalmap);
       gst_buffer_set_size (nal, nal_len);
 
-      gst_rtp_h265_depay_add_vps_sps_pps (rtph265depay, nal);
+      if (nal_len > 0) {
+        gst_rtp_h265_depay_add_vps_sps_pps (rtph265depay, nal);
+      } else {
+        GST_WARNING_OBJECT (depayload, "failed decoding param %d", i);
+        gst_buffer_unref (nal);
+      }
     }
     g_strfreev (params);
 
@@ -1293,8 +1328,10 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
      * type.  Assume that the remote payloader is buggy (didn't set the end bit
      * when the FU ended) and send out what we gathered thusfar */
     if (G_UNLIKELY (rtph265depay->current_fu_type != 0 &&
-            nal_unit_type != rtph265depay->current_fu_type))
+            nal_unit_type != rtph265depay->current_fu_type)) {
+      gst_rtp_base_depayload_delayed (depayload);
       gst_rtp_h265_finish_fragmentation_unit (rtph265depay);
+    }
 
     switch (nal_unit_type) {
       case 48:
@@ -1428,8 +1465,10 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
           /* If a new FU unit started, while still processing an older one.
            * Assume that the remote payloader is buggy (doesn't set the end
            * bit) and send out what we've gathered thusfar */
-          if (G_UNLIKELY (rtph265depay->current_fu_type != 0))
+          if (G_UNLIKELY (rtph265depay->current_fu_type != 0)) {
+            gst_rtp_base_depayload_delayed (depayload);
             gst_rtp_h265_finish_fragmentation_unit (rtph265depay);
+          }
 
           rtph265depay->current_fu_type = nal_unit_type;
           rtph265depay->fu_timestamp = timestamp;
@@ -1475,6 +1514,7 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
             /* previous FU packet missing start bit? */
             GST_WARNING_OBJECT (rtph265depay, "missing FU start bit on an "
                 "earlier packet. Dropping.");
+            gst_rtp_base_depayload_flush (depayload, FALSE);
             gst_adapter_clear (rtph265depay->adapter);
             return NULL;
           }
@@ -1485,6 +1525,7 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
                 "%u to %u within Fragmentation Unit. Data was lost, dropping "
                 "stored.", rtph265depay->last_fu_seqnum,
                 gst_rtp_buffer_get_seq (rtp));
+            gst_rtp_base_depayload_flush (depayload, FALSE);
             gst_adapter_clear (rtph265depay->adapter);
             return NULL;
           }
@@ -1560,11 +1601,13 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
 empty_packet:
   {
     GST_DEBUG_OBJECT (rtph265depay, "empty packet");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 waiting_start:
   {
     GST_DEBUG_OBJECT (rtph265depay, "waiting for start");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 #if 0
@@ -1572,6 +1615,7 @@ not_implemented_donl_present:
   {
     GST_ELEMENT_ERROR (rtph265depay, STREAM, FORMAT,
         (NULL), ("DONL field present not supported yet"));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 #endif
@@ -1579,6 +1623,7 @@ not_implemented:
   {
     GST_ELEMENT_ERROR (rtph265depay, STREAM, FORMAT,
         (NULL), ("NAL unit type %d not supported yet", nal_unit_type));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtph265pay.c b/gst/rtp/gstrtph265pay.c
index 82b68cce9f34b2664efe53b8b0255f1de377b70e..5a08f8bbe2c2d3ec83d2279d942158808ae0810c 100644
--- a/gst/rtp/gstrtph265pay.c
+++ b/gst/rtp/gstrtph265pay.c
@@ -922,8 +922,7 @@ gst_rtp_h265_pay_decode_nal (GstRtpH265Pay * payloader,
 }
 
 static GstFlowReturn gst_rtp_h265_pay_payload_nal (GstRTPBasePayload *
-    basepayload, GPtrArray * paybufs, GstClockTime dts, GstClockTime pts,
-    gboolean delta_unit);
+    basepayload, GPtrArray * paybufs, GstClockTime dts, GstClockTime pts);
 static GstFlowReturn gst_rtp_h265_pay_payload_nal_single (GstRTPBasePayload *
     basepayload, GstBuffer * paybuf, GstClockTime dts, GstClockTime pts,
     gboolean marker, gboolean delta_unit);
@@ -970,7 +969,7 @@ gst_rtp_h265_pay_send_vps_sps_pps (GstRTPBasePayload * basepayload,
     g_ptr_array_add (bufs, gst_buffer_ref (pps_buf));
   }
 
-  ret = gst_rtp_h265_pay_payload_nal (basepayload, bufs, dts, pts, FALSE);
+  ret = gst_rtp_h265_pay_payload_nal (basepayload, bufs, dts, pts);
   if (ret != GST_FLOW_OK) {
     /* not critical but warn */
     GST_WARNING_OBJECT (basepayload, "failed pushing VPS/SPS/PPS");
@@ -996,8 +995,7 @@ gst_rtp_h265_pay_reset_bundle (GstRtpH265Pay * rtph265pay)
 
 static GstFlowReturn
 gst_rtp_h265_pay_payload_nal (GstRTPBasePayload * basepayload,
-    GPtrArray * paybufs, GstClockTime dts, GstClockTime pts,
-    gboolean delta_unit)
+    GPtrArray * paybufs, GstClockTime dts, GstClockTime pts)
 {
   GstRtpH265Pay *rtph265pay;
   guint mtu;
@@ -1023,6 +1021,7 @@ gst_rtp_h265_pay_payload_nal (GstRTPBasePayload * basepayload,
     gboolean send_ps;
     guint size;
     gboolean marker;
+    gboolean delta_unit;
 
     paybuf = g_ptr_array_index (paybufs, i);
 
@@ -1033,6 +1032,7 @@ gst_rtp_h265_pay_payload_nal (GstRTPBasePayload * basepayload,
     }
 
     marker = GST_BUFFER_FLAG_IS_SET (paybuf, GST_BUFFER_FLAG_MARKER);
+    delta_unit = GST_BUFFER_FLAG_IS_SET (paybuf, GST_BUFFER_FLAG_DELTA_UNIT);
 
     size = gst_buffer_get_size (paybuf);
     gst_buffer_extract (paybuf, 0, nal_header, 2);
@@ -1351,7 +1351,7 @@ gst_rtp_h265_pay_send_bundle (GstRtpH265Pay * rtph265pay, gboolean marker)
       marker, delta_unit);
 }
 
-static gboolean
+static GstFlowReturn
 gst_rtp_h265_pay_payload_nal_bundle (GstRTPBasePayload * basepayload,
     GstBuffer * paybuf, GstClockTime dts, GstClockTime pts,
     gboolean marker, gboolean delta_unit, guint8 nal_type,
@@ -1583,6 +1583,14 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload,
         discont = FALSE;
       }
 
+      GST_BUFFER_FLAG_SET (paybuf, GST_BUFFER_FLAG_DELTA_UNIT);
+      if (!rtph265pay->delta_unit)
+        GST_BUFFER_FLAG_UNSET (paybuf, GST_BUFFER_FLAG_DELTA_UNIT);
+
+      if (!rtph265pay->delta_unit)
+        /* only the first outgoing packet doesn't have the DELTA_UNIT flag */
+        rtph265pay->delta_unit = TRUE;
+
       /* Skip current nal. If it is split over multiple GstMemory
        * advance_bytes () will switch to the correct GstMemory. The payloader
        * does not access those bytes directly but uses gst_buffer_copy_region ()
@@ -1592,13 +1600,7 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload,
       offset += nal_len;
       remaining_buffer_size -= nal_len;
     }
-    ret =
-        gst_rtp_h265_pay_payload_nal (basepayload, paybufs, dts, pts,
-        rtph265pay->delta_unit);
-
-    if (!rtph265pay->delta_unit)
-      /* only the first outgoing packet doesn't have the DELTA_UNIT flag */
-      rtph265pay->delta_unit = TRUE;
+    ret = gst_rtp_h265_pay_payload_nal (basepayload, paybufs, dts, pts);
 
     gst_buffer_memory_unmap (&memory);
     gst_buffer_unref (buffer);
@@ -1713,6 +1715,10 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload,
         discont = FALSE;
       }
 
+      GST_BUFFER_FLAG_SET (paybuf, GST_BUFFER_FLAG_DELTA_UNIT);
+      if (!rtph265pay->delta_unit)
+        GST_BUFFER_FLAG_UNSET (paybuf, GST_BUFFER_FLAG_DELTA_UNIT);
+
       if (delayed_not_delta_unit) {
         rtph265pay->delta_unit = FALSE;
         delayed_not_delta_unit = FALSE;
@@ -1726,9 +1732,7 @@ gst_rtp_h265_pay_handle_buffer (GstRTPBasePayload * basepayload,
       gst_adapter_flush (rtph265pay->adapter, nal_len - size);
     }
     /* put the data in one or more RTP packets */
-    ret =
-        gst_rtp_h265_pay_payload_nal (basepayload, paybufs, dts, pts,
-        rtph265pay->delta_unit);
+    ret = gst_rtp_h265_pay_payload_nal (basepayload, paybufs, dts, pts);
     g_array_set_size (nal_queue, 0);
   }
 
diff --git a/gst/rtp/gstrtpj2kdepay.c b/gst/rtp/gstrtpj2kdepay.c
index 4456b3dd893dab33370d0554e8f2db63e0cc51da..17ad6f510324528de75fdca59704d3d4790f9593 100644
--- a/gst/rtp/gstrtpj2kdepay.c
+++ b/gst/rtp/gstrtpj2kdepay.c
@@ -132,6 +132,9 @@ gst_rtp_j2k_depay_class_init (GstRtpJ2KDepayClass * klass)
 static void
 gst_rtp_j2k_depay_init (GstRtpJ2KDepay * rtpj2kdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpj2kdepay), TRUE);
+
   rtpj2kdepay->pu_adapter = gst_adapter_new ();
   rtpj2kdepay->t_adapter = gst_adapter_new ();
   rtpj2kdepay->f_adapter = gst_adapter_new ();
@@ -440,6 +443,7 @@ gst_rtp_j2k_depay_flush_frame (GstRTPBaseDepayload * depayload)
   } else {
     GST_WARNING_OBJECT (rtpj2kdepay, "empty packet");
     gst_adapter_clear (rtpj2kdepay->f_adapter);
+    gst_rtp_base_depayload_flush (depayload, TRUE);
   }
 
   /* we accept any mh_id now */
@@ -597,6 +601,7 @@ empty_packet:
   {
     GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE,
         ("Empty Payload."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 wrong_mh_id:
@@ -604,6 +609,7 @@ wrong_mh_id:
     GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE,
         ("Invalid mh_id %u, expected %u", mh_id, rtpj2kdepay->last_mh_id),
         (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     gst_rtp_j2k_depay_clear_pu (rtpj2kdepay);
     return NULL;
   }
diff --git a/gst/rtp/gstrtpjpegdepay.c b/gst/rtp/gstrtpjpegdepay.c
index 02209d53b7431bd86b85744d2bc7fc150e80be3d..a5f44ed82b4e0f80ef8107b7869a8786342ba6fc 100644
--- a/gst/rtp/gstrtpjpegdepay.c
+++ b/gst/rtp/gstrtpjpegdepay.c
@@ -119,6 +119,9 @@ gst_rtp_jpeg_depay_class_init (GstRtpJPEGDepayClass * klass)
 static void
 gst_rtp_jpeg_depay_init (GstRtpJPEGDepay * rtpjpegdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpjpegdepay), TRUE);
+
   rtpjpegdepay->adapter = gst_adapter_new ();
 }
 
@@ -705,7 +708,7 @@ gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
      * marker */
     gst_adapter_copy (rtpjpegdepay->adapter, end, avail - 2, 2);
 
-    if (end[0] != 0xff && end[1] != 0xd9) {
+    if (GST_READ_UINT16_BE (end) != 0xffd9) {
       GST_DEBUG_OBJECT (rtpjpegdepay, "no EOI marker, adding one");
 
       /* no EOI marker, add one */
@@ -737,17 +740,20 @@ empty_packet:
   {
     GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE,
         ("Empty Payload."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 invalid_dimension:
   {
     GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT,
         ("Invalid Dimension %dx%d.", width, height), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 no_qtable:
   {
     GST_WARNING_OBJECT (rtpjpegdepay, "no qtable");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 invalid_packet:
@@ -755,12 +761,14 @@ invalid_packet:
     GST_WARNING_OBJECT (rtpjpegdepay, "invalid packet");
     gst_adapter_flush (rtpjpegdepay->adapter,
         gst_adapter_available (rtpjpegdepay->adapter));
+    gst_rtp_base_depayload_flush (depayload, TRUE);
     return NULL;
   }
 no_header_packet:
   {
     GST_WARNING_OBJECT (rtpjpegdepay,
         "discarding data packets received when we have no header");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtpklvdepay.c b/gst/rtp/gstrtpklvdepay.c
index 1cb8bc3ab40038f48a92a0ed7641adbc8eb9ea4d..0d290287eb4f2b595f2055304e67e11a3283cf20 100644
--- a/gst/rtp/gstrtpklvdepay.c
+++ b/gst/rtp/gstrtpklvdepay.c
@@ -108,6 +108,9 @@ gst_rtp_klv_depay_class_init (GstRtpKlvDepayClass * klass)
 static void
 gst_rtp_klv_depay_init (GstRtpKlvDepay * klvdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (klvdepay), TRUE);
+
   klvdepay->adapter = gst_adapter_new ();
 }
 
@@ -261,6 +264,7 @@ gst_rtp_klv_depay_process_data (GstRtpKlvDepay * klvdepay)
 bad_klv_packet:
   {
     GST_WARNING_OBJECT (klvdepay, "bad KLV packet, dropping");
+    gst_rtp_base_depayload_flush (GST_RTP_BASE_DEPAYLOAD (klvdepay), TRUE);
     gst_rtp_klv_depay_reset (klvdepay);
     return NULL;
   }
@@ -330,8 +334,8 @@ gst_rtp_klv_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
     }
   }
 
-  /* If this is the first packet and looks like a start, clear resync flag */
-  if (klvdepay->resync && klvdepay->last_marker_seq == -1 && start)
+  /* If this looks like a start, clear the resync flag */
+  if (klvdepay->resync && start)
     klvdepay->resync = FALSE;
 
   if (marker)
@@ -357,6 +361,8 @@ gst_rtp_klv_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
 
   if (marker)
     outbuf = gst_rtp_klv_depay_process_data (klvdepay);
+  else if (outbuf)
+    gst_rtp_base_depayload_delayed (depayload);
 
 done:
 
diff --git a/gst/rtp/gstrtpmp4adepay.c b/gst/rtp/gstrtpmp4adepay.c
index f278fc598c5591fd668faf9f2146fba8bc198681..fc5e603033582408dcbfcd034013164094427374 100644
--- a/gst/rtp/gstrtpmp4adepay.c
+++ b/gst/rtp/gstrtpmp4adepay.c
@@ -111,6 +111,9 @@ gst_rtp_mp4a_depay_class_init (GstRtpMP4ADepayClass * klass)
 static void
 gst_rtp_mp4a_depay_init (GstRtpMP4ADepay * rtpmp4adepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpmp4adepay), TRUE);
+
   rtpmp4adepay->adapter = gst_adapter_new ();
   rtpmp4adepay->framed = FALSE;
 }
@@ -306,6 +309,7 @@ gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
   GstRtpMP4ADepay *rtpmp4adepay;
   GstBuffer *outbuf;
   GstMapInfo map;
+  GstBufferList *outbufs = NULL;
 
   rtpmp4adepay = GST_RTP_MP4A_DEPAY (depayload);
 
@@ -347,9 +351,11 @@ gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
     guint8 *data;
     guint pos;
     GstClockTime timestamp;
+    guint64 samples_consumed;
 
     avail = gst_adapter_available (rtpmp4adepay->adapter);
     timestamp = gst_adapter_prev_pts (rtpmp4adepay->adapter, NULL);
+    samples_consumed = 0;
 
     GST_LOG_OBJECT (rtpmp4adepay, "have marker and %u available", avail);
 
@@ -359,6 +365,7 @@ gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
     /* position in data we are at */
     pos = 0;
 
+    outbufs = gst_buffer_list_new_sized (rtpmp4adepay->numSubFrames);
     /* looping through the number of sub-frames in the audio payload */
     for (i = 0; i <= rtpmp4adepay->numSubFrames; i++) {
       /* determine payload length and set buffer data pointer accordingly */
@@ -397,18 +404,30 @@ gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
       avail -= skip;
 
       GST_BUFFER_PTS (tmp) = timestamp;
-      gst_rtp_drop_non_audio_meta (depayload, tmp);
-      gst_rtp_base_depayload_push (depayload, tmp);
 
-      /* shift ts for next buffers */
-      if (rtpmp4adepay->frame_len && timestamp != -1
-          && depayload->clock_rate != 0) {
-        timestamp +=
-            gst_util_uint64_scale_int (rtpmp4adepay->frame_len, GST_SECOND,
+      if (timestamp != -1 && depayload->clock_rate != 0) {
+        GST_BUFFER_PTS (tmp) +=
+            gst_util_uint64_scale_int (samples_consumed, GST_SECOND,
             depayload->clock_rate);
+
+        /* shift ts for next buffers */
+        if (rtpmp4adepay->frame_len) {
+          samples_consumed += rtpmp4adepay->frame_len;
+
+          GstClockTime next_timestamp =
+              timestamp + gst_util_uint64_scale_int (samples_consumed,
+              GST_SECOND, depayload->clock_rate);
+          GST_BUFFER_DURATION (tmp) = next_timestamp - GST_BUFFER_PTS (tmp);
+        }
       }
+
+      gst_rtp_drop_non_audio_meta (depayload, tmp);
+      gst_buffer_list_add (outbufs, tmp);
     }
 
+    /* now push all sub-frames we found */
+    gst_rtp_base_depayload_push_list (depayload, outbufs);
+
     /* just a check that lengths match */
     if (avail) {
       GST_ELEMENT_WARNING (depayload, STREAM, DECODE,
@@ -416,9 +435,9 @@ gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
               "possible wrongly encoded packet."));
     }
 
-    gst_buffer_unmap (outbuf, &map);
-    gst_buffer_unref (outbuf);
+    goto out;
   }
+
   return NULL;
 
   /* ERRORS */
@@ -426,6 +445,15 @@ wrong_size:
   {
     GST_ELEMENT_WARNING (rtpmp4adepay, STREAM, DECODE,
         ("Packet did not validate"), ("wrong packet size"));
+    /* push what we have so far */
+    gst_rtp_base_depayload_push_list (depayload, outbufs);
+  }
+
+out:
+  {
+    /* we may not have sent anything but we consumed all data from the
+       adapter so let's clear the hdrext cache */
+    gst_rtp_base_depayload_flush (depayload, FALSE);
     gst_buffer_unmap (outbuf, &map);
     gst_buffer_unref (outbuf);
     return NULL;
diff --git a/gst/rtp/gstrtpmp4gdepay.c b/gst/rtp/gstrtpmp4gdepay.c
index 8ee094d5bfa57629376a79ec14143b722955cd5d..1ee31289681671c5c2b1f44a1483d4f76de0f54f 100644
--- a/gst/rtp/gstrtpmp4gdepay.c
+++ b/gst/rtp/gstrtpmp4gdepay.c
@@ -182,6 +182,8 @@ gst_rtp_mp4g_depay_class_init (GstRtpMP4GDepayClass * klass)
 static void
 gst_rtp_mp4g_depay_init (GstRtpMP4GDepay * rtpmp4gdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpmp4gdepay), TRUE);
   rtpmp4gdepay->adapter = gst_adapter_new ();
   rtpmp4gdepay->packets = g_queue_new ();
 }
@@ -348,7 +350,11 @@ gst_rtp_mp4g_depay_push_outbuf (GstRtpMP4GDepay * rtpmp4gdepay,
       discont ? "" : "expected ", AU_index);
 
   gst_rtp_drop_meta (GST_ELEMENT_CAST (rtpmp4gdepay), outbuf, 0);
-  gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpmp4gdepay), outbuf);
+  if (!rtpmp4gdepay->outbufs) {
+    rtpmp4gdepay->outbufs =
+        gst_buffer_list_new_sized (g_queue_get_length (rtpmp4gdepay->packets));
+  }
+  gst_buffer_list_add (rtpmp4gdepay->outbufs, outbuf);
   rtpmp4gdepay->next_AU_index = AU_index + 1;
 }
 
@@ -727,6 +733,11 @@ gst_rtp_mp4g_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
         payload_AU += AU_size;
         payload_AU_size -= AU_size;
       }
+
+      if (rtpmp4gdepay->outbufs) {
+        gst_rtp_base_depayload_push_list (depayload,
+            g_steal_pointer (&rtpmp4gdepay->outbufs));
+      }
     } else {
       /* push complete buffer in adapter */
       outbuf = gst_rtp_buffer_get_payload_subbuffer (rtp, 0, payload_len);
@@ -755,6 +766,7 @@ short_payload:
   {
     GST_ELEMENT_WARNING (rtpmp4gdepay, STREAM, DECODE,
         ("Packet payload was too short."), (NULL));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtpmp4gdepay.h b/gst/rtp/gstrtpmp4gdepay.h
index a6a88a0d4b62ea394ab7d784940433b1c0b3755e..7887464c2b0bf11a4c7857d8276242628f0eb550 100644
--- a/gst/rtp/gstrtpmp4gdepay.h
+++ b/gst/rtp/gstrtpmp4gdepay.h
@@ -73,6 +73,7 @@ struct _GstRtpMP4GDepay
   GQueue *packets;
   
   GstAdapter *adapter;
+  GstBufferList *outbufs;
 };
 
 struct _GstRtpMP4GDepayClass
diff --git a/gst/rtp/gstrtpmp4vdepay.c b/gst/rtp/gstrtpmp4vdepay.c
index 204828c478494c25836b770445e5bfd7323e47e5..c99df605f525efa6cc769d15a3aaf9814bf88b3e 100644
--- a/gst/rtp/gstrtpmp4vdepay.c
+++ b/gst/rtp/gstrtpmp4vdepay.c
@@ -106,6 +106,8 @@ gst_rtp_mp4v_depay_class_init (GstRtpMP4VDepayClass * klass)
 static void
 gst_rtp_mp4v_depay_init (GstRtpMP4VDepay * rtpmp4vdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpmp4vdepay), TRUE);
   rtpmp4vdepay->adapter = gst_adapter_new ();
 }
 
diff --git a/gst/rtp/gstrtpmparobustdepay.c b/gst/rtp/gstrtpmparobustdepay.c
index ca7f1f19a3123aba80bf02a67ecde482a73bdfdb..b20072fb622750bcf465ab673ccc1288d5e75cb8 100644
--- a/gst/rtp/gstrtpmparobustdepay.c
+++ b/gst/rtp/gstrtpmparobustdepay.c
@@ -278,7 +278,7 @@ gst_rtp_mpa_robust_depay_generate_dummy_frame (GstRtpMPARobustDepay *
   GstADUFrame *dummy;
   GstMapInfo map;
 
-  dummy = g_slice_dup (GstADUFrame, frame);
+  dummy = g_memdup2 (frame, sizeof (GstADUFrame));
 
   /* go for maximum bitrate */
   dummy->header = (frame->header & ~(0xf << 12)) | (0xe << 12);
@@ -319,7 +319,7 @@ gst_rtp_mpa_robust_depay_queue_frame (GstRtpMPARobustDepay * rtpmpadepay,
   if (map.size < 6)
     goto corrupt_frame;
 
-  frame = g_slice_new0 (GstADUFrame);
+  frame = g_new0 (GstADUFrame, 1);
   frame->header = GST_READ_UINT32_BE (map.data);
 
   size = mp3_type_frame_length_from_header (GST_ELEMENT_CAST (rtpmpadepay),
@@ -377,7 +377,7 @@ corrupt_frame:
     gst_buffer_unmap (buf, &map);
     gst_buffer_unref (buf);
     if (frame)
-      g_slice_free (GstADUFrame, frame);
+      g_free (frame);
     return FALSE;
   }
 }
@@ -387,7 +387,7 @@ gst_rtp_mpa_robust_depay_free_frame (GstADUFrame * frame)
 {
   if (frame->buffer)
     gst_buffer_unref (frame->buffer);
-  g_slice_free (GstADUFrame, frame);
+  g_free (frame);
 }
 
 static inline void
@@ -500,7 +500,7 @@ gst_rtp_mpa_robust_depay_push_mp3_frames (GstRtpMPARobustDepay * rtpmpadepay)
           frame->buffer);
       frame->buffer = NULL;
       /* and remove it from any further consideration */
-      g_slice_free (GstADUFrame, frame);
+      g_free (frame);
       g_queue_delete_link (rtpmpadepay->adu_frames, rtpmpadepay->cur_adu_frame);
       rtpmpadepay->cur_adu_frame = NULL;
       continue;
@@ -682,8 +682,8 @@ gst_rtp_mpa_robust_depay_process (GstRTPBaseDepayload * depayload,
    */
   while (payload_len) {
     if (G_LIKELY (rtpmpadepay->has_descriptor)) {
-      cont = ! !(payload[offset] & 0x80);
-      dtype = ! !(payload[offset] & 0x40);
+      cont = !!(payload[offset] & 0x80);
+      dtype = !!(payload[offset] & 0x40);
       if (dtype) {
         size = (payload[offset] & 0x3f) << 8 | payload[offset + 1];
         payload_len--;
diff --git a/gst/rtp/gstrtpopusdepay.c b/gst/rtp/gstrtpopusdepay.c
index d20dd7d85a6cf71d6e57e07ba5ee5ce47e041f6d..64e0737169279a2322cd54b2938165c599828b12 100644
--- a/gst/rtp/gstrtpopusdepay.c
+++ b/gst/rtp/gstrtpopusdepay.c
@@ -100,6 +100,8 @@ gst_rtp_opus_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
   GstStructure *s;
   gboolean ret;
   const gchar *sprop_maxcapturerate;
+  /* Default unless overridden by sprop_maxcapturerate */
+  gint rate = 48000;
 
   srccaps = gst_caps_new_empty_simple ("audio/x-opus");
 
@@ -215,19 +217,22 @@ gst_rtp_opus_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
 
   if ((sprop_maxcapturerate =
           gst_structure_get_string (s, "sprop-maxcapturerate"))) {
-    gulong rate;
     gchar *tailptr;
+    gulong tmp_rate;
 
-    rate = strtoul (sprop_maxcapturerate, &tailptr, 10);
-    if (rate > INT_MAX || *tailptr != '\0') {
+    tmp_rate = strtoul (sprop_maxcapturerate, &tailptr, 10);
+    if (tmp_rate > INT_MAX || *tailptr != '\0') {
       GST_WARNING_OBJECT (depayload,
           "Failed to parse sprop-maxcapturerate value '%s'",
           sprop_maxcapturerate);
     } else {
-      gst_caps_set_simple (srccaps, "rate", G_TYPE_INT, rate, NULL);
+      /* Valid rate from sprop, let's use it */
+      rate = tmp_rate;
     }
   }
 
+  gst_caps_set_simple (srccaps, "rate", G_TYPE_INT, rate, NULL);
+
   ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps);
 
   GST_DEBUG_OBJECT (depayload,
@@ -252,6 +257,9 @@ gst_rtp_opus_depay_process (GstRTPBaseDepayload * depayload,
 
   outbuf = gst_rtp_buffer_get_payload_buffer (rtp_buffer);
 
+  if (gst_rtp_buffer_get_marker (rtp_buffer))
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);
+
   gst_rtp_drop_non_audio_meta (depayload, outbuf);
 
   return outbuf;
diff --git a/gst/rtp/gstrtppassthroughpay.c b/gst/rtp/gstrtppassthroughpay.c
new file mode 100644
index 0000000000000000000000000000000000000000..693d2708590b0908a9540acff553435782c00958
--- /dev/null
+++ b/gst/rtp/gstrtppassthroughpay.c
@@ -0,0 +1,477 @@
+/*
+ * GStreamer
+ *
+ * Copyright (C) 2023 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2023 Jonas Danielsson <jonas.danielsson@spiideo.com>
+ *
+ * gstrtppassthroughpay.c:
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-rtppassthroughpay
+ * @title: rtppassthroughpay
+ *
+ * This elements pass RTP packets along unchanged and appear as a RTP
+ * payloader element to the outside world.
+ *
+ * This is useful, for example, in the case where you are receiving RTP
+ * packets from a different source and want to serve them over RTSP. Since the
+ * gst-rtsp-server library expect the element marked as `payX` to be a RTP
+ * payloader element and assumes certain properties are available.
+ *
+ * ## Example pipelines
+ *
+ * |[
+ * gst-launch-1.0 rtpbin name=rtpbin \
+ *     videotestsrc ! videoconvert ! x264enc ! rtph264pay ! rtpbin.send_rtp_sink_0 \
+ *     rtpbin.send_rtp_src_0 ! udpsink port=5000
+ * ]| Encode and payload H264 video from videotestsrc. The H264 RTP packets are
+ * sent on UDP port 5000.
+ * |[
+ * test-launch "( udpsrc port=5000 caps=application/x-rtp, ..." ! .recv_rtp_sink_0 \
+ *      rtpbin ! rtppassthroughpay name=pay0 )"
+ * ]| Setup a gstreamer-rtsp-server using the example tool, it will listen for
+ * H264 RTP packets on port 5000 and present them using the rtppassthroughpay
+ * element as the well-known payloader pay0.
+ *
+ * Since: 1.24
+ */
+
+#include <gst/rtp/rtp.h>
+
+#include "gstrtpelements.h"
+#include "gstrtppassthroughpay.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_passthrough_pay_debug);
+#define GST_CAT_DEFAULT gst_rtp_passthrough_pay_debug
+
+#define PAYLOAD_TYPE_INVALID 128        /* valid range is 0 - 127 (seven bits) */
+
+G_DEFINE_TYPE (GstRtpPassthroughPay, gst_rtp_passthrough_pay, GST_TYPE_ELEMENT);
+GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtppassthroughpay,
+    "rtppassthroughpay", GST_RANK_NONE, GST_TYPE_RTP_PASSTHROUGH_PAY,
+    rtp_element_init (plugin));
+
+static GstStaticPadTemplate src_template =
+GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 127 ],"
+        "clock-rate = (int) [ 1, 2147483647 ]"));
+
+static GstStaticPadTemplate sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 127 ],"
+        "clock-rate = (int) [ 1, 2147483647 ]"));
+
+enum
+{
+  PROP_0,
+  PROP_PT,
+  PROP_MTU,
+  PROP_STATS,
+  PROP_SEQNUM,
+  PROP_SEQNUM_OFFSET,
+  PROP_TIMESTAMP,
+  PROP_TIMESTAMP_OFFSET,
+};
+
+static void gst_rtp_passthrough_pay_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+static void gst_rtp_passthrough_pay_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_rtp_passthrough_pay_finalize (GObject * object);
+
+static GstStateChangeReturn
+gst_rtp_passthrough_pay_change_state (GstElement * element,
+    GstStateChange transition);
+
+static GstFlowReturn gst_rtp_passthrough_pay_chain (GstPad * pad,
+    GstObject * parent, GstBuffer * buffer);
+
+static gboolean gst_rtp_passthrough_pay_sink_event (GstPad * pad,
+    GstObject * parent, GstEvent * event);
+
+static void gst_rtp_passthrough_set_payload_type (GstRtpPassthroughPay * self,
+    guint pt);
+
+static GstStructure
+    * gst_rtp_passthrough_pay_create_stats (GstRtpPassthroughPay * self);
+
+static void
+gst_rtp_passthrough_pay_init (GstRtpPassthroughPay * self)
+{
+  self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+  gst_pad_set_chain_function (self->sinkpad, gst_rtp_passthrough_pay_chain);
+  gst_pad_set_event_function (self->sinkpad,
+      gst_rtp_passthrough_pay_sink_event);
+  GST_OBJECT_FLAG_SET (self->sinkpad, GST_PAD_FLAG_PROXY_ALLOCATION);
+  GST_OBJECT_FLAG_SET (self->sinkpad, GST_PAD_FLAG_PROXY_CAPS);
+  GST_OBJECT_FLAG_SET (self->sinkpad, GST_PAD_FLAG_PROXY_SCHEDULING);
+  gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
+
+  self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
+  GST_OBJECT_FLAG_SET (self->srcpad, GST_PAD_FLAG_PROXY_CAPS);
+  gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
+
+  self->pt = PAYLOAD_TYPE_INVALID;
+}
+
+static void
+gst_rtp_passthrough_pay_class_init (GstRtpPassthroughPayClass
+    * gst_rtp_passthrough_pay_class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (gst_rtp_passthrough_pay_class);
+  GstElementClass *element_class =
+      GST_ELEMENT_CLASS (gst_rtp_passthrough_pay_class);
+
+  gobject_class->set_property = gst_rtp_passthrough_pay_set_property;
+  gobject_class->get_property = gst_rtp_passthrough_pay_get_property;
+  gobject_class->finalize = gst_rtp_passthrough_pay_finalize;
+
+  /**
+   * GstRtpPassthroughPay:pt:
+   *
+   * If set this will override the payload of the incoming RTP packets.
+   * If not set the payload type will be same as incoming RTP packets.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class), PROP_PT,
+      g_param_spec_uint ("pt", "payload type",
+          "The payload type of the packets", 0, 0x80, PAYLOAD_TYPE_INVALID,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpPassthroughPay:mtu:
+   *
+   * Setting this property has no effect on this element, it is here and it
+   * is writable only to emulate a proper RTP payloader.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class), PROP_MTU,
+      g_param_spec_uint ("mtu", "MTU", "Maximum size of one packet", 28,
+          G_MAXUINT, 1492, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpPassthroughPay:timestamp:
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
+      PROP_TIMESTAMP, g_param_spec_uint ("timestamp", "Timestamp",
+          "The RTP timestamp of the last processed packet", 0, G_MAXUINT32, 0,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpPassthroughPay:seqnum:
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class), PROP_SEQNUM,
+      g_param_spec_uint ("seqnum", "Sequence number",
+          "The RTP sequence number of the last processed packet", 0,
+          G_MAXUINT16, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpPassthroughPay:timestamp-offset:
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
+      PROP_TIMESTAMP_OFFSET, g_param_spec_uint ("timestamp-offset",
+          "Timestamp Offset",
+          "Offset to add to all outgoing timestamps (default = random)", 0,
+          G_MAXUINT32, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpPassthroughPay:seqnum-offset:
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class),
+      PROP_SEQNUM_OFFSET, g_param_spec_int ("seqnum-offset",
+          "Sequence number Offset",
+          "Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXUINT16,
+          -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpPassthroughPay:stats:
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (gobject_class), PROP_STATS,
+      g_param_spec_boxed ("stats", "Statistics", "Various statistics",
+          GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  element_class->change_state = gst_rtp_passthrough_pay_change_state;
+
+  gst_element_class_add_static_pad_template (element_class, &sink_template);
+  gst_element_class_add_static_pad_template (element_class, &src_template);
+
+  gst_element_class_set_static_metadata (element_class,
+      "RTP Passthrough payloader", "Codec/Payloader/Network/RTP",
+      "Passes through RTP packets",
+      "Sebastian Dröge <sebastian@centricular.com>, "
+      "Jonas Danielsson <jonas.danielsson@spiideo.com>");
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_passthrough_pay_debug, "rtppassthroughpay",
+      0, "RTP Passthrough Payloader");
+}
+
+static void
+gst_rtp_passthrough_pay_finalize (GObject * object)
+{
+  GstRtpPassthroughPay *self = GST_RTP_PASSTHROUGH_PAY (object);
+
+  gst_clear_caps (&self->caps);
+  G_OBJECT_CLASS (gst_rtp_passthrough_pay_parent_class)->finalize (object);
+}
+
+static void
+gst_rtp_passthrough_pay_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstRtpPassthroughPay *self = GST_RTP_PASSTHROUGH_PAY (object);
+
+  switch (prop_id) {
+    case PROP_PT:
+      g_value_set_uint (value, self->pt);
+      break;
+    case PROP_MTU:
+      g_value_set_uint (value, 0U);
+      break;
+    case PROP_TIMESTAMP:
+      g_value_set_uint (value, self->timestamp);
+      break;
+    case PROP_TIMESTAMP_OFFSET:
+      g_value_set_uint (value, self->timestamp_offset);
+      break;
+    case PROP_SEQNUM:
+      g_value_set_uint (value, self->seqnum);
+      break;
+    case PROP_SEQNUM_OFFSET:
+      g_value_set_int (value, (guint16) self->seqnum_offset);
+      break;
+    case PROP_STATS:
+      g_value_take_boxed (value, gst_rtp_passthrough_pay_create_stats (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_passthrough_pay_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRtpPassthroughPay *self = GST_RTP_PASSTHROUGH_PAY (object);
+
+  switch (prop_id) {
+    case PROP_PT:
+      gst_rtp_passthrough_set_payload_type (self, g_value_get_uint (value));
+      break;
+    case PROP_MTU:
+      GST_WARNING_OBJECT (self, "Setting the mtu property has no effect");
+      break;
+    case PROP_TIMESTAMP_OFFSET:
+      GST_FIXME_OBJECT (self,
+          "Setting the timestamp-offset property has no effect");
+      break;
+    case PROP_SEQNUM_OFFSET:
+      GST_FIXME_OBJECT (self,
+          "Setting the seqnum-offset property has no effect");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstStateChangeReturn
+gst_rtp_passthrough_pay_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstRtpPassthroughPay *self = GST_RTP_PASSTHROUGH_PAY (element);
+  GstStateChangeReturn ret;
+
+  ret = GST_ELEMENT_CLASS (gst_rtp_passthrough_pay_parent_class)
+      ->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_clear_caps (&self->caps);
+      gst_segment_init (&self->segment, GST_FORMAT_TIME);
+      self->clock_rate = -1;
+      self->pt = PAYLOAD_TYPE_INVALID;
+      self->pt_override = FALSE;
+      self->ssrc = -1;
+      self->ssrc_set = FALSE;
+      self->timestamp = -1;
+      self->timestamp_offset = -1;
+      self->timestamp_offset_set = FALSE;
+      self->seqnum = -1;
+      self->pts_or_dts = GST_CLOCK_TIME_NONE;
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_rtp_passthrough_pay_chain (GstPad * pad,
+    GstObject * parent, GstBuffer * buffer)
+{
+  GstRtpPassthroughPay *self = GST_RTP_PASSTHROUGH_PAY (parent);
+  GstRTPBuffer rtp_buffer = GST_RTP_BUFFER_INIT;
+  guint pt, ssrc, seqnum, timestamp;
+
+  buffer = gst_buffer_make_writable (buffer);
+
+  if (!gst_rtp_buffer_map (buffer, GST_MAP_READWRITE, &rtp_buffer)) {
+    GST_ERROR_OBJECT (self, "Invalid RTP buffer");
+    return gst_pad_push (self->srcpad, buffer);
+  }
+
+  /* If the PROP_PT property is set we override the incoming packets payload
+   * type. If it is not, we will mirror the payload type.
+   *
+   */
+  pt = gst_rtp_buffer_get_payload_type (&rtp_buffer);
+  if (self->pt_override && self->pt != PAYLOAD_TYPE_INVALID) {
+    gst_rtp_buffer_set_payload_type (&rtp_buffer, self->pt);
+  } else {
+    if (pt != self->pt) {
+      if (self->pt != PAYLOAD_TYPE_INVALID) {
+        GST_WARNING_OBJECT (self, "Payload type changed from %u to %u",
+            self->pt, pt);
+      }
+      self->pt = pt;
+      g_object_notify (G_OBJECT (self), "pt");
+    }
+  }
+
+  ssrc = gst_rtp_buffer_get_ssrc (&rtp_buffer);
+  if (self->ssrc_set && self->ssrc != ssrc) {
+    GST_WARNING_OBJECT (self, "SSRC changed from %u to %u", self->ssrc, ssrc);
+  }
+  self->ssrc = ssrc;
+  self->ssrc_set = TRUE;
+
+  seqnum = gst_rtp_buffer_get_seq (&rtp_buffer);
+  self->seqnum = seqnum;
+  if (self->seqnum_offset == (guint) (-1)) {
+    self->seqnum_offset = seqnum;
+    g_object_notify (G_OBJECT (self), "seqnum-offset");
+  }
+
+  timestamp = gst_rtp_buffer_get_timestamp (&rtp_buffer);
+  self->timestamp = timestamp;
+  if (!self->timestamp_offset_set) {
+    self->timestamp_offset = timestamp;
+    self->timestamp_offset_set = TRUE;
+    g_object_notify (G_OBJECT (self), "timestamp-offset");
+  }
+
+  gst_rtp_buffer_unmap (&rtp_buffer);
+
+  if (GST_BUFFER_PTS_IS_VALID (buffer))
+    self->pts_or_dts = GST_BUFFER_PTS (buffer);
+  else if (GST_BUFFER_DTS_IS_VALID (buffer))
+    self->pts_or_dts = GST_BUFFER_DTS (buffer);
+
+  return gst_pad_push (self->srcpad, buffer);
+}
+
+static gboolean
+gst_rtp_passthrough_pay_sink_event (GstPad * pad,
+    GstObject * parent, GstEvent * event)
+{
+  GstRtpPassthroughPay *self = GST_RTP_PASSTHROUGH_PAY (parent);
+  gboolean ret = FALSE;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEGMENT:{
+      gst_event_copy_segment (event, &self->segment);
+
+      ret = gst_pad_event_default (pad, parent, event);
+      break;
+    }
+    case GST_EVENT_CAPS:{
+      GstCaps *caps;
+      const GstStructure *s;
+
+      gst_event_parse_caps (event, &caps);
+      gst_caps_replace (&self->caps, caps);
+
+      s = gst_caps_get_structure (caps, 0);
+
+      if (!self->pt_override
+          && !gst_structure_get_int (s, "payload", &self->pt)) {
+        GST_WARNING_OBJECT (self, "Caps are missing payload type!");
+      }
+      if (!gst_structure_get_int (s, "clock-rate", &self->clock_rate))
+        GST_WARNING_OBJECT (self, "Caps are missing clock-rate!");
+      if (gst_structure_get_uint (s, "ssrc", &self->ssrc))
+        self->ssrc_set = TRUE;
+      if (gst_structure_get_uint (s, "clock-base", &self->timestamp_offset))
+        self->timestamp_offset_set = TRUE;
+      gst_structure_get_uint (s, "seqnum-base", &self->seqnum_offset);
+
+      ret = gst_pad_event_default (pad, parent, event);
+      break;
+    }
+    default:
+      ret = gst_pad_event_default (pad, parent, event);
+      break;
+  }
+
+  return ret;
+}
+
+static void
+gst_rtp_passthrough_set_payload_type (GstRtpPassthroughPay * self, guint pt)
+{
+  if (self->pt == pt) {
+    return;
+  }
+
+  if (pt != PAYLOAD_TYPE_INVALID) {
+    GST_INFO_OBJECT (self, "Overriding payload type (%u)", pt);
+    self->pt_override = TRUE;
+  } else {
+    self->pt_override = FALSE;
+  }
+
+  self->pt = pt;
+}
+
+static GstStructure *
+gst_rtp_passthrough_pay_create_stats (GstRtpPassthroughPay * self)
+{
+  GstClockTime running_time;
+
+  if (self->segment.format == GST_FORMAT_UNDEFINED) {
+    running_time = 0;
+  } else {
+    running_time = gst_segment_to_running_time (&self->segment, GST_FORMAT_TIME,
+        self->pts_or_dts);
+  }
+
+  return gst_structure_new ("application/x-rtp-payload-stats", "clock-rate",
+      G_TYPE_UINT, (guint) self->clock_rate, "running-time", G_TYPE_UINT64,
+      running_time, "seqnum", G_TYPE_UINT, (guint) self->seqnum, "timestamp",
+      G_TYPE_UINT, (guint) self->timestamp, "ssrc", G_TYPE_UINT, self->ssrc,
+      "pt", G_TYPE_UINT, self->pt, "seqnum-offset", G_TYPE_UINT,
+      (guint) self->seqnum_offset, "timestamp-offset", G_TYPE_UINT,
+      (guint) self->timestamp_offset, NULL);
+
+  return NULL;
+}
diff --git a/gst/rtp/gstrtppassthroughpay.h b/gst/rtp/gstrtppassthroughpay.h
new file mode 100644
index 0000000000000000000000000000000000000000..68536821d7e4fab514c5902027461689b6ad8af5
--- /dev/null
+++ b/gst/rtp/gstrtppassthroughpay.h
@@ -0,0 +1,71 @@
+/*
+ * GStreamer
+ *
+ * Copyright (C) 2023 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2023 Jonas Danielsson <jonas.danielsson@spiideo.com>
+ *
+ * gstrtppassthroughpay.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.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_RTP_PASSTHROUGH_PAY (gst_rtp_passthrough_pay_get_type())
+#define GST_RTP_PASSTHROUGH_PAY(obj)                                           \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RTP_PASSTHROUGH_PAY,             \
+                              GstRtpPassthroughPay))
+#define GST_RTP_PASSTHROUGH_PAY_CLASS(klass)                                   \
+  (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RTP_PASSTHROUGH_PAY,              \
+                           GstRtpPassthroughPayClass))
+#define GST_IS_RTP_PASSTHROUGH_PAY(obj)                                        \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RTP_PASSTHROUGH_PAY))
+#define GST_IS_RTP_PASSTHROUGH_PAY_CLASS(klass)                                \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RTP_PASSTHROUGH_PAY))
+#define GST_RTP_PASSTHROUGH_PAY_GET_CLASS(obj)                                 \
+  (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_RTP_PASSTHROUGH_PAY,              \
+                             GstRtpPassthroughPayClass))
+typedef struct _GstRtpPassthroughPay GstRtpPassthroughPay;
+typedef struct _GstRtpPassthroughPayClass GstRtpPassthroughPayClass;
+
+struct _GstRtpPassthroughPayClass
+{
+  GstElementClass parent_class;
+};
+
+struct _GstRtpPassthroughPay
+{
+  GstElement parent;
+
+  GstPad *sinkpad, *srcpad;
+
+  GstCaps *caps;
+  GstSegment segment;
+
+  gint clock_rate;
+  gint pt;
+  gboolean pt_override;
+  guint ssrc;
+  gboolean ssrc_set;
+  guint timestamp;
+  guint timestamp_offset;
+  gboolean timestamp_offset_set;
+  guint seqnum;
+  guint seqnum_offset;
+  GstClockTime pts_or_dts;
+};
+
+GType gst_rtp_passthrough_pay_get_type (void);
+
+G_END_DECLS
diff --git a/gst/rtp/gstrtpreddec.c b/gst/rtp/gstrtpreddec.c
index 6c7ba363f40b4c8def9f893d8a28c32e3568a776..178bae4bfcbfdb7b7fbdf5bdad98c329bca68f72 100644
--- a/gst/rtp/gstrtpreddec.c
+++ b/gst/rtp/gstrtpreddec.c
@@ -97,13 +97,13 @@ enum
 static RTPHistItem *
 rtp_hist_item_alloc (void)
 {
-  return g_slice_new (RTPHistItem);
+  return g_new (RTPHistItem, 1);
 }
 
 static void
 rtp_hist_item_free (gpointer item)
 {
-  g_slice_free (RTPHistItem, item);
+  g_free (item);
 }
 
 static gint
diff --git a/gst/rtp/gstrtpredenc.c b/gst/rtp/gstrtpredenc.c
index 7f234bc0d6cde52dc42c1f5e1b40f0dd853de0dd..91a5354f1a378982644d91d85841eaf2fbdd4c66 100644
--- a/gst/rtp/gstrtpredenc.c
+++ b/gst/rtp/gstrtpredenc.c
@@ -104,7 +104,7 @@ rtp_hist_item_init (RTPHistItem * item, GstRTPBuffer * rtp,
 static RTPHistItem *
 rtp_hist_item_new (GstRTPBuffer * rtp, GstBuffer * rtp_payload)
 {
-  RTPHistItem *item = g_slice_new0 (RTPHistItem);
+  RTPHistItem *item = g_new0 (RTPHistItem, 1);
   rtp_hist_item_init (item, rtp, rtp_payload);
   return item;
 }
@@ -122,7 +122,7 @@ rtp_hist_item_free (gpointer _item)
 {
   RTPHistItem *item = _item;
   gst_buffer_unref (item->payload);
-  g_slice_free (RTPHistItem, item);
+  g_free (item);
 }
 
 static GstEvent *
@@ -200,8 +200,8 @@ _alloc_red_packet_and_fill_headers (GstRtpRedEnc * self,
      * for our wrapper */
     if (gst_rtp_buffer_get_extension_onebyte_header (inp_rtp, self->twcc_ext_id,
             0, &inp_data, &inp_size)) {
-      gst_rtp_buffer_add_extension_onebyte_header (&red_rtp, 1, &data,
-          sizeof (guint16));
+      gst_rtp_buffer_add_extension_onebyte_header (&red_rtp, self->twcc_ext_id,
+          &data, sizeof (guint16));
     } else if (gst_rtp_buffer_get_extension_twobytes_header (inp_rtp, &appbits,
             self->twcc_ext_id, 0, &inp_data, &inp_size)) {
       gst_rtp_buffer_add_extension_twobytes_header (&red_rtp, appbits,
diff --git a/gst/rtp/gstrtpsbcdepay.c b/gst/rtp/gstrtpsbcdepay.c
index f5dec8b787aa6bf4e3fdc563db61e3084c581456..1de5a8bce5f68d8e030cfa439fe228defa09667b 100644
--- a/gst/rtp/gstrtpsbcdepay.c
+++ b/gst/rtp/gstrtpsbcdepay.c
@@ -117,6 +117,9 @@ gst_rtp_sbc_depay_class_init (GstRtpSbcDepayClass * klass)
 static void
 gst_rtp_sbc_depay_init (GstRtpSbcDepay * rtpsbcdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpsbcdepay), TRUE);
+
   rtpsbcdepay->adapter = gst_adapter_new ();
   rtpsbcdepay->stream_align =
       gst_audio_stream_align_new (48000, 40 * GST_MSECOND, 1 * GST_SECOND);
@@ -318,10 +321,11 @@ gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstRTPBuffer * rtp)
     if (start && gst_adapter_available (depay->adapter)) {
       GST_WARNING_OBJECT (depay, "Missing last fragment");
       gst_adapter_clear (depay->adapter);
-
+      gst_rtp_base_depayload_flush (base, TRUE);
     } else if (!start && !gst_adapter_available (depay->adapter)) {
       GST_WARNING_OBJECT (depay, "Missing start fragment");
       gst_buffer_unref (data);
+      gst_rtp_base_depayload_dropped (base);
       data = NULL;
       goto out;
     }
@@ -387,5 +391,11 @@ out:
 bad_packet:
   GST_ELEMENT_WARNING (depay, STREAM, DECODE,
       ("Received invalid RTP payload, dropping"), (NULL));
+  gst_rtp_base_depayload_dropped (base);
+  /* if the adapter has been cleared before using this error out we
+     have the clear the header ext cache as well */
+  if (!gst_adapter_available (depay->adapter))
+    gst_rtp_base_depayload_flush (base, FALSE);
+
   goto out;
 }
diff --git a/gst/rtp/gstrtpsv3vdepay.c b/gst/rtp/gstrtpsv3vdepay.c
index bac99458a1e088e4d97a30b2975f9ab944fc14d4..09c4769ae80f6d74796d6fd77497da5bfd02bbdd 100644
--- a/gst/rtp/gstrtpsv3vdepay.c
+++ b/gst/rtp/gstrtpsv3vdepay.c
@@ -98,6 +98,9 @@ gst_rtp_sv3v_depay_class_init (GstRtpSV3VDepayClass * klass)
 static void
 gst_rtp_sv3v_depay_init (GstRtpSV3VDepay * rtpsv3vdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpsv3vdepay), TRUE);
+
   rtpsv3vdepay->adapter = gst_adapter_new ();
 }
 
@@ -139,13 +142,13 @@ gst_rtp_sv3v_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
     guint width, height;
   } resolutions[7] = {
     {
-    160, 128}, {
-    128, 96}, {
-    176, 144}, {
-    352, 288}, {
-    704, 576}, {
-    240, 180}, {
-    320, 240}
+        160, 128}, {
+        128, 96}, {
+        176, 144}, {
+        352, 288}, {
+        704, 576}, {
+        240, 180}, {
+        320, 240}
   };
   gint payload_len;
   guint8 *payload;
@@ -281,6 +284,7 @@ bad_packet:
   {
     GST_ELEMENT_WARNING (rtpsv3vdepay, STREAM, DECODE,
         (NULL), ("Packet was too short"));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 }
diff --git a/gst/rtp/gstrtptheoradepay.c b/gst/rtp/gstrtptheoradepay.c
index e7ff9e18c677ecbd9323591cf8d203096dcc3c0b..431b9b947542e2c3a3f3f424286c3e31d0776701 100644
--- a/gst/rtp/gstrtptheoradepay.c
+++ b/gst/rtp/gstrtptheoradepay.c
@@ -116,6 +116,8 @@ gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass)
 static void
 gst_rtp_theora_depay_init (GstRtpTheoraDepay * rtptheoradepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtptheoradepay), TRUE);
   rtptheoradepay->adapter = gst_adapter_new ();
 }
 
@@ -422,10 +424,10 @@ gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
 {
   GstRtpTheoraDepay *rtptheoradepay;
   GstBuffer *outbuf;
-  GstFlowReturn ret;
   gint payload_len;
   GstMapInfo map;
   GstBuffer *payload_buffer = NULL;
+  GstBufferList *outbufs = NULL;
   guint8 *payload;
   guint32 header, ident;
   guint8 F, TDT, packets;
@@ -491,12 +493,15 @@ gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
     if (F == 1) {
       /* if we start a packet, clear adapter and start assembling. */
       gst_adapter_clear (rtptheoradepay->adapter);
+      gst_rtp_base_depayload_flush (depayload, TRUE);
       GST_DEBUG_OBJECT (depayload, "start assemble");
       rtptheoradepay->assembling = TRUE;
     }
 
-    if (!rtptheoradepay->assembling)
+    if (!rtptheoradepay->assembling) {
+      gst_rtp_base_depayload_dropped (depayload);
       goto no_output;
+    }
 
     /* skip header and length. */
     vdata = gst_rtp_buffer_get_payload_subbuffer (rtp, 6, -1);
@@ -526,6 +531,8 @@ gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
   rtptheoradepay->assembling = FALSE;
   gst_adapter_clear (rtptheoradepay->adapter);
 
+  outbufs = gst_buffer_list_new ();
+
   /* payload now points to a length with that many theora data bytes.
    * Iterate over the packets and send them out.
    *
@@ -562,6 +569,12 @@ gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
     /* handle in-band configuration */
     if (G_UNLIKELY (TDT == 1)) {
       GST_DEBUG_OBJECT (rtptheoradepay, "in-band configuration");
+
+      /* push all buffers we found so far and clear the cached header
+         extensions if any */
+      gst_rtp_base_depayload_push_list (depayload, outbufs);
+      gst_rtp_base_depayload_flush (depayload, FALSE);
+
       if (!gst_rtp_theora_depay_parse_inband_configuration (rtptheoradepay,
               ident, payload, payload_len, length))
         goto invalid_configuration;
@@ -584,11 +597,11 @@ gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
     /* make sure to read next length */
     length = 0;
 
-    ret = gst_rtp_base_depayload_push (depayload, outbuf);
-    if (ret != GST_FLOW_OK)
-      break;
+    gst_buffer_list_add (outbufs, outbuf);
   }
 
+  gst_rtp_base_depayload_push_list (depayload, outbufs);
+
   if (rtptheoradepay->needs_keyframe)
     goto request_keyframe;
 
@@ -607,23 +620,30 @@ switch_failed:
   {
     GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
         (NULL), ("Could not switch codebooks"));
+    gst_rtp_base_depayload_dropped (depayload);
     goto request_config;
   }
 packet_short:
   {
     GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
         (NULL), ("Packet was too short (%d < 4)", payload_len));
+    gst_rtp_base_depayload_dropped (depayload);
     goto request_keyframe;
   }
 ignore_reserved:
   {
     GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored");
+    gst_rtp_base_depayload_dropped (depayload);
     goto out;
   }
 length_short:
   {
     GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
         (NULL), ("Packet contains invalid data"));
+    /* there may already be some buffers to push so do that before
+       leaving */
+    gst_rtp_base_depayload_push_list (depayload, outbufs);
+    gst_rtp_base_depayload_flush (depayload, FALSE);
     goto request_keyframe;
   }
 invalid_configuration:
diff --git a/gst/rtp/gstrtpvorbisdepay.c b/gst/rtp/gstrtpvorbisdepay.c
index 053e647958d578d84f22926d3d6dc29a496f8248..87a6dcadfd69e70395b499341a8bdc98ea566bbf 100644
--- a/gst/rtp/gstrtpvorbisdepay.c
+++ b/gst/rtp/gstrtpvorbisdepay.c
@@ -110,6 +110,8 @@ gst_rtp_vorbis_depay_class_init (GstRtpVorbisDepayClass * klass)
 static void
 gst_rtp_vorbis_depay_init (GstRtpVorbisDepay * rtpvorbisdepay)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (rtpvorbisdepay), TRUE);
   rtpvorbisdepay->adapter = gst_adapter_new ();
 }
 
@@ -417,7 +419,7 @@ no_rate:
 
 static gboolean
 gst_rtp_vorbis_depay_switch_codebook (GstRtpVorbisDepay * rtpvorbisdepay,
-    guint32 ident)
+    guint32 ident, GstBufferList * outbufs)
 {
   GList *walk;
   gboolean res = FALSE;
@@ -436,8 +438,7 @@ gst_rtp_vorbis_depay_switch_codebook (GstRtpVorbisDepay * rtpvorbisdepay,
         GstBuffer *header = GST_BUFFER_CAST (headers->data);
 
         gst_buffer_ref (header);
-        gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpvorbisdepay),
-            header);
+        gst_buffer_list_add (outbufs, header);
       }
       /* remember the current config */
       rtpvorbisdepay->config = conf;
@@ -457,9 +458,9 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
 {
   GstRtpVorbisDepay *rtpvorbisdepay;
   GstBuffer *outbuf;
-  GstFlowReturn ret;
   gint payload_len;
   GstBuffer *payload_buffer = NULL;
+  GstBufferList *outbufs = NULL;
   guint8 *payload;
   GstMapInfo map;
   guint32 header, ident;
@@ -498,6 +499,8 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
   F = (header & 0xc0) >> 6;
   packets = (header & 0xf);
 
+  outbufs = gst_buffer_list_new ();
+
   if (VDT == 0) {
     gboolean do_switch = FALSE;
 
@@ -513,7 +516,8 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
       do_switch = TRUE;
     }
     if (do_switch) {
-      if (!gst_rtp_vorbis_depay_switch_codebook (rtpvorbisdepay, ident))
+      if (!gst_rtp_vorbis_depay_switch_codebook (rtpvorbisdepay,
+              ident, outbufs))
         goto switch_failed;
     }
   }
@@ -532,8 +536,10 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
       rtpvorbisdepay->assembling = TRUE;
     }
 
-    if (!rtpvorbisdepay->assembling)
-      goto no_output;
+    if (!rtpvorbisdepay->assembling) {
+      gst_rtp_base_depayload_dropped (depayload);
+      goto no_data_output;
+    }
 
     /* skip header and length. */
     vdata = gst_rtp_buffer_get_payload_subbuffer (rtp, 6, -1);
@@ -543,7 +549,7 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
 
     /* packet is not complete, we are done */
     if (F != 3)
-      goto no_output;
+      goto no_data_output;
 
     /* construct assembled buffer */
     length = gst_adapter_available (rtpvorbisdepay->adapter);
@@ -599,6 +605,12 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
     /* handle in-band configuration */
     if (G_UNLIKELY (VDT == 1)) {
       GST_DEBUG_OBJECT (rtpvorbisdepay, "in-band configuration");
+
+      /* push all buffers we found so far and clear the cached header
+         extensions if any */
+      gst_rtp_base_depayload_push_list (depayload, outbufs);
+      gst_rtp_base_depayload_flush (depayload, FALSE);
+
       if (!gst_rtp_vorbis_depay_parse_inband_configuration (rtpvorbisdepay,
               ident, payload, payload_len, length))
         goto invalid_configuration;
@@ -615,16 +627,20 @@ gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload,
     /* make sure to read next length */
     length = 0;
 
-    ret = gst_rtp_base_depayload_push (depayload, outbuf);
-    if (ret != GST_FLOW_OK)
-      break;
+    gst_buffer_list_add (outbufs, outbuf);
   }
 
+  gst_rtp_base_depayload_push_list (depayload, outbufs);
+
   gst_buffer_unmap (payload_buffer, &map);
   gst_buffer_unref (payload_buffer);
 
   return NULL;
 
+no_data_output:
+  /* we may have the headers from a codebook switch that should be
+     pushed */
+  gst_rtp_base_depayload_push_list (depayload, outbufs);
 no_output:
   {
     if (payload_buffer) {
@@ -638,17 +654,21 @@ switch_failed:
   {
     GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE,
         (NULL), ("Could not switch codebooks"));
+    gst_buffer_list_unref (outbufs);
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 packet_short:
   {
     GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE,
         (NULL), ("Packet was too short (%d < 4)", payload_len));
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 ignore_reserved:
   {
     GST_WARNING_OBJECT (rtpvorbisdepay, "reserved VDT ignored");
+    gst_rtp_base_depayload_dropped (depayload);
     return NULL;
   }
 length_short:
@@ -659,6 +679,8 @@ length_short:
       gst_buffer_unmap (payload_buffer, &map);
       gst_buffer_unref (payload_buffer);
     }
+    gst_rtp_base_depayload_push_list (depayload, outbufs);
+    gst_rtp_base_depayload_flush (depayload, FALSE);
     return NULL;
   }
 invalid_configuration:
diff --git a/gst/rtp/gstrtpvp8depay.c b/gst/rtp/gstrtpvp8depay.c
index 3428f43f66fdd798830af5689fa07faa48bd36e8..708bceb688c699f8df7e3cd2b0f510cc832e1e58 100644
--- a/gst/rtp/gstrtpvp8depay.c
+++ b/gst/rtp/gstrtpvp8depay.c
@@ -82,6 +82,9 @@ enum
 static void
 gst_rtp_vp8_depay_init (GstRtpVP8Depay * self)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (self), TRUE);
+
   self->adapter = gst_adapter_new ();
   self->started = FALSE;
   self->wait_for_keyframe = DEFAULT_WAIT_FOR_KEYFRAME;
@@ -351,6 +354,8 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
   if (frame_start) {
     if (G_UNLIKELY (self->started)) {
       GST_DEBUG_OBJECT (depay, "Incomplete frame, flushing adapter");
+      /* keep the current buffer because it may still be used later */
+      gst_rtp_base_depayload_flush (depay, TRUE);
       gst_adapter_clear (self->adapter);
       self->started = FALSE;
 
@@ -425,6 +430,7 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
       GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DELTA_UNIT);
 
       if (self->waiting_for_keyframe) {
+        gst_rtp_base_depayload_flush (depay, FALSE);
         gst_buffer_unref (out);
         out = NULL;
         GST_INFO_OBJECT (self, "Dropping inter-frame before intra-frame");
@@ -473,10 +479,12 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
   }
 
 done:
+  gst_rtp_base_depayload_dropped (depay);
   return NULL;
 
 too_small:
   GST_DEBUG_OBJECT (self, "Invalid rtp packet (too small), ignoring");
+  gst_rtp_base_depayload_flush (depay, FALSE);
   gst_adapter_clear (self->adapter);
   self->started = FALSE;
 
diff --git a/gst/rtp/gstrtpvp8pay.c b/gst/rtp/gstrtpvp8pay.c
index f4b4031d6ed4742e737a4898a6dafdb490949d80..af055a148acaaff411627a539b87b780164645fa 100644
--- a/gst/rtp/gstrtpvp8pay.c
+++ b/gst/rtp/gstrtpvp8pay.c
@@ -45,6 +45,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_rtp_vp8_pay_debug);
 enum
 {
   PROP_0,
+  PROP_PICTURE_ID,
   PROP_PICTURE_ID_MODE,
   PROP_PICTURE_ID_OFFSET
 };
@@ -98,7 +99,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
     GST_STATIC_CAPS ("video/x-vp8"));
 
 static gint
-picture_id_field_len (PictureIDMode mode)
+picture_id_field_len (GstVP8RtpPayPictureIDMode mode)
 {
   if (VP8_PAY_NO_PICTURE_ID == mode)
     return 0;
@@ -108,30 +109,41 @@ picture_id_field_len (PictureIDMode mode)
 }
 
 static void
-gst_rtp_vp8_pay_picture_id_reset (GstRtpVP8Pay * obj)
+gst_rtp_vp8_pay_picture_id_reset (GstRtpVP8Pay * self)
 {
   gint nbits;
+  gint old_picture_id = self->picture_id;
+  gint picture_id = 0;
 
-  if (obj->picture_id_offset == -1)
-    obj->picture_id = g_random_int ();
-  else
-    obj->picture_id = obj->picture_id_offset;
+  if (self->picture_id_mode != VP8_PAY_NO_PICTURE_ID) {
+    if (self->picture_id_offset == -1) {
+      picture_id = g_random_int ();
+    } else {
+      picture_id = self->picture_id_offset;
+    }
+    nbits = picture_id_field_len (self->picture_id_mode);
+    picture_id &= (1 << nbits) - 1;
+  }
+  g_atomic_int_set (&self->picture_id, picture_id);
 
-  nbits = picture_id_field_len (obj->picture_id_mode);
-  obj->picture_id &= (1 << nbits) - 1;
+  GST_LOG_OBJECT (self, "picture-id reset %d -> %d",
+      old_picture_id, picture_id);
 }
 
 static void
-gst_rtp_vp8_pay_picture_id_increment (GstRtpVP8Pay * obj)
+gst_rtp_vp8_pay_picture_id_increment (GstRtpVP8Pay * self)
 {
   gint nbits;
 
-  if (obj->picture_id_mode == VP8_PAY_NO_PICTURE_ID)
+  if (self->picture_id_mode == VP8_PAY_NO_PICTURE_ID)
     return;
 
-  nbits = picture_id_field_len (obj->picture_id_mode);
-  obj->picture_id++;
-  obj->picture_id &= (1 << nbits) - 1;
+  /* Atomically increment and wrap the picture id if it overflows */
+  nbits = picture_id_field_len (self->picture_id_mode);
+  gint picture_id = g_atomic_int_get (&self->picture_id);
+  picture_id++;
+  picture_id &= (1 << nbits) - 1;
+  g_atomic_int_set (&self->picture_id, picture_id);
 }
 
 static void
@@ -163,11 +175,24 @@ gst_rtp_vp8_pay_class_init (GstRtpVP8PayClass * gst_rtp_vp8_pay_class)
   gobject_class->set_property = gst_rtp_vp8_pay_set_property;
   gobject_class->get_property = gst_rtp_vp8_pay_get_property;
 
+  /**
+   * rtpvp8pay:picture-id:
+   *
+   * Currently used picture-id
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_PICTURE_ID,
+      g_param_spec_int ("picture-id", "Picture ID",
+          "Currently used picture-id for payloading", 0, 0x7FFF, 0,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (gobject_class, PROP_PICTURE_ID_MODE,
       g_param_spec_enum ("picture-id-mode", "Picture ID Mode",
           "The picture ID mode for payloading",
           GST_TYPE_RTP_VP8_PAY_PICTURE_ID_MODE, DEFAULT_PICTURE_ID_MODE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   /**
    * rtpvp8pay:picture-id-offset:
    *
@@ -228,6 +253,9 @@ gst_rtp_vp8_pay_get_property (GObject * object,
   GstRtpVP8Pay *rtpvp8pay = GST_RTP_VP8_PAY (object);
 
   switch (prop_id) {
+    case PROP_PICTURE_ID:
+      g_value_set_int (value, g_atomic_int_get (&rtpvp8pay->picture_id));
+      break;
     case PROP_PICTURE_ID_MODE:
       g_value_set_enum (value, rtpvp8pay->picture_id_mode);
       break;
@@ -528,13 +556,12 @@ gst_rtp_vp8_create_header_buffer (GstRtpVP8Pay * self, guint8 partid,
       gboolean use_temporal_scaling = FALSE;
 
       if (meta) {
-        GstStructure *s = gst_custom_meta_get_structure (meta);
-        gst_structure_get_boolean (s, "use-temporal-scaling",
+        gst_structure_get_boolean (meta->structure, "use-temporal-scaling",
             &use_temporal_scaling);
 
         if (use_temporal_scaling)
-          gst_structure_get (s, "layer-id", G_TYPE_UINT, &temporal_layer,
-              "layer-sync", G_TYPE_BOOLEAN, &layer_sync, NULL);
+          gst_structure_get (meta->structure, "layer-id", G_TYPE_UINT,
+              &temporal_layer, "layer-sync", G_TYPE_BOOLEAN, &layer_sync, NULL);
       }
 
       /* FIXME: Support a prediction structure where higher layers don't
@@ -583,7 +610,7 @@ gst_rtp_vp8_drop_vp8_meta (gpointer element, GstBuffer * buf)
 static guint
 gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, GstBufferList * list,
     guint offset, GstBuffer * buffer, gsize buffer_size, gsize max_payload_len,
-    GstCustomMeta * meta)
+    GstCustomMeta * meta, gboolean delta_unit)
 {
   guint partition;
   GstBuffer *header;
@@ -599,7 +626,7 @@ gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, GstBufferList * list,
   if (available > remaining)
     available = remaining;
 
-  if (meta) {
+  if (self->temporal_scalability_fields_present && meta) {
     /* If meta is present, then we have no partition offset information,
      * so always emit PID 0 and set the start bit for the first packet
      * of a frame only (c.f. RFC7741 $4.4)
@@ -623,6 +650,9 @@ gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, GstBufferList * list,
 
   out = gst_buffer_append (header, sub);
 
+  if (delta_unit)
+    GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DELTA_UNIT);
+
   gst_buffer_list_insert (list, -1, out);
 
   return available;
@@ -638,9 +668,12 @@ gst_rtp_vp8_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
   GstCustomMeta *meta;
   gsize size, max_paylen;
   guint offset, mtu, vp8_hdr_len;
+  gboolean delta_unit;
 
   size = gst_buffer_get_size (buffer);
   meta = gst_buffer_get_custom_meta (buffer, "GstVP8Meta");
+  delta_unit = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+
   if (G_UNLIKELY (!gst_rtp_vp8_pay_parse_frame (self, buffer, size))) {
     GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
         ("Failed to parse VP8 frame"));
@@ -648,13 +681,12 @@ gst_rtp_vp8_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
   }
 
   if (meta) {
-    GstStructure *s = gst_custom_meta_get_structure (meta);
     gboolean use_temporal_scaling;
     /* For interop it's most likely better to keep the temporal scalability
      * fields present if the stream previously had them present. Alternating
      * whether these fields are present or not may confuse the receiver. */
 
-    gst_structure_get_boolean (s, "use-temporal-scaling",
+    gst_structure_get_boolean (meta->structure, "use-temporal-scaling",
         &use_temporal_scaling);
     if (use_temporal_scaling)
       self->temporal_scalability_fields_present = TRUE;
@@ -671,7 +703,11 @@ gst_rtp_vp8_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
   while (offset < size) {
     offset +=
         gst_rtp_vp8_payload_next (self, list, offset, buffer, size,
-        max_paylen, meta);
+        max_paylen, meta, delta_unit);
+
+    /* only the first outgoing packet should not have the DELTA_UNIT flag */
+    if (!delta_unit)
+      delta_unit = TRUE;
   }
 
   ret = gst_rtp_base_payload_push_list (payload, list);
@@ -687,9 +723,13 @@ static gboolean
 gst_rtp_vp8_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
 {
   GstRtpVP8Pay *self = GST_RTP_VP8_PAY (payload);
+  GstEventType event_type = GST_EVENT_TYPE (event);
 
-  if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
-    gst_rtp_vp8_pay_reset (self);
+  if (event_type == GST_EVENT_GAP || event_type == GST_EVENT_FLUSH_START) {
+    gint picture_id = self->picture_id;
+    gst_rtp_vp8_pay_picture_id_increment (self);
+    GST_DEBUG_OBJECT (payload, "Incrementing picture ID on %s event %d -> %d",
+        GST_EVENT_TYPE_NAME (event), picture_id, self->picture_id);
   }
 
   return GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_vp8_pay_parent_class)->sink_event
diff --git a/gst/rtp/gstrtpvp8pay.h b/gst/rtp/gstrtpvp8pay.h
index 30ad99a67c93c310790559242d96925d4621d49e..748bec16501f8f6f49c74bf4c3a7449f11e93d64 100644
--- a/gst/rtp/gstrtpvp8pay.h
+++ b/gst/rtp/gstrtpvp8pay.h
@@ -39,9 +39,9 @@ G_BEGIN_DECLS
 
 typedef struct _GstRtpVP8Pay GstRtpVP8Pay;
 typedef struct _GstRtpVP8PayClass GstRtpVP8PayClass;
-typedef enum _PictureIDMode PictureIDMode;
+typedef enum _GstVP8RtpPayPictureIDMode GstVP8RtpPayPictureIDMode;
 
-enum _PictureIDMode {
+enum _GstVP8RtpPayPictureIDMode {
   VP8_PAY_NO_PICTURE_ID,
   VP8_PAY_PICTURE_ID_7BITS,
   VP8_PAY_PICTURE_ID_15BITS,
@@ -61,9 +61,9 @@ struct _GstRtpVP8Pay
    * folowed by max. 8 data partitions. last offset is the end of the buffer */
   guint partition_offset[10];
   guint partition_size[9];
-  PictureIDMode picture_id_mode;
+  GstVP8RtpPayPictureIDMode picture_id_mode;
   gint picture_id_offset;
-  guint16 picture_id;
+  gint picture_id;
   gboolean temporal_scalability_fields_present;
   guint8 tl0picidx;
 };
diff --git a/gst/rtp/gstrtpvp9depay.c b/gst/rtp/gstrtpvp9depay.c
index 94348c5c8ebbd5b792e607b8d5eca6d720d76820..5c5f71c76c240ecff614dad14e02ef1e6750dd56 100644
--- a/gst/rtp/gstrtpvp9depay.c
+++ b/gst/rtp/gstrtpvp9depay.c
@@ -83,6 +83,9 @@ enum
 static void
 gst_rtp_vp9_depay_init (GstRtpVP9Depay * self)
 {
+  gst_rtp_base_depayload_set_aggregate_hdrext_enabled (GST_RTP_BASE_DEPAYLOAD
+      (self), TRUE);
+
   self->adapter = gst_adapter_new ();
   self->started = FALSE;
   self->inter_picture = FALSE;
@@ -421,6 +424,8 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
   if (is_start_of_picture) {
     if (G_UNLIKELY (self->started)) {
       GST_DEBUG_OBJECT (depay, "Incomplete frame, flushing adapter");
+      /* keep the current buffer because it may still be used later */
+      gst_rtp_base_depayload_flush (depay, TRUE);
       gst_adapter_clear (self->adapter);
       self->started = FALSE;
       flushed_adapter = TRUE;
@@ -503,6 +508,7 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
       GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DELTA_UNIT);
 
       if (self->waiting_for_keyframe) {
+        gst_rtp_base_depayload_flush (depay, FALSE);
         gst_buffer_unref (out);
         out = NULL;
         GST_INFO_OBJECT (self, "Dropping inter-frame before intra-frame");
@@ -547,10 +553,12 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
   }
 
 done:
+  gst_rtp_base_depayload_dropped (depay);
   return NULL;
 
 too_small:
   GST_LOG_OBJECT (self, "Invalid rtp packet (too small), ignoring");
+  gst_rtp_base_depayload_flush (depay, FALSE);
   gst_adapter_clear (self->adapter);
   self->started = FALSE;
   goto done;
diff --git a/gst/rtp/gstrtpvp9pay.c b/gst/rtp/gstrtpvp9pay.c
index 0d29ac992ef795110d57e767c34e5f92610b01ff..adbc8db9f4fcdbc4b5b31c024d9a5e1912677cfb 100644
--- a/gst/rtp/gstrtpvp9pay.c
+++ b/gst/rtp/gstrtpvp9pay.c
@@ -33,7 +33,6 @@
 #include <gst/rtp/gstrtpbuffer.h>
 #include <gst/video/video.h>
 #include "gstrtpelements.h"
-#include "dboolhuff.h"
 #include "gstrtpvp9pay.h"
 #include "gstrtputils.h"
 
@@ -41,11 +40,14 @@ GST_DEBUG_CATEGORY_STATIC (gst_rtp_vp9_pay_debug);
 #define GST_CAT_DEFAULT gst_rtp_vp9_pay_debug
 
 #define DEFAULT_PICTURE_ID_MODE VP9_PAY_NO_PICTURE_ID
+#define DEFAULT_PICTURE_ID_OFFSET (-1)
 
 enum
 {
   PROP_0,
-  PROP_PICTURE_ID_MODE
+  PROP_PICTURE_ID,
+  PROP_PICTURE_ID_MODE,
+  PROP_PICTURE_ID_OFFSET,
 };
 
 #define GST_TYPE_RTP_VP9_PAY_PICTURE_ID_MODE (gst_rtp_vp9_pay_picture_id_mode_get_type())
@@ -96,14 +98,44 @@ GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/x-vp9"));
 
+static gint
+picture_id_field_len (GstVP9RtpPayPictureIDMode mode)
+{
+  if (VP9_PAY_NO_PICTURE_ID == mode)
+    return 0;
+  if (VP9_PAY_PICTURE_ID_7BITS == mode)
+    return 7;
+  return 15;
+}
+
+static void
+gst_rtp_vp9_pay_picture_id_reset (GstRtpVP9Pay * self)
+{
+  gint nbits;
+  gint old_picture_id = self->picture_id;
+  gint picture_id = 0;
+
+  if (self->picture_id_mode != VP9_PAY_NO_PICTURE_ID) {
+    if (self->picture_id_offset == -1) {
+      picture_id = g_random_int ();
+    } else {
+      picture_id = self->picture_id_offset;
+    }
+    nbits = picture_id_field_len (self->picture_id_mode);
+    picture_id &= (1 << nbits) - 1;
+  }
+  g_atomic_int_set (&self->picture_id, picture_id);
+
+  GST_LOG_OBJECT (self, "picture-id reset %d -> %d",
+      old_picture_id, picture_id);
+}
+
 static void
 gst_rtp_vp9_pay_init (GstRtpVP9Pay * obj)
 {
   obj->picture_id_mode = DEFAULT_PICTURE_ID_MODE;
-  if (obj->picture_id_mode == VP9_PAY_PICTURE_ID_7BITS)
-    obj->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F;
-  else if (obj->picture_id_mode == VP9_PAY_PICTURE_ID_15BITS)
-    obj->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF;
+  obj->picture_id_offset = DEFAULT_PICTURE_ID_OFFSET;
+  gst_rtp_vp9_pay_picture_id_reset (obj);
 }
 
 static void
@@ -117,12 +149,37 @@ gst_rtp_vp9_pay_class_init (GstRtpVP9PayClass * gst_rtp_vp9_pay_class)
   gobject_class->set_property = gst_rtp_vp9_pay_set_property;
   gobject_class->get_property = gst_rtp_vp9_pay_get_property;
 
+  /**
+   * rtpvp9pay:picture-id:
+   *
+   * Currently used picture-id
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_PICTURE_ID,
+      g_param_spec_int ("picture-id", "Picture ID",
+          "Currently used picture-id for payloading", 0, 0x7FFF, 0,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (gobject_class, PROP_PICTURE_ID_MODE,
       g_param_spec_enum ("picture-id-mode", "Picture ID Mode",
           "The picture ID mode for payloading",
           GST_TYPE_RTP_VP9_PAY_PICTURE_ID_MODE, DEFAULT_PICTURE_ID_MODE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * rtpvp9pay:picture-id-offset:
+   *
+   * Offset to add to the initial picture-id (-1 = random)
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_PICTURE_ID_OFFSET,
+      g_param_spec_int ("picture-id-offset", "Picture ID offset",
+          "Offset to add to the initial picture-id (-1 = random)",
+          -1, 0x7FFF, DEFAULT_PICTURE_ID_OFFSET,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_static_pad_template (element_class,
       &gst_rtp_vp9_pay_sink_template);
   gst_element_class_add_static_pad_template (element_class,
@@ -151,10 +208,11 @@ gst_rtp_vp9_pay_set_property (GObject * object,
   switch (prop_id) {
     case PROP_PICTURE_ID_MODE:
       rtpvp9pay->picture_id_mode = g_value_get_enum (value);
-      if (rtpvp9pay->picture_id_mode == VP9_PAY_PICTURE_ID_7BITS)
-        rtpvp9pay->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F;
-      else if (rtpvp9pay->picture_id_mode == VP9_PAY_PICTURE_ID_15BITS)
-        rtpvp9pay->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF;
+      gst_rtp_vp9_pay_picture_id_reset (rtpvp9pay);
+      break;
+    case PROP_PICTURE_ID_OFFSET:
+      rtpvp9pay->picture_id_offset = g_value_get_int (value);
+      gst_rtp_vp9_pay_picture_id_reset (rtpvp9pay);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -169,9 +227,15 @@ gst_rtp_vp9_pay_get_property (GObject * object,
   GstRtpVP9Pay *rtpvp9pay = GST_RTP_VP9_PAY (object);
 
   switch (prop_id) {
+    case PROP_PICTURE_ID:
+      g_value_set_int (value, g_atomic_int_get (&rtpvp9pay->picture_id));
+      break;
     case PROP_PICTURE_ID_MODE:
       g_value_set_enum (value, rtpvp9pay->picture_id_mode);
       break;
+    case PROP_PICTURE_ID_OFFSET:
+      g_value_set_int (value, rtpvp9pay->picture_id_offset);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -214,8 +278,11 @@ gst_rtp_vp9_pay_parse_frame (GstRtpVP9Pay * self, GstBuffer * buffer,
     goto error;
 
   /* profile, variable length */
-  if (!gst_bit_reader_get_bits_uint32 (&reader, &profile, 2))
+  guint8 version, high;
+  if (!(gst_bit_reader_get_bits_uint8 (&reader, &version, 1) &&
+          gst_bit_reader_get_bits_uint8 (&reader, &high, 1)))
     goto error;
+  profile = (high << 1) + version;
   if (profile > 2) {
     if (!gst_bit_reader_get_bits_uint32 (&reader, &tmp, 1))
       goto error;
@@ -445,7 +512,8 @@ gst_rtp_vp9_create_header_buffer (GstRtpVP9Pay * self,
 
 static guint
 gst_rtp_vp9_payload_next (GstRtpVP9Pay * self, GstBufferList * list,
-    guint offset, GstBuffer * buffer, gsize buffer_size, gsize max_payload_len)
+    guint offset, GstBuffer * buffer, gsize buffer_size, gsize max_payload_len,
+    gboolean delta_unit)
 {
   GstBuffer *header;
   GstBuffer *sub;
@@ -467,11 +535,29 @@ gst_rtp_vp9_payload_next (GstRtpVP9Pay * self, GstBufferList * list,
 
   out = gst_buffer_append (header, sub);
 
+  if (delta_unit)
+    GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DELTA_UNIT);
+
   gst_buffer_list_insert (list, -1, out);
 
   return available;
 }
 
+static void
+gst_rtp_vp9_pay_picture_id_increment (GstRtpVP9Pay * self)
+{
+  gint nbits;
+
+  if (self->picture_id_mode == VP9_PAY_NO_PICTURE_ID)
+    return;
+
+  /* Atomically increment and wrap the picture id if it overflows */
+  nbits = picture_id_field_len (self->picture_id_mode);
+  gint picture_id = g_atomic_int_get (&self->picture_id);
+  picture_id++;
+  picture_id &= (1 << nbits) - 1;
+  g_atomic_int_set (&self->picture_id, picture_id);
+}
 
 static GstFlowReturn
 gst_rtp_vp9_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
@@ -481,8 +567,10 @@ gst_rtp_vp9_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
   GstBufferList *list;
   gsize size, max_paylen;
   guint offset, mtu, vp9_hdr_len;
+  gboolean delta_unit;
 
   size = gst_buffer_get_size (buffer);
+  delta_unit = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
 
   if (G_UNLIKELY (!gst_rtp_vp9_pay_parse_frame (self, buffer, size))) {
     GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
@@ -499,7 +587,12 @@ gst_rtp_vp9_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
   offset = 0;
   while (offset < size) {
     offset +=
-        gst_rtp_vp9_payload_next (self, list, offset, buffer, size, max_paylen);
+        gst_rtp_vp9_payload_next (self, list, offset, buffer, size, max_paylen,
+        delta_unit);
+
+    /* only the first outgoing packet should not have the DELTA_UNIT flag */
+    if (!delta_unit)
+      delta_unit = TRUE;
   }
 
   ret = gst_rtp_base_payload_push_list (payload, list);
@@ -520,12 +613,13 @@ static gboolean
 gst_rtp_vp9_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
 {
   GstRtpVP9Pay *self = GST_RTP_VP9_PAY (payload);
+  GstEventType event_type = GST_EVENT_TYPE (event);
 
-  if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
-    if (self->picture_id_mode == VP9_PAY_PICTURE_ID_7BITS)
-      self->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F;
-    else if (self->picture_id_mode == VP9_PAY_PICTURE_ID_15BITS)
-      self->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF;
+  if (event_type == GST_EVENT_GAP || event_type == GST_EVENT_FLUSH_START) {
+    gint picture_id = self->picture_id;
+    gst_rtp_vp9_pay_picture_id_increment (self);
+    GST_DEBUG_OBJECT (payload, "Incrementing picture ID on %s event %d -> %d",
+        GST_EVENT_TYPE_NAME (event), picture_id, self->picture_id);
   }
 
   return GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_vp9_pay_parent_class)->sink_event
diff --git a/gst/rtp/gstrtpvp9pay.h b/gst/rtp/gstrtpvp9pay.h
index 407e3e08c459782979896cb54f85d89805ce7ef2..b2ad88445f99dacfd664bc97ee90f40abd640046 100644
--- a/gst/rtp/gstrtpvp9pay.h
+++ b/gst/rtp/gstrtpvp9pay.h
@@ -40,9 +40,9 @@ G_BEGIN_DECLS
 
 typedef struct _GstRtpVP9Pay GstRtpVP9Pay;
 typedef struct _GstRtpVP9PayClass GstRtpVP9PayClass;
-typedef enum _VP9PictureIDMode VP9PictureIDMode;
+typedef enum _GstVP9RtpPayPictureIDMode GstVP9RtpPayPictureIDMode;
 
-enum _VP9PictureIDMode {
+enum _GstVP9RtpPayPictureIDMode {
   VP9_PAY_NO_PICTURE_ID,
   VP9_PAY_PICTURE_ID_7BITS,
   VP9_PAY_PICTURE_ID_15BITS,
@@ -59,8 +59,9 @@ struct _GstRtpVP9Pay
   gboolean is_keyframe;
   guint width;
   guint height;
-  VP9PictureIDMode picture_id_mode;
-  guint16 picture_id;
+  GstVP9RtpPayPictureIDMode picture_id_mode;
+  gint picture_id_offset;
+  gint picture_id;
 };
 
 GType gst_rtp_vp9_pay_get_type (void);
diff --git a/gst/rtp/gstrtpvrawdepay.c b/gst/rtp/gstrtpvrawdepay.c
index d3bb5af05ed7a3535d4eb36e9ada11e49cfd3b26..74e8494a999245b2e9386c3ecaf592f0048abfa1 100644
--- a/gst/rtp/gstrtpvrawdepay.c
+++ b/gst/rtp/gstrtpvrawdepay.c
@@ -41,7 +41,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
     );
 
 static GstStaticPadTemplate gst_rtp_vraw_depay_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("application/x-rtp, "
@@ -55,7 +55,16 @@ GST_STATIC_PAD_TEMPLATE ("sink",
          * "width = (string) [1 32767],"
          * "height = (string) [1 32767],"
          */
-        "depth = (string) { \"8\", \"10\", \"12\", \"16\" }")
+        "depth = (string) 8; "
+        "application/x-rtp, "
+        "media = (string) video, "
+        "clock-rate = (int) 90000, "
+        "encoding-name = (string) RAW, sampling = (string) YCbCr-4:2:2,"
+        /* we cannot express these as strings
+         * "width = (string) [1 32767],"
+         * "height = (string) [1 32767],"
+         */
+        "depth = (string) 10")
     );
 
 #define gst_rtp_vraw_depay_parent_class parent_class
diff --git a/gst/rtp/meson.build b/gst/rtp/meson.build
index aa76523ec3eefa68977adb8fa5664fe2755e72e7..000b91d7ef6ec10762879b23412ce2dab9713c4d 100644
--- a/gst/rtp/meson.build
+++ b/gst/rtp/meson.build
@@ -26,6 +26,7 @@ rtp_sources = [
   'gstrtpmpvpay.c',
   'gstrtpopuspay.c',
   'gstrtpopusdepay.c',
+  'gstrtppassthroughpay.c',
   'gstrtppcmadepay.c',
   'gstrtppcmudepay.c',
   'gstrtppcmupay.c',
diff --git a/gst/rtp/rtpstoragestream.c b/gst/rtp/rtpstoragestream.c
index a0708859e47101739914ceb3b9626cfb416b36cd..851b9b508f814a7ea75044cd377e594fa18754fa 100644
--- a/gst/rtp/rtpstoragestream.c
+++ b/gst/rtp/rtpstoragestream.c
@@ -25,7 +25,7 @@
 static RtpStorageItem *
 rtp_storage_item_new (GstBuffer * buffer, guint8 pt, guint16 seq)
 {
-  RtpStorageItem *ret = g_slice_new0 (RtpStorageItem);
+  RtpStorageItem *ret = g_new0 (RtpStorageItem, 1);
   ret->buffer = buffer;
   ret->pt = pt;
   ret->seq = seq;
@@ -37,7 +37,7 @@ rtp_storage_item_free (RtpStorageItem * item)
 {
   g_assert (item->buffer != NULL);
   gst_buffer_unref (item->buffer);
-  g_slice_free (RtpStorageItem, item);
+  g_free (item);
 }
 
 static gint
@@ -150,7 +150,7 @@ rtp_storage_stream_resize_and_add_item (RtpStorageStream * stream,
 RtpStorageStream *
 rtp_storage_stream_new (guint32 ssrc)
 {
-  RtpStorageStream *ret = g_slice_new0 (RtpStorageStream);
+  RtpStorageStream *ret = g_new0 (RtpStorageStream, 1);
   ret->max_arrival_time = GST_CLOCK_TIME_NONE;
   ret->ssrc = ssrc;
   g_mutex_init (&ret->stream_lock);
@@ -165,7 +165,7 @@ rtp_storage_stream_free (RtpStorageStream * stream)
     rtp_storage_item_free (g_queue_pop_tail (&stream->queue));
   STREAM_UNLOCK (stream);
   g_mutex_clear (&stream->stream_lock);
-  g_slice_free (RtpStorageStream, stream);
+  g_free (stream);
 }
 
 void
diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c
index 0fbf0820c1cbb4b255810ee6735a4be20b584f32..4389df56f405b81a656f714a6452e649c9ae6b6b 100644
--- a/gst/rtpmanager/gstrtpbin.c
+++ b/gst/rtpmanager/gstrtpbin.c
@@ -358,6 +358,7 @@ enum
 #define DEFAULT_MIN_TS_OFFSET        MIN_TS_OFFSET_ROUND_OFF_COMP
 #define DEFAULT_TS_OFFSET_SMOOTHING_FACTOR  0
 #define DEFAULT_UPDATE_NTP64_HEADER_EXT TRUE
+#define DEFAULT_TIMEOUT_INACTIVE_SOURCES TRUE
 
 enum
 {
@@ -391,6 +392,7 @@ enum
   PROP_FEC_DECODERS,
   PROP_FEC_ENCODERS,
   PROP_UPDATE_NTP64_HEADER_EXT,
+  PROP_TIMEOUT_INACTIVE_SOURCES,
 };
 
 #define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type())
@@ -783,6 +785,9 @@ create_session (GstRtpBin * rtpbin, gint id)
   g_object_set (session, "update-ntp64-header-ext",
       rtpbin->update_ntp64_header_ext, NULL);
 
+  g_object_set (session, "timeout-inactive-sources",
+      rtpbin->timeout_inactive_sources, NULL);
+
   GST_OBJECT_UNLOCK (rtpbin);
 
   /* provide clock_rate to the session manager when needed */
@@ -1562,7 +1567,7 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
     stream_set_ts_offset (bin, stream, stream->rt_delta, bin->max_ts_offset,
         bin->min_ts_offset, FALSE);
   } else {
-    gint64 min, rtp_min, clock_base = stream->clock_base;
+    gint64 min, rtp_min, clock_base;
     gboolean all_sync, use_rtp;
     gboolean rtcp_sync = g_atomic_int_get (&bin->rtcp_sync);
 
@@ -1588,27 +1593,25 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
     min = rtp_min = G_MAXINT64;
     use_rtp = FALSE;
     if (rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) {
-      guint64 ext_base;
+      guint64 ext_base = -1;
+      gint64 rtp_delta = 0;
 
       use_rtp = TRUE;
-      /* signed version for convenience */
-      clock_base = base_rtptime;
-      /* deal with possible wrap-around */
-      ext_base = base_rtptime;
+      /* convert to extended RTP time */
       rtp_clock_base = gst_rtp_buffer_ext_timestamp (&ext_base, rtp_clock_base);
       /* sanity check; base rtp and provided clock_base should be close */
-      if (rtp_clock_base >= clock_base) {
-        if (rtp_clock_base - clock_base < 10 * clock_rate) {
-          rtp_clock_base = base_time +
-              gst_util_uint64_scale_int (rtp_clock_base - clock_base,
+      if (rtp_clock_base >= base_rtptime) {
+        if (rtp_clock_base - base_rtptime < 10 * clock_rate) {
+          rtp_delta = base_time +
+              gst_util_uint64_scale_int (rtp_clock_base - base_rtptime,
               GST_SECOND, clock_rate);
         } else {
           use_rtp = FALSE;
         }
       } else {
-        if (clock_base - rtp_clock_base < 10 * clock_rate) {
-          rtp_clock_base = base_time -
-              gst_util_uint64_scale_int (clock_base - rtp_clock_base,
+        if (base_rtptime - rtp_clock_base < 10 * clock_rate) {
+          rtp_delta = base_time -
+              gst_util_uint64_scale_int (base_rtptime - rtp_clock_base,
               GST_SECOND, clock_rate);
         } else {
           use_rtp = FALSE;
@@ -1620,11 +1623,11 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
         return;
       }
       /* store to track changes */
-      clock_base = rtp_clock_base;
+      clock_base = rtp_delta;
       /* generate a fake as before,
        * now equating rtptime obtained from RTP-Info,
        * where the large time represent the otherwise irrelevant npt/ntp time */
-      stream->rtp_delta = (GST_SECOND << 28) - rtp_clock_base;
+      stream->rtp_delta = (GST_SECOND << 28) - rtp_delta;
     } else {
       clock_base = rtp_clock_base;
     }
@@ -1962,8 +1965,8 @@ create_stream (GstRtpBinSession * session, guint32 ssrc)
   g_object_set_data (G_OBJECT (buffer), "GstRTPBin.stream", stream);
 
   /* configure latency and packet lost */
-  g_object_set (buffer, "latency", rtpbin->latency_ms, NULL);
-
+  if (g_object_class_find_property (jb_class, "latency"))
+    g_object_set (buffer, "latency", rtpbin->latency_ms, NULL);
   if (g_object_class_find_property (jb_class, "drop-on-latency"))
     g_object_set (buffer, "drop-on-latency", rtpbin->drop_on_latency, NULL);
   if (g_object_class_find_property (jb_class, "do-lost"))
@@ -3002,6 +3005,22 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass)
           DEFAULT_UPDATE_NTP64_HEADER_EXT,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstRtpBin:timeout-inactive-sources:
+   *
+   * Whether inactive sources should be timed out
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_TIMEOUT_INACTIVE_SOURCES,
+      g_param_spec_boolean ("timeout-inactive-sources",
+          "Time out inactive sources",
+          "Whether sources that don't receive RTP or RTCP packets for longer "
+          "than 5x RTCP interval should be removed",
+          DEFAULT_TIMEOUT_INACTIVE_SOURCES,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
   gstelement_class->request_new_pad =
       GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
@@ -3093,6 +3112,7 @@ gst_rtp_bin_init (GstRtpBin * rtpbin)
   rtpbin->min_ts_offset_is_set = FALSE;
   rtpbin->ts_offset_smoothing_factor = DEFAULT_TS_OFFSET_SMOOTHING_FACTOR;
   rtpbin->update_ntp64_header_ext = DEFAULT_UPDATE_NTP64_HEADER_EXT;
+  rtpbin->timeout_inactive_sources = DEFAULT_TIMEOUT_INACTIVE_SOURCES;
 
   /* some default SDES entries */
   cname = g_strdup_printf ("user%u@host-%x", g_random_int (), g_random_int ());
@@ -3439,6 +3459,13 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id,
       gst_rtp_bin_propagate_property_to_session (rtpbin,
           "update-ntp64-header-ext", value);
       break;
+    case PROP_TIMEOUT_INACTIVE_SOURCES:
+      GST_RTP_BIN_LOCK (rtpbin);
+      rtpbin->timeout_inactive_sources = g_value_get_boolean (value);
+      GST_RTP_BIN_UNLOCK (rtpbin);
+      gst_rtp_bin_propagate_property_to_session (rtpbin,
+          "timeout-inactive-sources", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -3551,6 +3578,9 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id,
     case PROP_UPDATE_NTP64_HEADER_EXT:
       g_value_set_boolean (value, rtpbin->update_ntp64_header_ext);
       break;
+    case PROP_TIMEOUT_INACTIVE_SOURCES:
+      g_value_set_boolean (value, rtpbin->timeout_inactive_sources);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
diff --git a/gst/rtpmanager/gstrtpbin.h b/gst/rtpmanager/gstrtpbin.h
index eb98eb28e8ed6acc7074589d4201e16ed2c701fe..bc7c7e5f66de80f4323f4f5886420c045e399f15 100644
--- a/gst/rtpmanager/gstrtpbin.h
+++ b/gst/rtpmanager/gstrtpbin.h
@@ -100,6 +100,8 @@ struct _GstRtpBin {
 
   gboolean       update_ntp64_header_ext;
 
+  gboolean       timeout_inactive_sources;
+
   /*< private >*/
   GstRtpBinPrivate *priv;
 };
diff --git a/gst/rtpmanager/gstrtpfunnel.c b/gst/rtpmanager/gstrtpfunnel.c
index 841b55b00fbd8ac3453ffe13d013defb9039b826..8de1ae8918c76dd90ad88595acd2ce534371f3bc 100644
--- a/gst/rtpmanager/gstrtpfunnel.c
+++ b/gst/rtpmanager/gstrtpfunnel.c
@@ -132,6 +132,8 @@ struct _GstRtpFunnel
   guint twcc_pads;              /* numer of sinkpads with negotiated twcc */
   GstRTPHeaderExtension *twcc_ext;
 
+  guint8 current_ntp64_ext_id;
+
   /* properties */
   gint common_ts_offset;
 };
@@ -259,6 +261,22 @@ map_failed:
   }
 }
 
+typedef struct
+{
+  GstRtpFunnel *funnel;
+  GstPad *pad;
+} SetTwccSeqnumData;
+
+static gboolean
+set_twcc_seqnum (GstBuffer ** buf, guint idx, gpointer user_data)
+{
+  SetTwccSeqnumData *data = user_data;
+
+  gst_rtp_funnel_set_twcc_seqnum (data->funnel, data->pad, buf);
+
+  return TRUE;
+}
+
 static GstFlowReturn
 gst_rtp_funnel_sink_chain_object (GstPad * pad, GstRtpFunnel * funnel,
     gboolean is_list, GstMiniObject * obj)
@@ -273,7 +291,12 @@ gst_rtp_funnel_sink_chain_object (GstPad * pad, GstRtpFunnel * funnel,
   gst_rtp_funnel_forward_segment (funnel, pad);
 
   if (is_list) {
-    res = gst_pad_push_list (funnel->srcpad, GST_BUFFER_LIST_CAST (obj));
+    GstBufferList *list = GST_BUFFER_LIST_CAST (obj);
+    SetTwccSeqnumData data = { funnel, pad };
+
+    list = gst_buffer_list_make_writable (list);
+    gst_buffer_list_foreach (list, set_twcc_seqnum, &data);
+    res = gst_pad_push_list (funnel->srcpad, list);
   } else {
     GstBuffer *buf = GST_BUFFER_CAST (obj);
     gst_rtp_funnel_set_twcc_seqnum (funnel, pad, &buf);
@@ -303,6 +326,9 @@ gst_rtp_funnel_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
       GST_MINI_OBJECT_CAST (buffer));
 }
 
+#define TWCC_EXTMAP_STR "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
+#define NTP64_EXTMAP_STR "urn:ietf:params:rtp-hdrext:ntp-64"
+
 static void
 gst_rtp_funnel_set_twcc_ext_id (GstRtpFunnel * funnel, guint8 twcc_ext_id)
 {
@@ -315,6 +341,7 @@ gst_rtp_funnel_set_twcc_ext_id (GstRtpFunnel * funnel, guint8 twcc_ext_id)
   if (current_ext_id == twcc_ext_id)
     return;
 
+  GST_DEBUG_OBJECT (funnel, "Got TWCC RTP header extension id %u", twcc_ext_id);
   name = g_strdup_printf ("extmap-%u", twcc_ext_id);
 
   gst_caps_set_simple (funnel->srccaps, name, G_TYPE_STRING,
@@ -328,7 +355,28 @@ gst_rtp_funnel_set_twcc_ext_id (GstRtpFunnel * funnel, guint8 twcc_ext_id)
   gst_rtp_header_extension_set_id (funnel->twcc_ext, twcc_ext_id);
 }
 
-#define TWCC_EXTMAP_STR "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
+static void
+gst_rtp_funnel_set_ntp64_ext_id (GstRtpFunnel * funnel, guint8 ntp64_ext_id)
+{
+  gchar *name;
+
+  if (funnel->current_ntp64_ext_id == ntp64_ext_id)
+    return;
+
+  GST_DEBUG_OBJECT (funnel, "Got NTP-64 RTP header extension id %u",
+      ntp64_ext_id);
+  funnel->current_ntp64_ext_id = ntp64_ext_id;
+
+  name = g_strdup_printf ("extmap-%u", ntp64_ext_id);
+
+  gst_caps_set_simple (funnel->srccaps, name, G_TYPE_STRING,
+      NTP64_EXTMAP_STR, NULL);
+
+  g_free (name);
+
+  /* make sure we update the sticky with the new caps */
+  funnel->send_sticky_events = TRUE;
+}
 
 static gboolean
 gst_rtp_funnel_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
@@ -382,11 +430,23 @@ gst_rtp_funnel_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
         funnel->twcc_pads++;
         gst_rtp_funnel_set_twcc_ext_id (funnel, ext_id);
       }
+
+      ext_id = gst_rtp_get_extmap_id_for_attribute (s, NTP64_EXTMAP_STR);
+      if (ext_id > 0) {
+        gst_rtp_funnel_set_ntp64_ext_id (funnel, ext_id);
+      }
       GST_OBJECT_UNLOCK (funnel);
 
       forward = FALSE;
       break;
     }
+    case GST_EVENT_FLUSH_START:
+      /* By resetting current_pad here the segment will be forwarded next time a
+         buffer is received. */
+      GST_OBJECT_LOCK (funnel);
+      funnel->current_pad = NULL;
+      GST_OBJECT_UNLOCK (funnel);
+      break;
     default:
       break;
   }
@@ -580,6 +640,7 @@ gst_rtp_funnel_change_state (GstElement * element, GstStateChange transition)
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       funnel->send_sticky_events = TRUE;
+      funnel->current_pad = NULL;
       break;
     default:
       break;
@@ -604,6 +665,9 @@ gst_rtp_funnel_release_pad (GstElement * element, GstPad * pad)
 
   GST_DEBUG_OBJECT (funnel, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
 
+  if (pad == funnel->current_pad)
+    funnel->current_pad = NULL;
+
   g_hash_table_foreach_remove (funnel->ssrc_to_pad, _remove_pad_func, pad);
 
   gst_pad_set_active (pad, FALSE);
diff --git a/gst/rtpmanager/gstrtphdrext-clientaudiolevel.c b/gst/rtpmanager/gstrtphdrext-clientaudiolevel.c
index 4012e18bc2982b82f2f1586d25e13b638c405ce3..0aa8a138e0c94c9fd370dcb63223344bc1d4ea68 100644
--- a/gst/rtpmanager/gstrtphdrext-clientaudiolevel.c
+++ b/gst/rtpmanager/gstrtphdrext-clientaudiolevel.c
@@ -25,7 +25,7 @@
  *
  * ## Example pipeline
  * |[
- * gst-launch-1.0 pulsesrc ! level audio-level-meta=true ! audiconvert !
+ * gst-launch-1.0 pulsesrc ! level audio-level-meta=true ! audioconvert !
  *   rtpL16pay ! application/x-rtp,
  *     extmap-1=(string)\< \"\", urn:ietf:params:rtp-hdrext:ssrc-audio-level,
  *     \"vad=on\" \> ! udpsink
@@ -176,11 +176,11 @@ gst_rtp_header_extension_client_audio_level_write (GstRTPHeaderExtension * ext,
     level = 127;
   }
 
-  GST_LOG_OBJECT (ext, "writing ext (level: %d voice: %d)", meta->level,
+  GST_LOG_OBJECT (ext, "writing ext (level: %d voice: %d)", level,
       meta->voice_activity);
 
   /* Both one & two byte use the same format, the second byte being padding */
-  data[0] = (meta->level & 0x7F) | (meta->voice_activity << 7);
+  data[0] = (level & 0x7F) | (meta->voice_activity << 7);
   if (write_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
     return 1;
   }
diff --git a/gst/rtpmanager/gstrtphdrext-ntp.c b/gst/rtpmanager/gstrtphdrext-ntp.c
index a027138d5b1737b523f9666e818710599b8d85c3..2d514f2c7b047afaf1b6517900e4dea112f7ae6e 100644
--- a/gst/rtpmanager/gstrtphdrext-ntp.c
+++ b/gst/rtpmanager/gstrtphdrext-ntp.c
@@ -254,7 +254,7 @@ static void
       "RTP Header Extension RFC6051 64-bit NTP timestamp",
       GST_RTP_HDREXT_ELEMENT_CLASS,
       "Extends RTP packets to add or retrieve a 64-bit NTP "
-      "timestamp as specified in RFC6501",
+      "timestamp as specified in RFC6051",
       "Sebastian Dröge <sebastian@centricular.com>");
   gst_rtp_header_extension_class_set_uri (rtp_hdr_class,
       GST_RTP_HDREXT_BASE GST_RTP_HDREXT_NTP_64);
diff --git a/gst/rtpmanager/gstrtphdrext-twcc.c b/gst/rtpmanager/gstrtphdrext-twcc.c
index 2457337d0ba364c397aa205288737cdbb9bb4470..78412be76cc784ac5bd327440ee672a07b7331ab 100644
--- a/gst/rtpmanager/gstrtphdrext-twcc.c
+++ b/gst/rtpmanager/gstrtphdrext-twcc.c
@@ -179,7 +179,7 @@ gst_rtp_header_extension_twcc_write (GstRTPHeaderExtension * ext,
   g_return_val_if_fail (write_flags &
       gst_rtp_header_extension_twcc_get_supported_flags (ext), -1);
 
-  if (!gst_rtp_buffer_map (output, GST_MAP_READWRITE, &rtp))
+  if (!gst_rtp_buffer_map (output, GST_MAP_READ, &rtp))
     goto map_failed;
 
   /* if there already is a twcc-seqnum inside the packet */
diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c
index 063e39b9f743708502b5b6045ac2a6d848c87702..7efdf2d0df06e6fd485aaab7a0bd026e9bb03754 100644
--- a/gst/rtpmanager/gstrtpjitterbuffer.c
+++ b/gst/rtpmanager/gstrtpjitterbuffer.c
@@ -158,6 +158,8 @@ enum
 #define DEFAULT_ADD_REFERENCE_TIMESTAMP_META FALSE
 #define DEFAULT_FASTSTART_MIN_PACKETS 0
 #define DEFAULT_SYNC_INTERVAL 0
+#define DEFAULT_RFC7273_USE_SYSTEM_CLOCK FALSE
+#define DEFAULT_RFC7273_REFERENCE_TIMESTAMP_META_ONLY FALSE
 
 #define DEFAULT_AUTO_RTX_DELAY (20 * GST_MSECOND)
 #define DEFAULT_AUTO_RTX_TIMEOUT (40 * GST_MSECOND)
@@ -193,6 +195,8 @@ enum
   PROP_ADD_REFERENCE_TIMESTAMP_META,
   PROP_FASTSTART_MIN_PACKETS,
   PROP_SYNC_INTERVAL,
+  PROP_RFC7273_USE_SYSTEM_CLOCK,
+  PROP_RFC7273_REFERENCE_TIMESTAMP_META_ONLY,
 };
 
 #define JBUF_LOCK(priv)   G_STMT_START {			\
@@ -337,6 +341,8 @@ struct _GstRtpJitterBufferPrivate
   guint faststart_min_packets;
   gboolean add_reference_timestamp_meta;
   guint sync_interval;
+  gboolean rfc7273_use_system_clock;
+  gboolean rfc7273_reference_timestamp_meta_only;
 
   /* Reference for GstReferenceTimestampMeta */
   GstCaps *reference_timestamp_caps;
@@ -997,6 +1003,59 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           0, G_MAXUINT, DEFAULT_SYNC_INTERVAL,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstRtpJitterBuffer:rfc7273-use-system-clock:
+   *
+   * Uses the system clock as media clock in RFC7273 mode instead of
+   * instantiating an NTP or PTP clock.
+   *
+   * This will always provide the correct sender timestamps in the
+   * `GstReferenceTimestampMeta` as long as the system clock is synced to the
+   * actual media clock with at most a few seconds difference.
+   *
+   * PTS on outgoing buffers would be as accurate as the synchronization
+   * between the actual media clock and the system clock.
+   *
+   * This can be useful if only recovery of the original sender timestamps is
+   * needed and syncing to a PTP/NTP clock would be unnecessarily complex, or
+   * if the system clock already is synchronized to the correct clock and
+   * doing it another time inside GStreamer would be unnecessary.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_RFC7273_USE_SYSTEM_CLOCK,
+      g_param_spec_boolean ("rfc7273-use-system-clock",
+          "Use system clock as RFC7273 clock",
+          "Use system clock as RFC7273 media clock (requires system clock "
+          "to be synced externally)", DEFAULT_RFC7273_USE_SYSTEM_CLOCK,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstRtpJitterBuffer:rfc7273-reference-timestamp-meta-only:
+   *
+   * When enabled, the jitterbuffer calculates the PTS of the outgoing buffers
+   * according to the configured mode as if not RFC7273 mode is enabled.
+   *
+   * The timestamps calculated from the RFC7273 clock are only put into the
+   * reference timestamp meta, if enabled via the corresponding property.
+   *
+   * This is useful in combination with the `rfc7273-use-system-clock`, or
+   * generally if synchronization should not be affected by the original
+   * sender timestamps but the original sender timestamps should nonetheless
+   * be available as metadata.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_RFC7273_REFERENCE_TIMESTAMP_META_ONLY,
+      g_param_spec_boolean ("rfc7273-reference-timestamp-meta-only",
+          "Use RFC7273 clock only for reference timestamp meta",
+          "When enabled the PTS on the buffers are calculated normally and the "
+          "RFC7273 clock is only used for the reference timestamp meta",
+          DEFAULT_RFC7273_REFERENCE_TIMESTAMP_META_ONLY,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+
   /**
    * GstRtpJitterBuffer::request-pt-map:
    * @buffer: the object which received the signal
@@ -1128,6 +1187,9 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
   priv->faststart_min_packets = DEFAULT_FASTSTART_MIN_PACKETS;
   priv->add_reference_timestamp_meta = DEFAULT_ADD_REFERENCE_TIMESTAMP_META;
   priv->sync_interval = DEFAULT_SYNC_INTERVAL;
+  priv->rfc7273_use_system_clock = DEFAULT_RFC7273_USE_SYSTEM_CLOCK;
+  priv->rfc7273_reference_timestamp_meta_only =
+      DEFAULT_RFC7273_REFERENCE_TIMESTAMP_META_ONLY;
 
   priv->ts_offset_remainder = 0;
   priv->last_dts = -1;
@@ -1549,14 +1611,15 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
 
   gst_rtp_packet_rate_ctx_reset (&priv->packet_rate_ctx, priv->clock_rate);
 
-  /* The clock base is the RTP timestamp corrsponding to the npt-start value. We
+  /* The clock base is the RTP timestamp corresponding to the npt-start value. We
    * can use this to track the amount of time elapsed on the sender. */
-  if (gst_structure_get_uint (caps_struct, "clock-base", &val))
-    priv->clock_base = val;
-  else
+  priv->ext_timestamp = -1;
+  if (gst_structure_get_uint (caps_struct, "clock-base", &val)) {
+    priv->clock_base = gst_rtp_buffer_ext_timestamp (&priv->ext_timestamp, val);
+    priv->ext_timestamp = priv->clock_base;
+  } else {
     priv->clock_base = -1;
-
-  priv->ext_timestamp = priv->clock_base;
+  }
 
   GST_DEBUG_OBJECT (jitterbuffer, "got clock-base %" G_GINT64_FORMAT,
       priv->clock_base);
@@ -1594,12 +1657,18 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
       GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop));
 
   if ((ts_refclk = gst_structure_get_string (caps_struct, "a-ts-refclk"))) {
+    gboolean use_system_clock;
+    gboolean reference_timestamp_meta_only;
     GstClock *clock = NULL;
     guint64 clock_offset = -1;
+    gint64 clock_correction = 0;
 
     GST_DEBUG_OBJECT (jitterbuffer, "Have timestamp reference clock %s",
         ts_refclk);
 
+    use_system_clock = priv->rfc7273_use_system_clock;
+    reference_timestamp_meta_only = priv->rfc7273_reference_timestamp_meta_only;
+
     if (g_str_has_prefix (ts_refclk, "ntp=")) {
       if (g_str_has_prefix (ts_refclk, "ntp=/traceable/")) {
         GST_FIXME_OBJECT (jitterbuffer, "Can't handle traceable NTP clocks");
@@ -1629,7 +1698,15 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
         else
           hostname = g_strdup (host);
 
-        clock = gst_ntp_clock_new (NULL, hostname, port, 0);
+        if (use_system_clock) {
+          clock =
+              g_object_new (GST_TYPE_SYSTEM_CLOCK, "clock-type",
+              GST_CLOCK_TYPE_REALTIME, NULL);
+          /* difference between UNIX epoch and NTP epoch */
+          clock_correction = GST_RTP_NTP_UNIX_OFFSET * GST_SECOND;
+        } else {
+          clock = gst_ntp_clock_new (NULL, hostname, port, 0);
+        }
 
         ts_meta_ref = gst_caps_new_simple ("timestamp/x-ntp",
             "host", G_TYPE_STRING, hostname, "port", G_TYPE_INT, port, NULL);
@@ -1644,7 +1721,15 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
       if (domainstr[0] != ':' || sscanf (domainstr, ":%u", &domain) != 1)
         domain = 0;
 
-      clock = gst_ptp_clock_new (NULL, domain);
+      if (use_system_clock) {
+        clock =
+            g_object_new (GST_TYPE_SYSTEM_CLOCK, "clock-type",
+            GST_CLOCK_TYPE_REALTIME, NULL);
+        /* difference between UNIX and PTP/TAI (37 leap seconds as of October 2023) */
+        clock_correction = 37 * GST_SECOND;
+      } else {
+        clock = gst_ptp_clock_new (NULL, domain);
+      }
 
       ts_meta_ref = gst_caps_new_simple ("timestamp/x-ptp",
           "version", G_TYPE_STRING, "IEEE1588-2008",
@@ -1652,7 +1737,14 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
     } else if (!g_strcmp0 (ts_refclk, "local")) {
       ts_meta_ref = gst_caps_new_empty_simple ("timestamp/x-ntp");
     } else {
-      GST_FIXME_OBJECT (jitterbuffer, "Unsupported timestamp reference clock");
+      if (use_system_clock) {
+        clock =
+            g_object_new (GST_TYPE_SYSTEM_CLOCK, "clock-type",
+            GST_CLOCK_TYPE_REALTIME, NULL);
+      } else {
+        GST_FIXME_OBJECT (jitterbuffer,
+            "Unsupported timestamp reference clock");
+      }
     }
 
     if ((mediaclk = gst_structure_get_string (caps_struct, "a-mediaclk"))) {
@@ -1668,9 +1760,10 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
       }
     }
 
-    rtp_jitter_buffer_set_media_clock (priv->jbuf, clock, clock_offset);
+    rtp_jitter_buffer_set_media_clock (priv->jbuf, clock, clock_offset,
+        clock_correction, reference_timestamp_meta_only);
   } else {
-    rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1);
+    rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1, 0, FALSE);
     ts_meta_ref = gst_caps_new_empty_simple ("timestamp/x-ntp");
   }
 
@@ -2669,7 +2762,7 @@ gst_rtp_jitter_buffer_handle_missing_packets (GstRtpJitterBuffer * jitterbuffer,
 
       /* based on the estimated packet duration, we
          can figure out how many packets we could possibly save */
-      if (est_pkt_duration)
+      if (est_pkt_duration && offset > 0)
         max_saveable_packets = offset / est_pkt_duration;
 
       /* and say that the amount of lost packet is the sequence-number
@@ -4594,7 +4687,7 @@ do_handle_sync_inband (GstRtpJitterBuffer * jitterbuffer, guint64 ntpnstime)
       "base-rtptime", G_TYPE_UINT64, base_rtptime,
       "base-time", G_TYPE_UINT64, base_time,
       "clock-rate", G_TYPE_UINT, clock_rate,
-      "clock-base", G_TYPE_UINT64, priv->clock_base,
+      "clock-base", G_TYPE_UINT64, priv->clock_base & G_MAXUINT32,
       "cname", G_TYPE_STRING, cname,
       "ssrc", G_TYPE_UINT, priv->last_ssrc,
       "inband-ext-rtptime", G_TYPE_UINT64, last_rtptime,
@@ -4682,7 +4775,7 @@ do_handle_sync (GstRtpJitterBuffer * jitterbuffer)
         "base-rtptime", G_TYPE_UINT64, base_rtptime,
         "base-time", G_TYPE_UINT64, base_time,
         "clock-rate", G_TYPE_UINT, clock_rate,
-        "clock-base", G_TYPE_UINT64, clock_base,
+        "clock-base", G_TYPE_UINT64, priv->clock_base & G_MAXUINT32,
         "ssrc", G_TYPE_UINT, priv->last_sr_ssrc,
         "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
         "sr-ntpnstime", G_TYPE_UINT64, priv->last_sr_ntpnstime,
@@ -4835,6 +4928,12 @@ out:
   priv->last_known_ext_rtptime = ext_rtptime;
   priv->last_known_ntpnstime = ntpnstime;
 
+  if (G_UNLIKELY (priv->last_ssrc == -1)) {
+    GST_DEBUG_OBJECT (jitterbuffer, "SSRC changed from %u to %u",
+        priv->last_ssrc, ssrc);
+    priv->last_ssrc = ssrc;
+  }
+
   if (priv->last_ntpnstime != GST_CLOCK_TIME_NONE
       && ntpnstime - priv->last_ntpnstime < priv->sync_interval * GST_MSECOND) {
     gst_buffer_replace (&priv->last_sr, NULL);
@@ -5205,6 +5304,17 @@ gst_rtp_jitter_buffer_set_property (GObject * object,
       priv->sync_interval = g_value_get_uint (value);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_RFC7273_USE_SYSTEM_CLOCK:
+      JBUF_LOCK (priv);
+      priv->rfc7273_use_system_clock = g_value_get_boolean (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_RFC7273_REFERENCE_TIMESTAMP_META_ONLY:
+      JBUF_LOCK (priv);
+      priv->rfc7273_reference_timestamp_meta_only = g_value_get_boolean (value);
+      JBUF_UNLOCK (priv);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5371,6 +5481,16 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
       g_value_set_uint (value, priv->sync_interval);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_RFC7273_USE_SYSTEM_CLOCK:
+      JBUF_LOCK (priv);
+      g_value_set_boolean (value, priv->rfc7273_use_system_clock);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_RFC7273_REFERENCE_TIMESTAMP_META_ONLY:
+      JBUF_LOCK (priv);
+      g_value_set_boolean (value, priv->rfc7273_reference_timestamp_meta_only);
+      JBUF_UNLOCK (priv);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
diff --git a/gst/rtpmanager/gstrtpmux.c b/gst/rtpmanager/gstrtpmux.c
index 6387c655c3fb633f7ae407154cb4ff524721dc83..a5ad2306cb3298224d4c397029e6b23afa84c270 100644
--- a/gst/rtpmanager/gstrtpmux.c
+++ b/gst/rtpmanager/gstrtpmux.c
@@ -164,6 +164,8 @@ gst_rtp_mux_class_init (GstRTPMuxClass * klass)
       GST_DEBUG_FUNCPTR (gst_rtp_mux_request_new_pad);
   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_mux_release_pad);
   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_mux_change_state);
+
+  gst_type_mark_as_plugin_api (GST_TYPE_RTP_MUX, 0);
 }
 
 static void
@@ -277,7 +279,7 @@ gst_rtp_mux_init (GstRTPMux * rtp_mux)
 static void
 gst_rtp_mux_setup_sinkpad (GstRTPMux * rtp_mux, GstPad * sinkpad)
 {
-  GstRTPMuxPadPrivate *padpriv = g_slice_new0 (GstRTPMuxPadPrivate);
+  GstRTPMuxPadPrivate *padpriv = g_new0 (GstRTPMuxPadPrivate, 1);
 
   /* setup some pad functions */
   gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_rtp_mux_chain));
@@ -336,7 +338,7 @@ gst_rtp_mux_release_pad (GstElement * element, GstPad * pad)
   gst_element_remove_pad (element, pad);
 
   if (padpriv) {
-    g_slice_free (GstRTPMuxPadPrivate, padpriv);
+    g_free (padpriv);
   }
 }
 
diff --git a/gst/rtpmanager/gstrtpptdemux.c b/gst/rtpmanager/gstrtpptdemux.c
index 5cbbc7264d661c918687180ac7950c7b174ab071..0454d97134862da791ca08e1aed8fda034d10406 100644
--- a/gst/rtpmanager/gstrtpptdemux.c
+++ b/gst/rtpmanager/gstrtpptdemux.c
@@ -493,7 +493,7 @@ gst_rtp_pt_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
     gst_pad_set_event_function (srcpad, gst_rtp_pt_demux_src_event);
 
     GST_DEBUG_OBJECT (rtpdemux, "Adding pt=%d to the list.", pt);
-    rtpdemuxpad = g_slice_new0 (GstRtpPtDemuxPad);
+    rtpdemuxpad = g_new0 (GstRtpPtDemuxPad, 1);
     rtpdemuxpad->pt = pt;
     rtpdemuxpad->newcaps = FALSE;
     rtpdemuxpad->pad = srcpad;
@@ -505,8 +505,31 @@ gst_rtp_pt_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
     gst_pad_set_active (srcpad, TRUE);
 
     /* First push the stream-start event, it must always come first */
-    gst_pad_push_event (srcpad,
-        gst_pad_get_sticky_event (rtpdemux->sink, GST_EVENT_STREAM_START, 0));
+    {
+      gchar *stream_id;
+      GstEvent *sink_event, *event;
+      guint group_id;
+      GstStreamFlags flags;
+
+      sink_event =
+          gst_pad_get_sticky_event (rtpdemux->sink, GST_EVENT_STREAM_START, 0);
+
+      stream_id =
+          gst_pad_create_stream_id_printf (srcpad, GST_ELEMENT_CAST (rtpdemux),
+          "%u", pt);
+
+      event = gst_event_new_stream_start (stream_id);
+      if (gst_event_parse_group_id (sink_event, &group_id)) {
+        gst_event_set_group_id (event, group_id);
+      }
+      gst_event_parse_stream_flags (sink_event, &flags);
+      gst_event_set_stream_flags (event, flags);
+
+      gst_pad_push_event (srcpad, event);
+
+      gst_event_unref (sink_event);
+      g_free (stream_id);
+    }
 
     /* Then caps event is sent */
     gst_pad_set_caps (srcpad, caps);
@@ -720,7 +743,7 @@ gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux)
 
     gst_pad_set_active (pad->pad, FALSE);
     gst_element_remove_pad (GST_ELEMENT_CAST (ptdemux), pad->pad);
-    g_slice_free (GstRtpPtDemuxPad, pad);
+    g_free (pad);
   }
   g_slist_free (tmppads);
 }
diff --git a/gst/rtpmanager/gstrtprtxreceive.c b/gst/rtpmanager/gstrtprtxreceive.c
index 274ceed2263453e002c3d34a56956088c96e7ad0..5d74c2350311190c80d5ce8980ecdeea87bc5aa5 100644
--- a/gst/rtpmanager/gstrtprtxreceive.c
+++ b/gst/rtpmanager/gstrtprtxreceive.c
@@ -381,7 +381,7 @@ typedef struct
 static SsrcAssoc *
 ssrc_assoc_new (guint32 ssrc, GstClockTime time)
 {
-  SsrcAssoc *assoc = g_slice_new (SsrcAssoc);
+  SsrcAssoc *assoc = g_new (SsrcAssoc, 1);
 
   assoc->ssrc = ssrc;
   assoc->time = time;
@@ -392,7 +392,7 @@ ssrc_assoc_new (guint32 ssrc, GstClockTime time)
 static void
 ssrc_assoc_free (SsrcAssoc * assoc)
 {
-  g_slice_free (SsrcAssoc, assoc);
+  g_free (assoc);
 }
 
 static void
diff --git a/gst/rtpmanager/gstrtprtxsend.c b/gst/rtpmanager/gstrtprtxsend.c
index 349bb169be788e4a0f2537b9fb36fab047483b63..1c36e5272f2133c8ae72c84d12daff55345c1c35 100644
--- a/gst/rtpmanager/gstrtprtxsend.c
+++ b/gst/rtpmanager/gstrtprtxsend.c
@@ -169,7 +169,7 @@ static void
 buffer_queue_item_free (BufferQueueItem * item)
 {
   gst_buffer_unref (item->buffer);
-  g_slice_free (BufferQueueItem, item);
+  g_free (item);
 }
 
 typedef struct
@@ -185,7 +185,7 @@ typedef struct
 static SSRCRtxData *
 ssrc_rtx_data_new (guint32 rtx_ssrc)
 {
-  SSRCRtxData *data = g_slice_new0 (SSRCRtxData);
+  SSRCRtxData *data = g_new0 (SSRCRtxData, 1);
 
   data->rtx_ssrc = rtx_ssrc;
   data->next_seqnum = data->seqnum_base = g_random_int_range (0, G_MAXUINT16);
@@ -198,7 +198,7 @@ static void
 ssrc_rtx_data_free (SSRCRtxData * data)
 {
   g_sequence_free (data->queue);
-  g_slice_free (SSRCRtxData, data);
+  g_free (data);
 }
 
 typedef enum
@@ -442,7 +442,7 @@ gst_rtp_rtx_data_queue_item_free (gpointer item)
   GstDataQueueItem *data = item;
   if (data->object)
     gst_mini_object_unref (data->object);
-  g_slice_free (GstDataQueueItem, data);
+  g_free (data);
 }
 
 static gboolean
@@ -451,7 +451,7 @@ gst_rtp_rtx_send_push_out (GstRtpRtxSend * rtx, gpointer object)
   GstDataQueueItem *data;
   gboolean success;
 
-  data = g_slice_new0 (GstDataQueueItem);
+  data = g_new0 (GstDataQueueItem, 1);
   data->object = GST_MINI_OBJECT (object);
   data->size = 1;
   data->duration = 1;
@@ -1042,7 +1042,7 @@ process_buffer (GstRtpRtxSend * rtx, GstBuffer * buffer)
     }
 
     /* add current rtp buffer to queue history */
-    item = g_slice_new0 (BufferQueueItem);
+    item = g_new0 (BufferQueueItem, 1);
     item->seqnum = seqnum;
     item->timestamp = rtptime;
     item->buffer = gst_buffer_ref (buffer);
diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c
index b131e776111ee4ab0ac0c858a4abd4f84b2ac2f7..15dc2fc2d2f60eea51526430d8bd888057448ba7 100644
--- a/gst/rtpmanager/gstrtpsession.c
+++ b/gst/rtpmanager/gstrtpsession.c
@@ -224,6 +224,7 @@ enum
 #define DEFAULT_NTP_TIME_SOURCE      GST_RTP_NTP_TIME_SOURCE_NTP
 #define DEFAULT_RTCP_SYNC_SEND_TIME  TRUE
 #define DEFAULT_UPDATE_NTP64_HEADER_EXT  TRUE
+#define DEFAULT_TIMEOUT_INACTIVE_SOURCES TRUE
 
 enum
 {
@@ -246,7 +247,8 @@ enum
   PROP_RTP_PROFILE,
   PROP_NTP_TIME_SOURCE,
   PROP_RTCP_SYNC_SEND_TIME,
-  PROP_UPDATE_NTP64_HEADER_EXT
+  PROP_UPDATE_NTP64_HEADER_EXT,
+  PROP_TIMEOUT_INACTIVE_SOURCES,
 };
 
 #define GST_RTP_SESSION_LOCK(sess)   g_mutex_lock (&(sess)->priv->lock)
@@ -274,6 +276,9 @@ struct _GstRtpSessionPrivate
   GHashTable *ptmap;
 
   GstClockTime send_latency;
+  /* Set if we warned once already that no latency is configured yet but we
+   * need it to calculate correct send running time of the packets */
+  gboolean warned_latency_once;
 
   gboolean use_pipeline_clock;
   GstRtpNtpTimeSource ntp_time_source;
@@ -289,6 +294,8 @@ struct _GstRtpSessionPrivate
    * pushed a buffer list.
    */
   GstBufferList *processed_list;
+
+  gboolean send_rtp_sink_eos;
 };
 
 /* callbacks to handle actions from the session manager */
@@ -828,6 +835,22 @@ gst_rtp_session_class_init (GstRtpSessionClass * klass)
           DEFAULT_UPDATE_NTP64_HEADER_EXT,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstRtpSession:timeout-inactive-sources:
+   *
+   * Whether inactive sources should be timed out
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_TIMEOUT_INACTIVE_SOURCES,
+      g_param_spec_boolean ("timeout-inactive-sources",
+          "Time out inactive sources",
+          "Whether sources that don't receive RTP or RTCP packets for longer "
+          "than 5x RTCP interval should be removed",
+          DEFAULT_TIMEOUT_INACTIVE_SOURCES,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_rtp_session_change_state);
   gstelement_class->request_new_pad =
@@ -925,6 +948,8 @@ gst_rtp_session_init (GstRtpSession * rtpsession)
   rtpsession->priv->sent_rtx_req_count = 0;
 
   rtpsession->priv->ntp_time_source = DEFAULT_NTP_TIME_SOURCE;
+
+  rtpsession->priv->send_rtp_sink_eos = FALSE;
 }
 
 static void
@@ -1004,6 +1029,10 @@ gst_rtp_session_set_property (GObject * object, guint prop_id,
       g_object_set_property (G_OBJECT (priv->session),
           "update-ntp64-header-ext", value);
       break;
+    case PROP_TIMEOUT_INACTIVE_SOURCES:
+      g_object_set_property (G_OBJECT (priv->session),
+          "timeout-inactive-sources", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1087,6 +1116,10 @@ gst_rtp_session_get_property (GObject * object, guint prop_id,
       g_object_get_property (G_OBJECT (priv->session),
           "update-ntp64-header-ext", value);
       break;
+    case PROP_TIMEOUT_INACTIVE_SOURCES:
+      g_object_get_property (G_OBJECT (priv->session),
+          "timeout-inactive-sources", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1337,12 +1370,16 @@ gst_rtp_session_change_state (GstElement * element, GstStateChange transition)
       GST_RTP_SESSION_LOCK (rtpsession);
       rtpsession->priv->wait_send = TRUE;
       rtpsession->priv->send_latency = GST_CLOCK_TIME_NONE;
+      rtpsession->priv->warned_latency_once = FALSE;
       GST_RTP_SESSION_UNLOCK (rtpsession);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       break;
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
     case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_RTP_SESSION_LOCK (rtpsession);
+      rtpsession->priv->send_rtp_sink_eos = FALSE;
+      GST_RTP_SESSION_UNLOCK (rtpsession);
       /* no need to join yet, we might want to continue later. Also, the
        * dataflow could block downstream so that a join could just block
        * forever. */
@@ -1545,9 +1582,12 @@ gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src,
 
     /* Forward send an EOS on the RTCP sink if we received an EOS on the
      * send_rtp_sink. We don't need to check the recv_rtp_sink since in this
-     * case the EOS event would already have been sent */
-    if (all_sources_bye && rtpsession->send_rtp_sink &&
-        GST_PAD_IS_EOS (rtpsession->send_rtp_sink)) {
+     * case the EOS event would already have been sent. Also, prevent a
+     * race condition between the EOS event handling and rtcp send
+     * function/thread  by using send_rtp_sink_eos directly instead of
+     * GST_PAD_IS_EOS*/
+    GST_RTP_SESSION_LOCK (rtpsession);
+    if (all_sources_bye && rtpsession->priv->send_rtp_sink_eos) {
       GstEvent *event;
 
       GST_LOG_OBJECT (rtpsession, "sending EOS");
@@ -1556,6 +1596,7 @@ gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src,
       gst_event_set_seqnum (event, rtpsession->recv_rtcp_segment_seqnum);
       gst_pad_push_event (rtcp_src, event);
     }
+    GST_RTP_SESSION_UNLOCK (rtpsession);
     gst_object_unref (rtcp_src);
   } else {
     GST_RTP_SESSION_UNLOCK (rtpsession);
@@ -1754,6 +1795,9 @@ gst_rtp_session_event_recv_rtp_sink (GstPad * pad, GstObject * parent,
       gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED);
       rtpsession->recv_rtcp_segment_seqnum = GST_SEQNUM_INVALID;
       ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
+      GST_RTP_SESSION_LOCK (rtpsession);
+      rtpsession->priv->send_rtp_sink_eos = FALSE;
+      GST_RTP_SESSION_UNLOCK (rtpsession);
       break;
     case GST_EVENT_SEGMENT:
     {
@@ -2253,6 +2297,9 @@ gst_rtp_session_event_send_rtp_sink (GstPad * pad, GstObject * parent,
        * because we stop sending. */
       ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
       current_time = gst_clock_get_time (rtpsession->priv->sysclock);
+      GST_RTP_SESSION_LOCK (rtpsession);
+      rtpsession->priv->send_rtp_sink_eos = TRUE;
+      GST_RTP_SESSION_UNLOCK (rtpsession);
 
       GST_DEBUG_OBJECT (rtpsession, "scheduling BYE message");
       rtp_session_mark_all_bye (rtpsession->priv->session, "End Of Stream");
@@ -2432,8 +2479,14 @@ gst_rtp_session_chain_send_rtp_common (GstRtpSession * rtpsession,
       if (priv->send_latency != GST_CLOCK_TIME_NONE) {
         running_time += priv->send_latency;
       } else {
-        GST_WARNING_OBJECT (rtpsession,
-            "Can't determine running time for this packet without knowing configured latency");
+        if (!priv->warned_latency_once) {
+          priv->warned_latency_once = TRUE;
+          GST_WARNING_OBJECT (rtpsession,
+              "Can't determine running time for this packet without knowing configured latency");
+        } else {
+          GST_LOG_OBJECT (rtpsession,
+              "Can't determine running time for this packet without knowing configured latency");
+        }
         running_time = -1;
       }
     }
@@ -2954,6 +3007,8 @@ gst_rtp_session_notify_twcc (RTPSession * sess,
     event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, twcc_packets);
     gst_pad_push_event (send_rtp_sink, event);
     gst_object_unref (send_rtp_sink);
+  } else {
+    gst_structure_free (twcc_packets);
   }
 
   g_object_notify (G_OBJECT (rtpsession), "twcc-stats");
diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c
index 9713131e1b1f5f5ed78a3fd996f4304f0d062a02..243449be28fbfa2bf311febe2e004bed83dc04df 100644
--- a/gst/rtpmanager/gstrtpssrcdemux.c
+++ b/gst/rtpmanager/gstrtpssrcdemux.c
@@ -226,6 +226,30 @@ add_ssrc_and_ref (GstEvent * event, guint32 ssrc)
       gst_caps_unref (newcaps);
       break;
     }
+    case GST_EVENT_STREAM_START:
+    {
+      const gchar *stream_id;
+      gchar *new_stream_id;
+      guint group_id;
+      GstStreamFlags flags;
+      GstEvent *new_event;
+
+      gst_event_parse_stream_start (event, &stream_id);
+
+      new_stream_id =
+          g_strdup_printf ("%s/%u", stream_id ? stream_id : "", ssrc);
+      new_event = gst_event_new_stream_start (new_stream_id);
+      g_free (new_stream_id);
+
+      if (gst_event_parse_group_id (event, &group_id))
+        gst_event_set_group_id (new_event, group_id);
+      gst_event_parse_stream_flags (event, &flags);
+      gst_event_set_stream_flags (new_event, flags);
+
+      event = new_event;
+
+      break;
+    }
     default:
       gst_event_ref (event);
       break;
diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c
index 1ab0aaf6009a12248feb7d46a02d1b4ef5331a3d..067657c0ab61b065c66a67994c8537111eff90e8 100644
--- a/gst/rtpmanager/rtpjitterbuffer.c
+++ b/gst/rtpmanager/rtpjitterbuffer.c
@@ -21,6 +21,7 @@
 
 #include <gst/rtp/gstrtpbuffer.h>
 #include <gst/rtp/gstrtcpbuffer.h>
+#include <gst/net/net.h>
 
 #include "rtpjitterbuffer.h"
 
@@ -112,9 +113,6 @@ rtp_jitter_buffer_finalize (GObject * object)
   if (jbuf->pipeline_clock)
     gst_object_unref (jbuf->pipeline_clock);
 
-  /* We cannot use g_queue_clear() as it would pass the wrong size to
-   * g_slice_free() which may lead to data corruption in the slice allocator.
-   */
   rtp_jitter_buffer_flush (jbuf, NULL, NULL);
 
   g_mutex_clear (&jbuf->clock_lock);
@@ -218,6 +216,50 @@ rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
   return jbuf->clock_rate;
 }
 
+static gboolean
+same_clock (GstClock * a, GstClock * b)
+{
+  if (a == b)
+    return TRUE;
+
+  if (GST_IS_NTP_CLOCK (a) && GST_IS_NTP_CLOCK (b)) {
+    gchar *a_addr, *b_addr;
+    gint a_port, b_port;
+    gboolean same;
+
+    g_object_get (a, "address", &a_addr, "port", &a_port, NULL);
+    g_object_get (b, "address", &b_addr, "port", &b_port, NULL);
+
+    same = (g_strcmp0 (a_addr, b_addr) == 0 && a_port == b_port);
+    g_free (a_addr);
+    g_free (b_addr);
+
+    if (same)
+      return TRUE;
+  } else if (GST_IS_PTP_CLOCK (a) && GST_IS_PTP_CLOCK (b)) {
+    guint a_domain, b_domain;
+
+    g_object_get (a, "domain", &a_domain, NULL);
+    g_object_get (b, "domain", &b_domain, NULL);
+
+    if (a_domain == b_domain)
+      return TRUE;
+    /* need to check the exact type because almost every clock is a subclass
+     * of GstSystemClock but would have a completely different behaviour */
+  } else if (G_OBJECT_TYPE (a) == G_OBJECT_TYPE (b)
+      && G_OBJECT_TYPE (a) == GST_TYPE_SYSTEM_CLOCK) {
+    GstClockType a_type, b_type;
+
+    g_object_get (a, "clock-type", &a_type, NULL);
+    g_object_get (b, "clock-type", &b_type, NULL);
+
+    if (a_type == b_type)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
 static void
 media_clock_synced_cb (GstClock * clock, gboolean synced,
     RTPJitterBuffer * jbuf)
@@ -225,7 +267,8 @@ media_clock_synced_cb (GstClock * clock, gboolean synced,
   GstClockTime internal, external;
 
   g_mutex_lock (&jbuf->clock_lock);
-  if (jbuf->pipeline_clock) {
+  if (jbuf->pipeline_clock
+      && !same_clock (jbuf->pipeline_clock, jbuf->media_clock)) {
     internal = gst_clock_get_internal_time (jbuf->media_clock);
     external = gst_clock_get_time (jbuf->pipeline_clock);
 
@@ -245,7 +288,8 @@ media_clock_synced_cb (GstClock * clock, gboolean synced,
  */
 void
 rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
-    guint64 clock_offset)
+    guint64 clock_offset, gint64 clock_correction,
+    gboolean reference_timestamp_meta_only)
 {
   g_mutex_lock (&jbuf->clock_lock);
   if (jbuf->media_clock) {
@@ -257,22 +301,29 @@ rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
   }
   jbuf->media_clock = clock;
   jbuf->media_clock_offset = clock_offset;
+  jbuf->media_clock_correction = clock_correction;
+  jbuf->media_clock_reference_timestamp_meta_only =
+      reference_timestamp_meta_only;
+
+  if (jbuf->pipeline_clock && jbuf->media_clock) {
+    if (same_clock (jbuf->pipeline_clock, jbuf->media_clock)) {
+      gst_clock_set_master (jbuf->media_clock, NULL);
+      gst_clock_set_calibration (jbuf->media_clock, 0, 0, 1, 1);
+    } else {
+      jbuf->media_clock_synced_id =
+          g_signal_connect (jbuf->media_clock, "synced",
+          G_CALLBACK (media_clock_synced_cb), jbuf);
+      if (gst_clock_is_synced (jbuf->media_clock)) {
+        GstClockTime internal, external;
 
-  if (jbuf->pipeline_clock && jbuf->media_clock &&
-      jbuf->pipeline_clock != jbuf->media_clock) {
-    jbuf->media_clock_synced_id =
-        g_signal_connect (jbuf->media_clock, "synced",
-        G_CALLBACK (media_clock_synced_cb), jbuf);
-    if (gst_clock_is_synced (jbuf->media_clock)) {
-      GstClockTime internal, external;
+        internal = gst_clock_get_internal_time (jbuf->media_clock);
+        external = gst_clock_get_time (jbuf->pipeline_clock);
 
-      internal = gst_clock_get_internal_time (jbuf->media_clock);
-      external = gst_clock_get_time (jbuf->pipeline_clock);
+        gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
+      }
 
-      gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
+      gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
     }
-
-    gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
   }
   g_mutex_unlock (&jbuf->clock_lock);
 }
@@ -293,19 +344,26 @@ rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
     gst_object_unref (jbuf->pipeline_clock);
   jbuf->pipeline_clock = clock ? gst_object_ref (clock) : NULL;
 
-  if (jbuf->pipeline_clock && jbuf->media_clock &&
-      jbuf->pipeline_clock != jbuf->media_clock) {
-    if (gst_clock_is_synced (jbuf->media_clock)) {
-      GstClockTime internal, external;
+  if (jbuf->pipeline_clock && jbuf->media_clock) {
+    if (same_clock (jbuf->pipeline_clock, jbuf->media_clock)) {
+      gst_clock_set_master (jbuf->media_clock, NULL);
+      gst_clock_set_calibration (jbuf->media_clock, 0, 0, 1, 1);
+    } else {
+      if (gst_clock_is_synced (jbuf->media_clock)) {
+        GstClockTime internal, external;
 
-      internal = gst_clock_get_internal_time (jbuf->media_clock);
-      external = gst_clock_get_time (jbuf->pipeline_clock);
+        internal = gst_clock_get_internal_time (jbuf->media_clock);
+        external = gst_clock_get_time (jbuf->pipeline_clock);
 
-      gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
-    }
+        gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
+      }
 
-    gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
+      gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
+    }
+  } else if (!jbuf->pipeline_clock && jbuf->media_clock) {
+    gst_clock_set_master (jbuf->media_clock, NULL);
   }
+
   g_mutex_unlock (&jbuf->clock_lock);
 }
 
@@ -709,6 +767,8 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
   GstClockTime gstrtptime, pts;
   GstClock *media_clock, *pipeline_clock;
   guint64 media_clock_offset;
+  gint64 media_clock_correction;
+  gboolean media_clock_reference_timestamp_meta_only;
   gboolean rfc7273_mode;
 
   *p_ntp_time = GST_CLOCK_TIME_NONE;
@@ -755,6 +815,9 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
   pipeline_clock =
       jbuf->pipeline_clock ? gst_object_ref (jbuf->pipeline_clock) : NULL;
   media_clock_offset = jbuf->media_clock_offset;
+  media_clock_correction = jbuf->media_clock_correction;
+  media_clock_reference_timestamp_meta_only =
+      jbuf->media_clock_reference_timestamp_meta_only;
   g_mutex_unlock (&jbuf->clock_lock);
 
   gstrtptime =
@@ -890,6 +953,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
 
     /* Current NTP clock estimation */
     ntptime = gst_clock_get_internal_time (media_clock);
+    ntptime += media_clock_correction;
 
     /* Current RTP time based on the estimated NTP clock and the corresponding
      * RTP time period start */
@@ -984,22 +1048,33 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
 
     *p_ntp_time = ntptime;
 
-    /* Packet timestamp converted to the pipeline clock.
-     * Note that this includes again inaccuracy caused by the estimation of
-     * the NTP vs. pipeline clock. */
-    rtpsystime =
-        gst_clock_adjust_with_calibration (media_clock, ntptime, internal,
-        external, rate_num, rate_denom);
-
-    /* All this assumes that the pipeline has enough additional
-     * latency to cover for the network delay */
-    if (rtpsystime > base_time)
-      pts = rtpsystime - base_time;
-    else
-      pts = 0;
+    if (media_clock_reference_timestamp_meta_only) {
+      /* do skew calculation by measuring the difference between rtptime and the
+       * receive dts, this function will return the skew corrected rtptime. */
+      pts = calculate_skew (jbuf, ext_rtptime, gstrtptime, dts, gap, is_rtx);
+    } else {
+      if (media_clock_correction < 0 || ntptime >= media_clock_correction)
+        ntptime -= media_clock_correction;
+      else
+        ntptime = 0;
+
+      /* Packet timestamp converted to the pipeline clock.
+       * Note that this includes again inaccuracy caused by the estimation of
+       * the NTP vs. pipeline clock. */
+      rtpsystime =
+          gst_clock_adjust_with_calibration (media_clock, ntptime, internal,
+          external, rate_num, rate_denom);
+
+      /* All this assumes that the pipeline has enough additional
+       * latency to cover for the network delay */
+      if (rtpsystime > base_time)
+        pts = rtpsystime - base_time;
+      else
+        pts = 0;
 
-    GST_DEBUG ("Packet pipeline clock time %" GST_TIME_FORMAT ", PTS %"
-        GST_TIME_FORMAT, GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (pts));
+      GST_DEBUG ("Packet pipeline clock time %" GST_TIME_FORMAT ", PTS %"
+          GST_TIME_FORMAT, GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (pts));
+    }
   } else {
     /* If we used the RFC7273 clock before and not anymore,
      * we need to resync it later again */
@@ -1007,7 +1082,9 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
 
     /* do skew calculation by measuring the difference between rtptime and the
      * receive dts, this function will return the skew corrected rtptime. */
-    pts = calculate_skew (jbuf, ext_rtptime, gstrtptime, dts, gap, is_rtx);
+    pts =
+        calculate_skew (jbuf, ext_rtptime, gstrtptime, estimated_dts ? -1 : dts,
+        gap, is_rtx);
   }
 
   /* check if timestamps are not going backwards, we can only check this if we
@@ -1078,6 +1155,11 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
   GList *list, *event = NULL;
   guint16 seqnum;
 
+  if (G_LIKELY (head))
+    *head = FALSE;
+  if (percent)
+    *percent = -1;
+
   g_return_val_if_fail (jbuf != NULL, FALSE);
   g_return_val_if_fail (item != NULL, FALSE);
 
@@ -1177,7 +1259,7 @@ rtp_jitter_buffer_alloc_item (gpointer data, guint type, GstClockTime dts,
 {
   RTPJitterBufferItem *item;
 
-  item = g_slice_new (RTPJitterBufferItem);
+  item = g_new (RTPJitterBufferItem, 1);
   item->data = data;
   item->next = NULL;
   item->prev = NULL;
@@ -1232,7 +1314,7 @@ rtp_jitter_buffer_append_query (RTPJitterBuffer * jbuf, GstQuery * query)
   RTPJitterBufferItem *item =
       rtp_jitter_buffer_alloc_item (query, ITEM_TYPE_QUERY, -1, -1, -1, 0, -1,
       NULL);
-  gboolean head;
+  gboolean head = FALSE;
   rtp_jitter_buffer_insert (jbuf, item, &head, NULL);
   return head;
 }
@@ -1629,5 +1711,5 @@ rtp_jitter_buffer_free_item (RTPJitterBufferItem * item)
 
   if (item->data && item->free_data)
     item->free_data (item->data);
-  g_slice_free (RTPJitterBufferItem, item);
+  g_free (item);
 }
diff --git a/gst/rtpmanager/rtpjitterbuffer.h b/gst/rtpmanager/rtpjitterbuffer.h
index d0e60c275d4e59e6660e8dcdc92e9023d2360bac..bf60703884e17058d84a8bfbb61da48e9e0b14cd 100644
--- a/gst/rtpmanager/rtpjitterbuffer.h
+++ b/gst/rtpmanager/rtpjitterbuffer.h
@@ -108,6 +108,8 @@ struct _RTPJitterBuffer {
   GstClock      *media_clock;
   gulong         media_clock_synced_id;
   guint64        media_clock_offset;
+  gint64         media_clock_correction;
+  gboolean       media_clock_reference_timestamp_meta_only;
 
   gboolean       rfc7273_sync;
 };
@@ -172,7 +174,7 @@ void                  rtp_jitter_buffer_set_delay        (RTPJitterBuffer *jbuf,
 void                  rtp_jitter_buffer_set_clock_rate   (RTPJitterBuffer *jbuf, guint32 clock_rate);
 guint32               rtp_jitter_buffer_get_clock_rate   (RTPJitterBuffer *jbuf);
 
-void                  rtp_jitter_buffer_set_media_clock  (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset);
+void                  rtp_jitter_buffer_set_media_clock  (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset, gint64 clock_correction, gboolean reference_timestamp_meta_only);
 void                  rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer *jbuf, GstClock * clock);
 
 gboolean              rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer *jbuf);
diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c
index 294bcf0c1714e19f4bae6d96196dcf12d3b9c92e..9dbf424fc0c267865476de2421709ffa3bf4f60f 100644
--- a/gst/rtpmanager/rtpsession.c
+++ b/gst/rtpmanager/rtpsession.c
@@ -82,6 +82,7 @@ enum
 #define DEFAULT_FAVOR_NEW            FALSE
 #define DEFAULT_TWCC_FEEDBACK_INTERVAL GST_CLOCK_TIME_NONE
 #define DEFAULT_UPDATE_NTP64_HEADER_EXT TRUE
+#define DEFAULT_TIMEOUT_INACTIVE_SOURCES TRUE
 
 enum
 {
@@ -110,6 +111,7 @@ enum
   PROP_RTCP_DISABLE_SR_TIMESTAMP,
   PROP_TWCC_FEEDBACK_INTERVAL,
   PROP_UPDATE_NTP64_HEADER_EXT,
+  PROP_TIMEOUT_INACTIVE_SOURCES,
   PROP_LAST,
 };
 
@@ -660,6 +662,21 @@ rtp_session_class_init (RTPSessionClass * klass)
       DEFAULT_UPDATE_NTP64_HEADER_EXT,
       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+  /**
+   * RTPSession:timeout-inactive-sources:
+   *
+   * Whether inactive sources should be timed out
+   *
+   * Since: 1.24
+   */
+  properties[PROP_TIMEOUT_INACTIVE_SOURCES] =
+      g_param_spec_boolean ("timeout-inactive-sources",
+      "Time out inactive sources",
+      "Whether sources that don't receive RTP or RTCP packets for longer "
+      "than 5x RTCP interval should be removed",
+      DEFAULT_TIMEOUT_INACTIVE_SOURCES,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
 
   klass->get_source_by_ssrc =
@@ -706,6 +723,7 @@ rtp_session_init (RTPSession * sess)
   sess->mtu = DEFAULT_RTCP_MTU;
 
   sess->update_ntp64_header_ext = DEFAULT_UPDATE_NTP64_HEADER_EXT;
+  sess->timeout_inactive_sources = DEFAULT_TIMEOUT_INACTIVE_SOURCES;
 
   sess->probation = DEFAULT_PROBATION;
   sess->max_dropout_time = DEFAULT_MAX_DROPOUT_TIME;
@@ -950,6 +968,9 @@ rtp_session_set_property (GObject * object, guint prop_id,
     case PROP_UPDATE_NTP64_HEADER_EXT:
       sess->update_ntp64_header_ext = g_value_get_boolean (value);
       break;
+    case PROP_TIMEOUT_INACTIVE_SOURCES:
+      sess->timeout_inactive_sources = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1039,6 +1060,9 @@ rtp_session_get_property (GObject * object, guint prop_id,
     case PROP_UPDATE_NTP64_HEADER_EXT:
       g_value_set_boolean (value, sess->update_ntp64_header_ext);
       break;
+    case PROP_TIMEOUT_INACTIVE_SOURCES:
+      g_value_set_boolean (value, sess->timeout_inactive_sources);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1189,6 +1213,7 @@ rtp_session_reset (RTPSession * sess)
 {
   g_return_if_fail (RTP_IS_SESSION (sess));
 
+  RTP_SESSION_LOCK (sess);
   /* remove all sources */
   g_hash_table_remove_all (sess->ssrcs[sess->mask_idx]);
   sess->total_sources = 0;
@@ -1217,6 +1242,7 @@ rtp_session_reset (RTPSession * sess)
   g_list_free_full (sess->conflicting_addresses,
       (GDestroyNotify) rtp_conflicting_address_free);
   sess->conflicting_addresses = NULL;
+  RTP_SESSION_UNLOCK (sess);
 }
 
 /**
@@ -2972,8 +2998,12 @@ rtp_session_process_twcc (RTPSession * sess, guint32 sender_ssrc,
 
   RTP_SESSION_UNLOCK (sess);
   if (sess->callbacks.notify_twcc)
-    sess->callbacks.notify_twcc (sess, twcc_packets_s, twcc_stats_s,
-        sess->notify_twcc_user_data);
+    sess->callbacks.notify_twcc (sess, g_steal_pointer (&twcc_packets_s),
+        g_steal_pointer (&twcc_stats_s), sess->notify_twcc_user_data);
+  else {
+    gst_structure_free (twcc_packets_s);
+    gst_structure_free (twcc_stats_s);
+  }
   RTP_SESSION_LOCK (sess);
 }
 
@@ -3788,12 +3818,12 @@ typedef struct
   GstRTCPPacket packet;
   gboolean has_sdes;
   gboolean is_early;
-  gboolean may_suppress;
   GQueue output;
   guint nacked_seqnums;
+  gboolean timeout_inactive_sources;
 } ReportData;
 
-static void
+static gboolean
 session_start_rtcp (RTPSession * sess, ReportData * data)
 {
   GstRTCPPacket *packet = &data->packet;
@@ -3818,8 +3848,11 @@ session_start_rtcp (RTPSession * sess, ReportData * data)
     gst_rtcp_buffer_add_packet (rtcp, GST_RTCP_TYPE_SR, packet);
 
     /* get latest stats */
-    rtp_source_get_new_sr (own, data->ntpnstime, data->running_time,
-        &ntptime, &rtptime, &packet_count, &octet_count);
+    if (!rtp_source_get_new_sr (own, data->ntpnstime, data->running_time,
+            &ntptime, &rtptime, &packet_count, &octet_count)) {
+      gst_rtcp_buffer_unmap (&data->rtcpbuf);
+      return FALSE;
+    }
     /* store stats */
     rtp_source_process_sr (own, data->current_time, ntptime, rtptime,
         packet_count, octet_count);
@@ -3835,6 +3868,8 @@ session_start_rtcp (RTPSession * sess, ReportData * data)
     gst_rtcp_buffer_add_packet (rtcp, GST_RTCP_TYPE_RR, packet);
     gst_rtcp_packet_rr_set_ssrc (packet, own->ssrc);
   }
+
+  return TRUE;
 }
 
 /* construct a Sender or Receiver Report */
@@ -3951,8 +3986,6 @@ session_fir (RTPSession * sess, ReportData * data)
 
   if (gst_rtcp_packet_fb_get_fci_length (packet) == 0)
     gst_rtcp_packet_remove (packet);
-  else
-    data->may_suppress = FALSE;
 }
 
 static gboolean
@@ -3998,7 +4031,6 @@ session_pli (const gchar * key, RTPSource * source, ReportData * data)
   gst_rtcp_packet_fb_set_media_ssrc (packet, source->ssrc);
 
   source->send_pli = FALSE;
-  data->may_suppress = FALSE;
 
   source->stats.sent_pli_count++;
 }
@@ -4114,7 +4146,6 @@ session_nack (const gchar * key, RTPSource * source, ReportData * data)
 done:
   data->nacked_seqnums += nacked_seqnums;
   rtp_source_clear_nacks (source, nacked_seqnums);
-  data->may_suppress = FALSE;
 }
 
 /* perform cleanup of sources that timed out */
@@ -4184,27 +4215,29 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
     remove = TRUE;
   }
 
-  /* sources that were inactive for more than 5 times the deterministic reporting
-   * interval get timed out. the min timeout is 5 seconds. */
-  /* mind old time that might pre-date last time going to PLAYING */
-  btime = MAX (source->last_activity, sess->start_time);
-  if (data->current_time > btime) {
-    interval = MAX (binterval * 5, 5 * GST_SECOND);
-    if (data->current_time - btime > interval) {
-      GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
-          source->ssrc, GST_TIME_ARGS (btime));
-      if (source->internal) {
-        /* this is an internal source that is not using our suggested ssrc.
-         * since there must be another source using this ssrc, we can remove
-         * this one instead of making it a receiver forever */
-        if (source->ssrc != sess->suggested_ssrc
-            && source->media_ssrc != sess->suggested_ssrc) {
-          rtp_source_mark_bye (source, "timed out");
-          /* do not schedule bye here, since we are inside the RTCP timeout
-           * processing and scheduling bye will interfere with SR/RR sending */
+  if (data->timeout_inactive_sources) {
+    /* sources that were inactive for more than 5 times the deterministic reporting
+     * interval get timed out. the min timeout is 5 seconds. */
+    /* mind old time that might pre-date last time going to PLAYING */
+    btime = MAX (source->last_activity, sess->start_time);
+    if (data->current_time > btime) {
+      interval = MAX (binterval * 5, 5 * GST_SECOND);
+      if (data->current_time - btime > interval) {
+        GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
+            source->ssrc, GST_TIME_ARGS (btime));
+        if (source->internal) {
+          /* this is an internal source that is not using our suggested ssrc.
+           * since there must be another source using this ssrc, we can remove
+           * this one instead of making it a receiver forever */
+          if (source->ssrc != sess->suggested_ssrc
+              && source->media_ssrc != sess->suggested_ssrc) {
+            rtp_source_mark_bye (source, "timed out");
+            /* do not schedule bye here, since we are inside the RTCP timeout
+             * processing and scheduling bye will interfere with SR/RR sending */
+          }
+        } else {
+          remove = TRUE;
         }
-      } else {
-        remove = TRUE;
       }
     }
   }
@@ -4482,7 +4515,7 @@ generate_twcc (const gchar * key, RTPSource * source, ReportData * data)
   GST_DEBUG ("generating TWCC feedback for source %08x", source->ssrc);
 
   while ((buf = rtp_twcc_manager_get_feedback (sess->twcc, source->ssrc))) {
-    ReportOutput *output = g_slice_new (ReportOutput);
+    ReportOutput *output = g_new (ReportOutput, 1);
     output->source = g_object_ref (source);
     output->is_bye = FALSE;
     output->buffer = buf;
@@ -4517,7 +4550,10 @@ generate_rtcp (const gchar * key, RTPSource * source, ReportData * data)
   data->source = source;
 
   /* open packet */
-  session_start_rtcp (sess, data);
+  if (!session_start_rtcp (sess, data)) {
+    GST_WARNING ("source %08x can not generate RTCP", source->ssrc);
+    return;
+  }
 
   if (source->marked_bye) {
     /* send BYE */
@@ -4546,7 +4582,7 @@ generate_rtcp (const gchar * key, RTPSource * source, ReportData * data)
 
   gst_rtcp_buffer_unmap (&data->rtcpbuf);
 
-  output = g_slice_new (ReportOutput);
+  output = g_new (ReportOutput, 1);
   output->source = g_object_ref (source);
   output->is_bye = is_bye;
   output->buffer = data->rtcp;
@@ -4656,8 +4692,8 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
   data.ntpnstime = ntpnstime;
   data.running_time = running_time;
   data.num_to_report = 0;
-  data.may_suppress = FALSE;
   data.nacked_seqnums = 0;
+  data.timeout_inactive_sources = sess->timeout_inactive_sources;
   g_queue_init (&data.output);
 
   RTP_SESSION_LOCK (sess);
@@ -4710,20 +4746,27 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
   /* check if all the buffers are empty after generation */
   all_empty = TRUE;
 
+  /* Make a local copy of the hashtable. We need to do this because the
+   * generate_rtcp stage below releases the session lock. */
+  table_copy = g_hash_table_new_full (NULL, NULL, NULL,
+      (GDestroyNotify) g_object_unref);
+  g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
+      (GHFunc) clone_ssrcs_hashtable, table_copy);
+
   GST_DEBUG
-      ("doing RTCP generation %u for %u sources, early %d, may suppress %d",
-      sess->generation, data.num_to_report, data.is_early, data.may_suppress);
+      ("doing RTCP generation %u for %u sources, early %d",
+      sess->generation, data.num_to_report, data.is_early);
 
-  /* generate RTCP for all internal sources */
-  g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
-      (GHFunc) generate_rtcp, &data);
+  /* generate RTCP for all internal sources, this might release the
+   * session lock. */
+  g_hash_table_foreach (table_copy, (GHFunc) generate_rtcp, &data);
 
-  g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
-      (GHFunc) generate_twcc, &data);
+  g_hash_table_foreach (table_copy, (GHFunc) generate_twcc, &data);
 
   /* update the generation for all the sources that have been reported */
-  g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
-      (GHFunc) update_generation, &data);
+  g_hash_table_foreach (table_copy, (GHFunc) update_generation, &data);
+
+  g_hash_table_destroy (table_copy);
 
   /* we keep track of the last report time in order to timeout inactive
    * receivers or senders */
@@ -4761,13 +4804,16 @@ done:
     g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDING_RTCP], 0,
         buffer, data.is_early, &do_not_suppress);
 
+    /* do_not_suppress is ignored because we never ever suppress RTCP packets
+     * here. Suppression of regular RTCP happens by simply not scheduling this
+     * function unless needed */
+
     empty_buffer = gst_buffer_get_size (buffer) == 0;
 
     if (!empty_buffer)
       all_empty = FALSE;
 
-    if (sess->callbacks.send_rtcp &&
-        !empty_buffer && (do_not_suppress || !data.may_suppress)) {
+    if (sess->callbacks.send_rtcp && !empty_buffer) {
       guint packet_size;
 
       packet_size = gst_buffer_get_size (buffer) + sess->header_len;
@@ -4785,9 +4831,7 @@ done:
       RTP_SESSION_UNLOCK (sess);
     } else {
       GST_DEBUG ("freeing packet callback: %p"
-          " empty_buffer: %d, "
-          " do_not_suppress: %d may_suppress: %d", sess->callbacks.send_rtcp,
-          empty_buffer, do_not_suppress, data.may_suppress);
+          " empty_buffer: %d", sess->callbacks.send_rtcp, empty_buffer);
       if (!empty_buffer) {
         RTP_SESSION_LOCK (sess);
         sess->stats.nacks_dropped += data.nacked_seqnums;
@@ -4796,7 +4840,7 @@ done:
       gst_buffer_unref (buffer);
     }
     g_object_unref (source);
-    g_slice_free (ReportOutput, output);
+    g_free (output);
   }
 
   if (all_empty)
diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h
index 84b2948dc47730d191112b92a207b72c86af5c69..79f0b1f2fb0b7ad3dda8c23698d66e790068b705 100644
--- a/gst/rtpmanager/rtpsession.h
+++ b/gst/rtpmanager/rtpsession.h
@@ -159,6 +159,9 @@ typedef void (*RTPSessionNotifyNACK) (RTPSession *sess,
 
 /**
  * RTPSessionNotifyTWCC:
+ * @sess: an #RTPSession
+ * @twcc_packets: (transfer full): TWCC packets #GstStructure
+ * @twcc_stats: (transfer full): TWCC stats #GstStructure
  * @user_data: user data specified when registering
  *
  * Notifies of Transport-wide congestion control packets and stats.
@@ -276,7 +279,6 @@ struct _RTPSession {
   GstClockTime  last_rtcp_interval;   /* T_rr */
   GstClockTime  start_time;
   gboolean      first_rtcp;
-  gboolean      allow_early;
 
   GstClockTime  next_early_rtcp_time;
 
@@ -315,6 +317,8 @@ struct _RTPSession {
 
   gboolean update_ntp64_header_ext;
 
+  gboolean timeout_inactive_sources;
+
   /* Transport-wide cc-extension */
   RTPTWCCManager *twcc;
   RTPTWCCStats *twcc_stats;
diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c
index c221c1f229910fb08384e07f2a5ea8bf4f134ac8..2c4fe559bd2536c90dbca4902831f6f65e142125 100644
--- a/gst/rtpmanager/rtpsource.c
+++ b/gst/rtpmanager/rtpsource.c
@@ -328,7 +328,7 @@ void
 rtp_conflicting_address_free (RTPConflictingAddress * addr)
 {
   g_object_unref (addr->address);
-  g_slice_free (RTPConflictingAddress, addr);
+  g_free (addr);
 }
 
 static void
@@ -1415,7 +1415,8 @@ rtp_source_send_rtp (RTPSource * src, RTPPacketInfo * pinfo)
 
   running_time = pinfo->running_time;
 
-  do_bitrate_estimation (src, running_time, &src->bytes_sent);
+  if (GST_CLOCK_TIME_IS_VALID (running_time))
+    do_bitrate_estimation (src, running_time, &src->bytes_sent);
 
   rtptime = pinfo->rtptime;
 
@@ -1427,7 +1428,9 @@ rtp_source_send_rtp (RTPSource * src, RTPPacketInfo * pinfo)
 
   if (ext_rtptime > src->last_rtptime) {
     rtp_diff = ext_rtptime - src->last_rtptime;
-    rt_diff = running_time - src->last_rtime;
+    rt_diff =
+        GST_CLOCK_TIME_IS_VALID (running_time) ? running_time -
+        src->last_rtime : GST_CLOCK_TIME_NONE;
 
     /* calc the diff so we can detect drift at the sender. This can also be used
      * to guestimate the clock rate if the NTP time is locked to the RTP
@@ -1436,10 +1439,12 @@ rtp_source_send_rtp (RTPSource * src, RTPPacketInfo * pinfo)
         GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (rt_diff));
   }
 
-  /* we keep track of the last received RTP timestamp and the corresponding
-   * buffer running_time so that we can use this info when constructing SR reports */
-  src->last_rtime = running_time;
-  src->last_rtptime = ext_rtptime;
+  if (GST_CLOCK_TIME_IS_VALID (running_time)) {
+    /* we keep track of the last received RTP timestamp and the corresponding
+     * buffer running_time so that we can use this info when constructing SR reports */
+    src->last_rtime = running_time;
+    src->last_rtptime = ext_rtptime;
+  }
 
   /* push packet */
   if (!src->callbacks.push_rtp)
@@ -1622,6 +1627,13 @@ rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime,
   }
 
   if (src->clock_rate != -1) {
+    /* if no running time has been set yet we wait until we get one */
+    if (src->last_rtime == -1) {
+      GST_WARNING ("running time not set, can not create SR for SSRC %u",
+          src->ssrc);
+      return FALSE;
+    }
+
     /* get the diff between the clock running_time and the buffer running_time.
      * This is the elapsed time, as measured against the pipeline clock, between
      * when the rtp timestamp was observed and the current running_time.
@@ -1872,7 +1884,7 @@ add_conflicting_address (GList * conflicting_addresses,
 {
   RTPConflictingAddress *new_conflict;
 
-  new_conflict = g_slice_new (RTPConflictingAddress);
+  new_conflict = g_new (RTPConflictingAddress, 1);
 
   new_conflict->address = G_SOCKET_ADDRESS (g_object_ref (address));
   new_conflict->time = time;
diff --git a/gst/rtpmanager/rtptimerqueue.c b/gst/rtpmanager/rtptimerqueue.c
index 6cee0266e3f0d34dbb957d080f65957c9ec3ddf5..f4e20bea78641a1775262073aaa47ff2e164d9f5 100644
--- a/gst/rtpmanager/rtptimerqueue.c
+++ b/gst/rtpmanager/rtptimerqueue.c
@@ -39,7 +39,7 @@ G_DEFINE_TYPE (RtpTimerQueue, rtp_timer_queue, G_TYPE_OBJECT);
 static RtpTimer *
 rtp_timer_new (void)
 {
-  return g_slice_new0 (RtpTimer);
+  return g_new0 (RtpTimer, 1);
 }
 
 static inline void
@@ -329,7 +329,7 @@ rtp_timer_free (RtpTimer * timer)
   g_return_if_fail (timer->list.next == NULL);
   g_return_if_fail (timer->list.prev == NULL);
 
-  g_slice_free (RtpTimer, timer);
+  g_free (timer);
 }
 
 /**
@@ -343,7 +343,7 @@ rtp_timer_free (RtpTimer * timer)
 RtpTimer *
 rtp_timer_dup (const RtpTimer * timer)
 {
-  RtpTimer *copy = g_slice_new (RtpTimer);
+  RtpTimer *copy = g_new (RtpTimer, 1);
   memcpy (copy, timer, sizeof (RtpTimer));
   memset (&copy->list, 0, sizeof (GList));
   copy->queued = FALSE;
diff --git a/gst/rtpmanager/rtptwcc.c b/gst/rtpmanager/rtptwcc.c
index ba8b5a854fe2224a1404be4e32082d5c8672f11e..bd42987961954f4e3307a1d5426c2da25a1879ea 100644
--- a/gst/rtpmanager/rtptwcc.c
+++ b/gst/rtpmanager/rtptwcc.c
@@ -103,6 +103,9 @@ struct _RTPTWCCManager
 
   GstClockTime next_feedback_send_time;
   GstClockTime feedback_interval;
+
+  guint64 remote_ts_base;
+  gint64 base_time_prev;
 };
 
 G_DEFINE_TYPE (RTPTWCCManager, rtp_twcc_manager, G_TYPE_OBJECT);
@@ -124,6 +127,8 @@ rtp_twcc_manager_init (RTPTWCCManager * twcc)
 
   twcc->feedback_interval = GST_CLOCK_TIME_NONE;
   twcc->next_feedback_send_time = GST_CLOCK_TIME_NONE;
+
+  twcc->remote_ts_base = -1;
 }
 
 static void
@@ -598,6 +603,20 @@ rtp_twcc_manager_add_fci (RTPTWCCManager * twcc, GstRTCPPacket * packet)
 
   g_array_sort (twcc->recv_packets, _twcc_seqnum_sort);
 
+  /* Quick scan to remove duplicates */
+  prev = &g_array_index (twcc->recv_packets, RecvPacket, 0);
+  for (i = 1; i < twcc->recv_packets->len;) {
+    RecvPacket *cur = &g_array_index (twcc->recv_packets, RecvPacket, i);
+
+    if (prev->seqnum == cur->seqnum) {
+      GST_DEBUG ("Removing duplicate packet #%u", cur->seqnum);
+      g_array_remove_index (twcc->recv_packets, i);
+    } else {
+      prev = cur;
+      i += 1;
+    }
+  }
+
   /* get first and last packet */
   first = &g_array_index (twcc->recv_packets, RecvPacket, 0);
   last =
@@ -740,6 +759,11 @@ _many_packets_some_lost (RTPTWCCManager * twcc, guint16 seqnum)
   first = &g_array_index (twcc->recv_packets, RecvPacket, 0);
   packet_count = seqnum - first->seqnum + 1;
 
+  /* If there are a high number of duplicates, we can't use the following
+   * metrics */
+  if (received_packets > packet_count)
+    return FALSE;
+
   /* check if we lost half of the threshold */
   lost_packets = packet_count - received_packets;
   if (received_packets >= 30 && lost_packets >= 60)
@@ -787,17 +811,6 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc, RTPPacketInfo * pinfo)
     return FALSE;
   }
 
-  if (twcc->recv_packets->len > 0) {
-    RecvPacket *last = &g_array_index (twcc->recv_packets, RecvPacket,
-        twcc->recv_packets->len - 1);
-
-    diff = gst_rtp_buffer_compare_seqnum (last->seqnum, seqnum);
-    if (diff == 0) {
-      GST_INFO ("Received duplicate packet (%u), dropping", seqnum);
-      return FALSE;
-    }
-  }
-
   /* store the packet for Transport-wide RTCP feedback message */
   recv_packet_init (&packet, seqnum, pinfo);
   g_array_append_val (twcc->recv_packets, packet);
@@ -817,6 +830,8 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc, RTPPacketInfo * pinfo)
           pinfo->running_time + twcc->feedback_interval;
 
     if (pinfo->running_time >= twcc->next_feedback_send_time) {
+      GST_LOG ("Generating feedback : Exceeded feedback interval %"
+          GST_TIME_FORMAT, GST_TIME_ARGS (twcc->feedback_interval));
       rtp_twcc_manager_create_feedback (twcc);
       send_feedback = TRUE;
 
@@ -824,6 +839,8 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc, RTPPacketInfo * pinfo)
         twcc->next_feedback_send_time += twcc->feedback_interval;
     }
   } else if (pinfo->marker || _many_packets_some_lost (twcc, seqnum)) {
+    GST_LOG ("Generating feedback because of %s",
+        pinfo->marker ? "marker packet" : "many packets some lost");
     rtp_twcc_manager_create_feedback (twcc);
     send_feedback = TRUE;
 
@@ -1003,6 +1020,7 @@ rtp_twcc_manager_parse_fci (RTPTWCCManager * twcc,
   guint16 base_seqnum;
   guint16 packet_count;
   GstClockTime base_time;
+  gint64 base_time_ext;
   GstClockTime ts_rounded;
   guint8 fb_pkt_count;
   guint packets_parsed = 0;
@@ -1017,12 +1035,17 @@ rtp_twcc_manager_parse_fci (RTPTWCCManager * twcc,
 
   base_seqnum = GST_READ_UINT16_BE (&fci_data[0]);
   packet_count = GST_READ_UINT16_BE (&fci_data[2]);
-  base_time = GST_READ_UINT24_BE (&fci_data[4]) * REF_TIME_UNIT;
+  base_time = GST_READ_UINT24_BE (&fci_data[4]);
+  /* Sign-extend the base_time from a 24-bit integer into a 64-bit signed integer
+   * so that we can calculate diffs with regular 64-bit operations. */
+  base_time_ext =
+      (base_time & 0x800000) ? base_time | 0xFFFFFFFFFF800000 : base_time;
   fb_pkt_count = fci_data[7];
 
   GST_DEBUG ("Parsed TWCC feedback: base_seqnum: #%u, packet_count: %u, "
       "base_time %" GST_TIME_FORMAT " fb_pkt_count: %u",
-      base_seqnum, packet_count, GST_TIME_ARGS (base_time), fb_pkt_count);
+      base_seqnum, packet_count, GST_TIME_ARGS (base_time * REF_TIME_UNIT),
+      fb_pkt_count);
 
   twcc_packets = g_array_sized_new (FALSE, FALSE,
       sizeof (RTPTWCCPacket), packet_count);
@@ -1052,7 +1075,21 @@ rtp_twcc_manager_parse_fci (RTPTWCCManager * twcc,
   if (twcc->sent_packets->len > 0)
     first_sent_pkt = &g_array_index (twcc->sent_packets, SentPacket, 0);
 
-  ts_rounded = base_time;
+  if (twcc->remote_ts_base == -1) {
+    /* Add an initial offset of 1 << 24 so that we don't risk going below 0 if
+     * a future extended timestamp is earlier than the first. */
+    twcc->remote_ts_base = (G_GINT64_CONSTANT (1) << 24) + base_time_ext;
+  } else {
+    /* Calculate our internal accumulated reference timestamp by continously
+     * adding the diff between the current and the previous sign-extended
+     * reference time. */
+    twcc->remote_ts_base += base_time_ext - twcc->base_time_prev;
+  }
+  twcc->base_time_prev = base_time_ext;
+  /* Our internal accumulated reference time is in units of 64ms, propagate as
+   * GstClockTime in ns. */
+  ts_rounded = twcc->remote_ts_base * REF_TIME_UNIT;
+
   for (i = 0; i < twcc_packets->len; i++) {
     RTPTWCCPacket *pkt = &g_array_index (twcc_packets, RTPTWCCPacket, i);
     gint16 delta = 0;
diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c
index 2f98052f6a5d24091302fe32174d2d0431025658..a12a6f0cc2d212fae15490ccb26d8c48762ccbac 100644
--- a/gst/rtsp/gstrtspsrc.c
+++ b/gst/rtsp/gstrtspsrc.c
@@ -117,6 +117,8 @@
 #include "gstrtspelements.h"
 #include "gstrtspsrc.h"
 
+#include <gst/glib-compat-private.h>
+
 GST_DEBUG_CATEGORY_STATIC (rtspsrc_debug);
 #define GST_CAT_DEFAULT (rtspsrc_debug)
 
@@ -299,7 +301,7 @@ gst_rtsp_backchannel_get_type (void)
 #define DEFAULT_TLS_INTERACTION     NULL
 #define DEFAULT_DO_RETRANSMISSION        TRUE
 #define DEFAULT_NTP_TIME_SOURCE  NTP_TIME_SOURCE_NTP
-#define DEFAULT_USER_AGENT       "GStreamer/" PACKAGE_VERSION
+#define DEFAULT_USER_AGENT       "GStreamer/{VERSION}"
 #define DEFAULT_MAX_RTCP_RTP_TIME_DIFF 1000
 #define DEFAULT_RFC7273_SYNC         FALSE
 #define DEFAULT_ADD_REFERENCE_TIMESTAMP_META FALSE
@@ -312,6 +314,8 @@ gst_rtsp_backchannel_get_type (void)
 #define DEFAULT_ONVIF_RATE_CONTROL TRUE
 #define DEFAULT_IS_LIVE TRUE
 #define DEFAULT_IGNORE_X_SERVER_REPLY FALSE
+#define DEFAULT_FORCE_NON_COMPLIANT_URL FALSE
+#define DEFAULT_TCP_TIMESTAMP FALSE
 
 enum
 {
@@ -361,7 +365,10 @@ enum
   PROP_ONVIF_MODE,
   PROP_ONVIF_RATE_CONTROL,
   PROP_IS_LIVE,
-  PROP_IGNORE_X_SERVER_REPLY
+  PROP_IGNORE_X_SERVER_REPLY,
+  PROP_EXTRA_HTTP_REQUEST_HEADERS,
+  PROP_FORCE_NON_COMPLIANT_URL,
+  PROP_TCP_TIMESTAMP,
 };
 
 #define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
@@ -471,6 +478,8 @@ static GstFlowReturn gst_rtspsrc_push_backchannel_buffer (GstRTSPSrc * src,
 static GstFlowReturn gst_rtspsrc_push_backchannel_sample (GstRTSPSrc * src,
     guint id, GstSample * sample);
 
+static void gst_rtspsrc_reset_flows (GstRTSPSrc * src);
+
 typedef struct
 {
   guint8 pt;
@@ -505,7 +514,7 @@ static guint gst_rtspsrc_signals[LAST_SIGNAL] = { 0 };
 #define gst_rtspsrc_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstRTSPSrc, gst_rtspsrc, GST_TYPE_BIN,
     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_rtspsrc_uri_handler_init));
-GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtspsrc, "rtspsrc", GST_RANK_NONE,
+GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtspsrc, "rtspsrc", GST_RANK_PRIMARY,
     GST_TYPE_RTSPSRC, rtsp_element_init (plugin));
 
 #ifndef GST_DISABLE_GST_DEBUG
@@ -895,6 +904,9 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
    *
    * The string to set in the User-Agent header.
    *
+   * If the string contains `{VERSION}` that will be replaced with the
+   * GStreamer version at runtime (since GStreamer 1.24).
+   *
    * Since: 1.6
    */
   g_object_class_install_property (gobject_class, PROP_USER_AGENT,
@@ -1093,6 +1105,73 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
           DEFAULT_IGNORE_X_SERVER_REPLY,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+   /**
+   * GstRTSPSrc:extra-http-request-headers
+   *
+   * When in tunneled mode append provided headers to any HTTP requests
+   * made by rtspsrc.
+   *
+   * Only applicable for RTSP over HTTP.
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_EXTRA_HTTP_REQUEST_HEADERS,
+      g_param_spec_boxed ("extra-http-request-headers", "Extra Headers",
+          "Extra headers to append to HTTP requests when in tunneled mode",
+          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+   /**
+   * GstRTSPSrc:tcp-timestamp
+   *
+   * Timestamp all buffers with their receive time when receiving RTP packets
+   * over TCP or HTTP.
+   *
+   * When dealing with TCP based connections, setting timestamps for every
+   * packet is not done by default because a server typically bursts data, for
+   * which we don't want to compensate by speeding up the media. The other
+   * timestamps will be interpollated from this one using the RTP timestamps.
+   *
+   * This has the side effect that no drift compensation between the server
+   * and client is done, and over time the RTP timestamps will drift against
+   * the client's clock. This can lead to buffers (and observed end-to-end
+   * latency) to grow over time, or all packets arriving too late once a
+   * threshold is reached.
+   *
+   * Enabling this property will timestamp all RTP packets with their receive
+   * times, which gets around the drift problem but can cause other problems
+   * if the server is sending data not smoothly in real-time.
+   *
+   * Only applicable for RTSP over TCP or HTTP.
+   *
+   * Since: 1.24.10
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_TCP_TIMESTAMP,
+      g_param_spec_boolean ("tcp-timestamp", "TCP Timestamp",
+          "Timestamp RTP packets with receive times in TCP/HTTP mode",
+          DEFAULT_TCP_TIMESTAMP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstRTSPSrc:force-non-compliant-url
+   *
+   * There are various non-compliant servers that don't require control URLs
+   * that are not resolved correctly but instead are just appended.
+   *
+   * As some of these servers will nevertheless reply OK to SETUP requests
+   * even if they didn't handle URIs correctly, this property can be set to
+   * revert to the old non-compliant method for constructing URLs.
+   *
+   * Since: 1.24.7
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_FORCE_NON_COMPLIANT_URL,
+      g_param_spec_boolean ("force-non-compliant-url",
+          "Force non-compliant URL",
+          "Revert to old non-compliant method of constructing URLs",
+          DEFAULT_FORCE_NON_COMPLIANT_URL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   /**
    * GstRTSPSrc::handle-request:
    * @rtspsrc: a #GstRTSPSrc
@@ -1234,7 +1313,7 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
    * @sample: RTP sample to send back
    *
    * Deprecated: 1.22: Use action signal GstRTSPSrc::push-backchannel-sample instead.
-   * IMPORTANT: Please note that this signal decrements the reference count 
+   * IMPORTANT: Please note that this signal decrements the reference count
    *            of sample internally! So it cannot be used from other
    *            language bindings in general.
    *
@@ -1255,9 +1334,9 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
    */
   gst_rtspsrc_signals[SIGNAL_PUSH_BACKCHANNEL_SAMPLE] =
       g_signal_new ("push-backchannel-sample", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION | G_SIGNAL_DEPRECATED,
-      G_STRUCT_OFFSET (GstRTSPSrcClass, push_backchannel_buffer), NULL, NULL,
-      NULL, GST_TYPE_FLOW_RETURN, 2, G_TYPE_UINT, GST_TYPE_SAMPLE);
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRTSPSrcClass,
+          push_backchannel_sample), NULL, NULL, NULL,
+      GST_TYPE_FLOW_RETURN, 2, G_TYPE_UINT, GST_TYPE_SAMPLE);
 
   /**
    * GstRTSPSrc::get-parameter:
@@ -1522,6 +1601,10 @@ gst_rtspsrc_init (GstRTSPSrc * src)
   src->is_live = DEFAULT_IS_LIVE;
   src->seek_seqnum = GST_SEQNUM_INVALID;
   src->group_id = GST_GROUP_ID_INVALID;
+  src->prop_extra_http_request_headers =
+      gst_structure_new_empty ("extra-http-request-headers");
+  src->force_non_compliant_url = DEFAULT_FORCE_NON_COMPLIANT_URL;
+  src->tcp_timestamp = DEFAULT_TCP_TIMESTAMP;
 
   /* get a list of all extensions */
   src->extensions = gst_rtsp_ext_list_get ();
@@ -1597,6 +1680,11 @@ gst_rtspsrc_finalize (GObject * object)
   if (rtspsrc->initial_seek)
     gst_event_unref (rtspsrc->initial_seek);
 
+  if (rtspsrc->prop_extra_http_request_headers) {
+    gst_structure_free (rtspsrc->prop_extra_http_request_headers);
+    rtspsrc->prop_extra_http_request_headers = NULL;
+  }
+
   /* free locks */
   g_rec_mutex_clear (&rtspsrc->stream_rec_lock);
   g_rec_mutex_clear (&rtspsrc->state_rec_lock);
@@ -1867,6 +1955,22 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
     case PROP_IGNORE_X_SERVER_REPLY:
       rtspsrc->ignore_x_server_reply = g_value_get_boolean (value);
       break;
+    case PROP_EXTRA_HTTP_REQUEST_HEADERS:{
+      const GstStructure *s = gst_value_get_structure (value);
+      if (rtspsrc->prop_extra_http_request_headers) {
+        gst_structure_free (rtspsrc->prop_extra_http_request_headers);
+      }
+      rtspsrc->prop_extra_http_request_headers =
+          s ? gst_structure_copy (s) :
+          gst_structure_new_empty ("extra-http-request-headers");
+    }
+      break;
+    case PROP_FORCE_NON_COMPLIANT_URL:
+      rtspsrc->force_non_compliant_url = g_value_get_boolean (value);
+      break;
+    case PROP_TCP_TIMESTAMP:
+      rtspsrc->tcp_timestamp = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2040,6 +2144,15 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_IGNORE_X_SERVER_REPLY:
       g_value_set_boolean (value, rtspsrc->ignore_x_server_reply);
       break;
+    case PROP_EXTRA_HTTP_REQUEST_HEADERS:
+      gst_value_set_structure (value, rtspsrc->prop_extra_http_request_headers);
+      break;
+    case PROP_FORCE_NON_COMPLIANT_URL:
+      g_value_set_boolean (value, rtspsrc->force_non_compliant_url);
+      break;
+    case PROP_TCP_TIMESTAMP:
+      g_value_set_boolean (value, rtspsrc->tcp_timestamp);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2301,6 +2414,13 @@ gst_rtspsrc_collect_payloads (GstRTSPSrc * src, const GstSDPMessage * sdp,
     outcaps = gst_caps_intersect (caps, global_caps);
     gst_caps_unref (caps);
 
+    if (gst_caps_is_empty (outcaps)) {
+      GST_WARNING_OBJECT (src,
+          " skipping pt %d with caps conflicting with the global caps", pt);
+      gst_caps_unref (outcaps);
+      continue;
+    }
+
     /* the first pt will be the default */
     if (stream->ptmap->len == 0)
       stream->default_pt = pt;
@@ -2435,14 +2555,14 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, GstSDPMessage * sdp, gint idx,
      * If the control_path starts with a non rtsp: protocol we will most
      * likely build a URL that the server will fail to understand, this is ok,
      * we will fail then. */
-    if (g_str_has_prefix (control_path, "rtsp://"))
+    if (gst_uri_is_valid (control_path))
       stream->conninfo.location = g_strdup (control_path);
     else {
       const gchar *base;
 
       base = get_aggregate_control (src);
       if (g_strcmp0 (control_path, "*") == 0)
-        control_path = g_strdup (base);
+        stream->conninfo.location = g_strdup (base);
       else
         stream->conninfo.location = gst_uri_join_strings (base, control_path);
     }
@@ -2803,6 +2923,7 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing)
       state = GST_STATE_PAUSED;
   }
   gst_rtspsrc_push_event (src, event);
+  gst_rtspsrc_reset_flows (src);
   gst_rtspsrc_loop_send_cmd (src, cmd, CMD_LOOP);
   gst_rtspsrc_set_state (src, state);
 }
@@ -2970,7 +3091,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
 
   /* If an accurate seek was requested, we want to clip the segment we
    * output in ONVIF mode to the requested bounds */
-  src->clip_out_segment = ! !(flags & GST_SEEK_FLAG_ACCURATE);
+  src->clip_out_segment = !!(flags & GST_SEEK_FLAG_ACCURATE);
   src->seek_seqnum = gst_event_get_seqnum (event);
 
   /* prepare for streaming again */
@@ -3149,11 +3270,10 @@ gst_rtspsrc_handle_src_sink_event (GstPad * pad, GstObject * parent,
     GstEvent * event)
 {
   GstRTSPStream *stream;
-  GstRTSPSrc *self = GST_RTSPSRC (GST_OBJECT_PARENT (parent));
 
   stream = gst_pad_get_element_private (pad);
 
-  event = gst_rtspsrc_update_src_event (self, stream, event);
+  event = gst_rtspsrc_update_src_event (stream->parent, stream, event);
 
   return gst_pad_push_event (stream->srcpad, event);
 }
@@ -4353,7 +4473,13 @@ gst_rtspsrc_stream_configure_tcp (GstRTSPSrc * src, GstRTSPStream * stream,
     /* create a new pad we will use to stream to */
     name = g_strdup_printf ("stream_%u", stream->id);
     template = gst_static_pad_template_get (&rtptemplate);
-    stream->channelpad[0] = gst_pad_new_from_template (template, name);
+    pad0 = gst_pad_new_from_template (template, name);
+    stream->channelpad[0] = pad0;
+
+    gst_pad_set_event_function (pad0, gst_rtspsrc_handle_internal_src_event);
+    gst_pad_set_query_function (pad0, gst_rtspsrc_handle_internal_src_query);
+    gst_pad_set_element_private (pad0, src);
+
     gst_object_unref (template);
     g_free (name);
 
@@ -4728,6 +4854,9 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src,
     /* no sync or async state changes needed */
     g_object_set (G_OBJECT (stream->udpsink[0]), "auto-multicast", FALSE,
         "loop", FALSE, "sync", FALSE, "async", FALSE, NULL);
+    if (src->multi_iface != NULL)
+      g_object_set (G_OBJECT (stream->udpsink[0]), "multicast-iface",
+          src->multi_iface, NULL);
     if (ttl > 0)
       g_object_set (G_OBJECT (stream->udpsink[0]), "ttl", ttl, NULL);
 
@@ -4791,8 +4920,11 @@ gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src,
     /* no sync or async state changes needed */
     g_object_set (G_OBJECT (stream->udpsink[1]), "auto-multicast", FALSE,
         "loop", FALSE, "sync", FALSE, "async", FALSE, NULL);
+    if (src->multi_iface != NULL)
+      g_object_set (G_OBJECT (stream->udpsink[1]), "multicast-iface",
+          src->multi_iface, NULL);
     if (ttl > 0)
-      g_object_set (G_OBJECT (stream->udpsink[0]), "ttl", ttl, NULL);
+      g_object_set (G_OBJECT (stream->udpsink[1]), "ttl", ttl, NULL);
 
     if (stream->udpsrc[1]) {
       /* configure socket, we give it the same UDP socket as the udpsrc for RTCP
@@ -5175,6 +5307,15 @@ done:
   return ret;
 }
 
+static void
+gst_rtspsrc_reset_flows (GstRTSPSrc * src)
+{
+  for (GList * streams = src->streams; streams; streams = g_list_next (streams)) {
+    GstRTSPStream *ostream = (GstRTSPStream *) streams->data;
+    ostream->last_ret = GST_FLOW_OK;
+  }
+}
+
 static gboolean
 gst_rtspsrc_stream_push_event (GstRTSPSrc * src, GstRTSPStream * stream,
     GstEvent * event)
@@ -5278,6 +5419,18 @@ accept_certificate_cb (GTlsConnection * conn, GTlsCertificate * peer_cert,
   return accept;
 }
 
+static gboolean
+_add_header_to_conn (GQuark field_id, const GValue * value, gpointer user_data)
+{
+  const gchar *key_str = g_quark_to_string (field_id);
+  const gchar *value_str = g_value_get_string (value);
+
+  GstRTSPConnection *conn = (GstRTSPConnection *) user_data;
+  gst_rtsp_connection_add_extra_http_request_header (conn, key_str, value_str);
+
+  return TRUE;
+}
+
 static GstRTSPResult
 gst_rtsp_conninfo_connect (GstRTSPSrc * src, GstRTSPConnInfo * info,
     gboolean async)
@@ -5285,6 +5438,8 @@ gst_rtsp_conninfo_connect (GstRTSPSrc * src, GstRTSPConnInfo * info,
   GstRTSPResult res;
   GstRTSPMessage response;
   gboolean retry = FALSE;
+  GstRTSPUrl *url;
+  gchar *new_url;
   memset (&response, 0, sizeof (response));
   gst_rtsp_message_init (&response);
   do {
@@ -5336,6 +5491,11 @@ gst_rtsp_conninfo_connect (GstRTSPSrc * src, GstRTSPConnInfo * info,
         gst_rtsp_connection_set_proxy (info->connection, src->proxy_host,
             src->proxy_port);
       }
+
+      if (src->prop_extra_http_request_headers != NULL) {
+        gst_structure_foreach (src->prop_extra_http_request_headers,
+            _add_header_to_conn, info->connection);
+      }
     }
 
     if (!info->connected) {
@@ -5360,7 +5520,19 @@ gst_rtsp_conninfo_connect (GstRTSPSrc * src, GstRTSPConnInfo * info,
 
       if (res == GST_RTSP_OK)
         info->connected = TRUE;
-      else if (!retry)
+      else if (res == GST_RTSP_OK_REDIRECT) {
+        url = gst_rtsp_connection_get_url (info->connection);
+
+        if (url == NULL || info->url_str == NULL)
+          goto could_not_connect;
+
+        new_url = gst_rtsp_url_get_request_uri (url);
+        GST_DEBUG_OBJECT (src, "redirected from %s to %s", info->url_str,
+            new_url);
+        g_free (info->url_str);
+        info->url_str = new_url;
+        info->connected = TRUE;
+      } else if (!retry)
         goto could_not_connect;
     }
   } while (!info->connected && retry);
@@ -5461,8 +5633,13 @@ gst_rtspsrc_init_request (GstRTSPSrc * src, GstRTSPMessage * msg,
     return res;
 
   /* set user-agent */
-  if (src->user_agent)
-    gst_rtsp_message_add_header (msg, GST_RTSP_HDR_USER_AGENT, src->user_agent);
+  if (src->user_agent) {
+    GString *user_agent = g_string_new (src->user_agent);
+
+    g_string_replace (user_agent, "{VERSION}", PACKAGE_VERSION, 0);
+    gst_rtsp_message_add_header (msg, GST_RTSP_HDR_USER_AGENT, user_agent->str);
+    g_string_free (user_agent, TRUE);
+  }
 
   return res;
 }
@@ -5710,30 +5887,6 @@ gst_rtspsrc_handle_data (GstRTSPSrc * src, GstRTSPMessage * message)
     src->need_segment = TRUE;
   }
 
-  if (src->base_time == -1) {
-    /* Take current running_time. This timestamp will be put on
-     * the first buffer of each stream because we are a live source and so we
-     * timestamp with the running_time. When we are dealing with TCP, we also
-     * only timestamp the first buffer (using the DISCONT flag) because a server
-     * typically bursts data, for which we don't want to compensate by speeding
-     * up the media. The other timestamps will be interpollated from this one
-     * using the RTP timestamps. */
-    GST_OBJECT_LOCK (src);
-    if (GST_ELEMENT_CLOCK (src)) {
-      GstClockTime now;
-      GstClockTime base_time;
-
-      now = gst_clock_get_time (GST_ELEMENT_CLOCK (src));
-      base_time = GST_ELEMENT_CAST (src)->base_time;
-
-      src->base_time = now - base_time;
-
-      GST_DEBUG_OBJECT (src, "first buffer at time %" GST_TIME_FORMAT ", base %"
-          GST_TIME_FORMAT, GST_TIME_ARGS (now), GST_TIME_ARGS (base_time));
-    }
-    GST_OBJECT_UNLOCK (src);
-  }
-
   /* If needed send a new segment, don't forget we are live and buffer are
    * timestamped with running time */
   if (src->need_segment) {
@@ -5770,16 +5923,28 @@ gst_rtspsrc_handle_data (GstRTSPSrc * src, GstRTSPMessage * message)
     stream->need_caps = FALSE;
   }
 
+  if (!is_rtcp && (stream->discont || src->tcp_timestamp)) {
+    GstClockTime timestamp = GST_CLOCK_TIME_NONE;
+
+    GST_OBJECT_LOCK (src);
+    if (GST_ELEMENT_CLOCK (src)) {
+      GstClockTime now;
+
+      now = gst_clock_get_time (GST_ELEMENT_CLOCK (src));
+      timestamp = now - GST_ELEMENT_CAST (src)->base_time;
+    }
+    GST_OBJECT_UNLOCK (src);
+
+    GST_TRACE_OBJECT (src, "setting timestamp %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (timestamp));
+
+    GST_BUFFER_DTS (buf) = timestamp;
+  }
+
   if (stream->discont && !is_rtcp) {
     /* mark first RTP buffer as discont */
     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
     stream->discont = FALSE;
-    /* first buffer gets the timestamp, other buffers are not timestamped and
-     * their presentation time will be interpollated from the rtp timestamps. */
-    GST_DEBUG_OBJECT (src, "setting timestamp %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (src->base_time));
-
-    GST_BUFFER_TIMESTAMP (buf) = src->base_time;
   }
 
   /* chain to the peer pad */
@@ -6597,7 +6762,8 @@ propagate_error:
 
 static GstRTSPResult
 gst_rtsp_src_receive_response (GstRTSPSrc * src, GstRTSPConnInfo * conninfo,
-    GstRTSPMessage * response, GstRTSPStatusCode * code)
+    GstRTSPMessage * response, GstRTSPStatusCode * code,
+    gboolean update_content_base)
 {
   GstRTSPStatusCode thecode;
   gchar *content_base = NULL;
@@ -6658,12 +6824,14 @@ next:
   if (thecode != GST_RTSP_STS_OK)
     return GST_RTSP_OK;
 
-  /* store new content base if any */
-  gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE,
-      &content_base, 0);
-  if (content_base) {
-    g_free (src->content_base);
-    src->content_base = g_strdup (content_base);
+  if (update_content_base) {
+    /* store new content base if any */
+    gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE,
+        &content_base, 0);
+    if (content_base) {
+      g_free (src->content_base);
+      src->content_base = g_strdup (content_base);
+    }
   }
 
   return GST_RTSP_OK;
@@ -6710,7 +6878,7 @@ server_eof:
 static GstRTSPResult
 gst_rtspsrc_try_send (GstRTSPSrc * src, GstRTSPConnInfo * conninfo,
     GstRTSPMessage * request, GstRTSPMessage * response,
-    GstRTSPStatusCode * code)
+    GstRTSPStatusCode * code, gboolean update_content_base)
 {
   GstRTSPResult res;
   gint try = 0;
@@ -6739,7 +6907,9 @@ again:
   if (!response)
     return res;
 
-  res = gst_rtsp_src_receive_response (src, conninfo, response, code);
+  res =
+      gst_rtsp_src_receive_response (src, conninfo, response, code,
+      update_content_base);
   if (res == GST_RTSP_EEOF) {
     GST_WARNING_OBJECT (src, "server closed connection");
     /* only try once after reconnect, then fallthrough and error out */
@@ -6815,7 +6985,8 @@ receive_error:
 static GstRTSPResult
 gst_rtspsrc_send (GstRTSPSrc * src, GstRTSPConnInfo * conninfo,
     GstRTSPMessage * request, GstRTSPMessage * response,
-    GstRTSPStatusCode * code, GstRTSPVersion * versions)
+    GstRTSPStatusCode * code, GstRTSPVersion * versions,
+    gboolean update_content_base)
 {
   GstRTSPStatusCode int_code = GST_RTSP_STS_OK;
   GstRTSPResult res = GST_RTSP_ERROR;
@@ -6840,7 +7011,7 @@ gst_rtspsrc_send (GstRTSPSrc * src, GstRTSPConnInfo * conninfo,
 
     if ((res =
             gst_rtspsrc_try_send (src, conninfo, request, response,
-                &int_code)) < 0)
+                &int_code, update_content_base)) < 0)
       goto error;
 
     switch (int_code) {
@@ -6967,7 +7138,8 @@ static GstRTSPResult
 gst_rtspsrc_send_cb (GstRTSPExtension * ext, GstRTSPMessage * request,
     GstRTSPMessage * response, GstRTSPSrc * src)
 {
-  return gst_rtspsrc_send (src, &src->conninfo, request, response, NULL, NULL);
+  return gst_rtspsrc_send (src, &src->conninfo, request, response, NULL, NULL,
+      FALSE);
 }
 
 
@@ -7453,7 +7625,7 @@ gst_rtspsrc_setup_streams_end (GstRTSPSrc * src, gboolean async)
     if (!src->conninfo.connection)
       conninfo = &((GstRTSPStream *) tmp->data)->conninfo;
 
-    gst_rtsp_src_receive_response (src, conninfo, &response, NULL);
+    gst_rtsp_src_receive_response (src, conninfo, &response, NULL, FALSE);
 
     gst_rtsp_src_setup_stream_from_response (src, stream,
         &response, NULL, 0, NULL, NULL);
@@ -7462,6 +7634,31 @@ gst_rtspsrc_setup_streams_end (GstRTSPSrc * src, gboolean async)
   return GST_RTSP_OK;
 }
 
+static void
+try_non_compliant_url (GstRTSPSrc * src, GstRTSPStream * stream)
+{
+  const gchar *base;
+
+  base = get_aggregate_control (src);
+
+  g_free (stream->conninfo.location);
+
+  /* Make sure to not accumulate too many `/` */
+  if ((g_str_has_suffix (base, "/")
+          && !g_str_has_suffix (stream->control_url, "/"))
+      || (!g_str_has_suffix (base, "/")
+          && g_str_has_suffix (stream->control_url, "/"))
+      )
+    stream->conninfo.location = g_strconcat (base, stream->control_url, NULL);
+  else if (g_str_has_suffix (base, "/")
+      && g_str_has_suffix (stream->control_url, "/"))
+    stream->conninfo.location =
+        g_strconcat (base, stream->control_url + 1, NULL);
+  else
+    stream->conninfo.location =
+        g_strconcat (base, "/", stream->control_url, NULL);
+}
+
 /* Perform the SETUP request for all the streams.
  *
  * We ask the server for a specific transport, which initially includes all the
@@ -7525,7 +7722,7 @@ gst_rtspsrc_setup_streams_start (GstRTSPSrc * src, gboolean async)
     GstRTSPConnInfo *conninfo;
     gchar *transports;
     gint retry = 0;
-    gboolean tried_non_compliant_url = FALSE;
+    gboolean tried_non_compliant_url;
     guint mask = 0;
     gboolean selected;
     GstCaps *caps;
@@ -7607,6 +7804,14 @@ gst_rtspsrc_setup_streams_start (GstRTSPSrc * src, gboolean async)
     if (!protocol_masks[mask])
       goto no_protocols;
 
+    if (src->force_non_compliant_url) {
+      try_non_compliant_url (src, stream);
+
+      tried_non_compliant_url = TRUE;
+    } else {
+      tried_non_compliant_url = FALSE;
+    }
+
   retry:
     GST_DEBUG_OBJECT (src, "protocols = 0x%x, protocol mask = 0x%x", protocols,
         protocol_masks[mask]);
@@ -7684,7 +7889,7 @@ gst_rtspsrc_setup_streams_start (GstRTSPSrc * src, gboolean async)
     /* handle the code ourselves */
     res =
         gst_rtspsrc_send (src, conninfo, &request,
-        pipelined_request_id ? NULL : &response, &code, NULL);
+        pipelined_request_id ? NULL : &response, &code, NULL, FALSE);
     if (res < 0)
       goto send_error;
 
@@ -7720,6 +7925,9 @@ gst_rtspsrc_setup_streams_start (GstRTSPSrc * src, gboolean async)
           goto retry;
       case GST_RTSP_STS_BAD_REQUEST:
       case GST_RTSP_STS_NOT_FOUND:
+      case GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE:
+      case GST_RTSP_STS_PARAMETER_NOT_UNDERSTOOD:
+      case GST_RTSP_STS_SERVICE_UNAVAILABLE:
         /* There are various non-compliant servers that don't require control
          * URLs that are not resolved correctly but instead are just appended.
          * See e.g.
@@ -7727,31 +7935,12 @@ gst_rtspsrc_setup_streams_start (GstRTSPSrc * src, gboolean async)
          *   https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1447
          */
         if (!tried_non_compliant_url && stream->control_url
-            && !g_str_has_prefix (stream->control_url, "rtsp://")) {
-          const gchar *base;
-
+            && !gst_uri_is_valid (stream->control_url)) {
           gst_rtsp_message_unset (&request);
           gst_rtsp_message_unset (&response);
           gst_rtspsrc_stream_free_udp (stream);
 
-          g_free (stream->conninfo.location);
-          base = get_aggregate_control (src);
-
-          /* Make sure to not accumulate too many `/` */
-          if ((g_str_has_suffix (base, "/")
-                  && !g_str_has_suffix (stream->control_url, "/"))
-              || (!g_str_has_suffix (base, "/")
-                  && g_str_has_suffix (stream->control_url, "/"))
-              )
-            stream->conninfo.location =
-                g_strconcat (base, stream->control_url, NULL);
-          else if (g_str_has_suffix (base, "/")
-              && g_str_has_suffix (stream->control_url, "/"))
-            stream->conninfo.location =
-                g_strconcat (base, stream->control_url + 1, NULL);
-          else
-            stream->conninfo.location =
-                g_strconcat (base, "/", stream->control_url, NULL);
+          try_non_compliant_url (src, stream);
 
           tried_non_compliant_url = TRUE;
 
@@ -8121,23 +8310,29 @@ gst_rtspsrc_open_from_sdp (GstRTSPSrc * src, GstSDPMessage * sdp,
       if (control == NULL)
         break;
 
+      if (g_strcmp0 (control, "*") == 0)
+        break;
+
       /* only take fully qualified urls */
-      if (g_str_has_prefix (control, "rtsp://"))
+      if (gst_uri_is_valid (control))
         break;
     }
-    if (control) {
-      g_free (src->conninfo.location);
-      src->conninfo.location = g_strdup (control);
-      /* make a connection for this, if there was a connection already, nothing
-       * happens. */
-      if (gst_rtsp_conninfo_connect (src, &src->conninfo, async) < 0) {
-        GST_ERROR_OBJECT (src, "could not connect");
+
+    if (g_strcmp0 (control, "*") != 0) {
+      if (control) {
+        g_free (src->conninfo.location);
+        src->conninfo.location = g_strdup (control);
+        /* make a connection for this, if there was a connection already, nothing
+         * happens. */
+        if (gst_rtsp_conninfo_connect (src, &src->conninfo, async) < 0) {
+          GST_ERROR_OBJECT (src, "could not connect");
+        }
       }
+      /* we need to keep the control url separate from the connection url because
+       * the rules for constructing the media control url need it */
+      g_free (src->control);
+      src->control = g_strdup (control);
     }
-    /* we need to keep the control url separate from the connection url because
-     * the rules for constructing the media control url need it */
-    g_free (src->control);
-    src->control = g_strdup (control);
   }
 
   /* create streams */
@@ -8218,7 +8413,7 @@ restart:
 
   if ((res =
           gst_rtspsrc_send (src, &src->conninfo, &request, &response,
-              NULL, versions)) < 0) {
+              NULL, versions, TRUE)) < 0) {
     goto send_error;
   }
 
@@ -8255,7 +8450,7 @@ restart:
 
   if ((res =
           gst_rtspsrc_send (src, &src->conninfo, &request, &response,
-              NULL, NULL)) < 0)
+              NULL, NULL, TRUE)) < 0)
     goto send_error;
 
   /* we only perform redirect for describe and play, currently */
@@ -8496,7 +8691,8 @@ gst_rtspsrc_close (GstRTSPSrc * src, gboolean async, gboolean only_close)
       GST_ELEMENT_PROGRESS (src, CONTINUE, "close", ("Closing stream"));
 
     if ((res =
-            gst_rtspsrc_send (src, info, &request, &response, NULL, NULL)) < 0)
+            gst_rtspsrc_send (src, info, &request, &response, NULL, NULL,
+                FALSE)) < 0)
       goto send_error;
 
     /* FIXME, parse result? */
@@ -8519,9 +8715,8 @@ close:
   }
 
   /* cleanup */
-  gst_rtspsrc_cleanup (src);
-
   src->state = GST_RTSP_STATE_INVALID;
+  gst_rtspsrc_cleanup (src);
 
   if (async)
     gst_rtspsrc_loop_end_cmd (src, CMD_CLOSE, res);
@@ -8944,7 +9139,8 @@ restart:
       GST_ELEMENT_PROGRESS (src, CONTINUE, "request", ("Sending PLAY request"));
 
     if ((res =
-            gst_rtspsrc_send (src, conninfo, &request, &response, NULL, NULL))
+            gst_rtspsrc_send (src, conninfo, &request, &response, NULL, NULL,
+                FALSE))
         < 0)
       goto send_error;
 
@@ -8989,12 +9185,6 @@ restart:
 
     gst_rtsp_message_unset (&request);
 
-    /* parse RTP npt field. This is the current position in the stream (Normal
-     * Play Time) and should be put in the NEWSEGMENT position field. */
-    if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RANGE, &hval,
-            0) == GST_RTSP_OK)
-      gst_rtspsrc_parse_range (src, hval, segment, FALSE);
-
     /* assume 1.0 rate now, overwrite when the SCALE or SPEED headers are present. */
     segment->rate = 1.0;
 
@@ -9008,6 +9198,12 @@ restart:
       segment->rate = gst_rtspsrc_get_float (hval);
     }
 
+    /* parse RTP npt field. This is the current position in the stream (Normal
+     * Play Time) and should be put in the NEWSEGMENT position field. */
+    if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RANGE, &hval,
+            0) == GST_RTSP_OK)
+      gst_rtspsrc_parse_range (src, hval, segment, FALSE);
+
     /* parse the RTP-Info header field (if ANY) to get the base seqnum and timestamp
      * for the RTP packets. If this is not present, we assume all starts from 0...
      * This is info for the RTP session manager that we pass to it in caps. */
@@ -9056,7 +9252,6 @@ restart:
   src->need_range = FALSE;
 
   src->running = TRUE;
-  src->base_time = -1;
   src->state = GST_RTSP_STATE_PLAYING;
 
   /* mark discont */
@@ -9178,7 +9373,7 @@ gst_rtspsrc_pause (GstRTSPSrc * src, gboolean async)
 
     if ((res =
             gst_rtspsrc_send (src, conninfo, &request, &response, NULL,
-                NULL)) < 0)
+                NULL, FALSE)) < 0)
       goto send_error;
 
     gst_rtsp_message_unset (&request);
@@ -9332,21 +9527,33 @@ gst_rtspsrc_thread (GstRTSPSrc * src)
 
   GST_OBJECT_LOCK (src);
   cmd = src->pending_cmd;
-  if (cmd == CMD_RECONNECT || cmd == CMD_PLAY || cmd == CMD_PAUSE
-      || cmd == CMD_LOOP || cmd == CMD_OPEN || cmd == CMD_GET_PARAMETER
-      || cmd == CMD_SET_PARAMETER) {
-    if (g_queue_is_empty (&src->set_get_param_q)) {
-      src->pending_cmd = CMD_LOOP;
-    } else {
-      ParameterRequest *next_req;
-      if (cmd == CMD_GET_PARAMETER || cmd == CMD_SET_PARAMETER) {
-        req = g_queue_pop_head (&src->set_get_param_q);
+
+  switch (cmd) {
+    case CMD_CLOSE:
+      src->pending_cmd = CMD_WAIT;
+      break;
+    case CMD_GET_PARAMETER:
+    case CMD_SET_PARAMETER:
+      req = g_queue_pop_head (&src->set_get_param_q);
+      if (!req)
+        cmd = CMD_LOOP;
+      /* fall through */
+    case CMD_OPEN:
+    case CMD_PLAY:
+    case CMD_PAUSE:
+    case CMD_LOOP:
+    case CMD_RECONNECT:
+      if (g_queue_is_empty (&src->set_get_param_q)) {
+        src->pending_cmd = CMD_LOOP;
+      } else {
+        ParameterRequest *next_req;
+        next_req = g_queue_peek_head (&src->set_get_param_q);
+        src->pending_cmd = next_req->cmd;
       }
-      next_req = g_queue_peek_head (&src->set_get_param_q);
-      src->pending_cmd = next_req ? next_req->cmd : CMD_LOOP;
-    }
-  } else
-    src->pending_cmd = CMD_WAIT;
+      break;
+    default:
+      break;
+  }
   GST_DEBUG_OBJECT (src, "got command %s", cmd_to_string (cmd));
 
   /* we got the message command, so ensure communication is possible again */
@@ -9433,8 +9640,10 @@ gst_rtspsrc_stop (GstRTSPSrc * src)
 
   GST_DEBUG_OBJECT (src, "stopping");
 
-  /* also cancels pending task */
-  gst_rtspsrc_loop_send_cmd (src, CMD_WAIT, CMD_ALL);
+  /* If we've already started cleanup, we only need to stop the task */
+  if (src->state != GST_RTSP_STATE_INVALID)
+    /* also cancels pending task */
+    gst_rtspsrc_loop_send_cmd (src, CMD_WAIT, CMD_ALL);
 
   GST_OBJECT_LOCK (src);
   if ((task = src->task)) {
@@ -9457,8 +9666,10 @@ gst_rtspsrc_stop (GstRTSPSrc * src)
   }
   GST_OBJECT_UNLOCK (src);
 
-  /* ensure synchronously all is closed and clean */
-  gst_rtspsrc_close (src, FALSE, TRUE);
+  /* ensure synchronously all is closed and clean if we haven't started
+   * cleanup yet */
+  if (src->state != GST_RTSP_STATE_INVALID)
+    gst_rtspsrc_close (src, FALSE, TRUE);
 
   return TRUE;
 }
@@ -9531,11 +9742,12 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
       if (rtspsrc->is_live) {
         /* send pause request and keep the idle task around */
         gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_PAUSE, CMD_LOOP);
+        ret = GST_STATE_CHANGE_NO_PREROLL;
+      } else {
+        ret = GST_STATE_CHANGE_SUCCESS;
       }
-      ret = GST_STATE_CHANGE_SUCCESS;
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
-      rtspsrc->seek_seqnum = GST_SEQNUM_INVALID;
       gst_rtspsrc_loop_send_cmd_and_wait (rtspsrc, CMD_CLOSE, CMD_ALL,
           rtspsrc->teardown_timeout);
       ret = GST_STATE_CHANGE_SUCCESS;
@@ -9770,7 +9982,7 @@ gst_rtspsrc_get_parameter (GstRTSPSrc * src, ParameterRequest * req)
   }
 
   if ((res = gst_rtspsrc_send (src, &src->conninfo,
-              &request, &response, &code, NULL)) < 0)
+              &request, &response, &code, NULL, FALSE)) < 0)
     goto send_error;
 
   res = gst_rtsp_message_get_body (&response, (guint8 **) & recv_body,
@@ -9890,7 +10102,7 @@ gst_rtspsrc_set_parameter (GstRTSPSrc * src, ParameterRequest * req)
   }
 
   if ((res = gst_rtspsrc_send (src, &src->conninfo,
-              &request, &response, &code, NULL)) < 0)
+              &request, &response, &code, NULL, FALSE)) < 0)
     goto send_error;
 
 done:
diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h
index 9457972b5634ecd1ef34eaa4700aae1d309b0c2e..6df84bf2acbc1ac79eb6e39dfa92a85f40fcd687 100644
--- a/gst/rtsp/gstrtspsrc.h
+++ b/gst/rtsp/gstrtspsrc.h
@@ -213,7 +213,6 @@ struct _GstRTSPSrc {
   gboolean         need_segment;
   gboolean         clip_out_segment;
   GstSegment       out_segment;
-  GstClockTime     base_time;
 
   /* UDP mode loop */
   gint             pending_cmd;
@@ -280,6 +279,9 @@ struct _GstRTSPSrc {
   gboolean          onvif_rate_control;
   gboolean          is_live;
   gboolean          ignore_x_server_reply;
+  GstStructure     *prop_extra_http_request_headers;
+  gboolean          force_non_compliant_url;
+  gboolean          tcp_timestamp;
 
   /* state */
   GstRTSPState       state;
diff --git a/gst/shapewipe/gstshapewipe.c b/gst/shapewipe/gstshapewipe.c
index 06aa5b7ea59edb3fd9bbd105e3cd2cb51a15ba81..442e666463222d862cdd16d43e6ba4b2f1efe12a 100644
--- a/gst/shapewipe/gstshapewipe.c
+++ b/gst/shapewipe/gstshapewipe.c
@@ -695,7 +695,8 @@ gst_shape_wipe_update_qos (GstShapeWipe * self, gdouble proportion,
   self->proportion = proportion;
   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
     if (G_UNLIKELY (diff > 0))
-      self->earliest_time = timestamp + 2 * diff + self->frame_duration;
+      self->earliest_time =
+          timestamp + MIN (2 * diff, GST_SECOND) + self->frame_duration;
     else
       self->earliest_time = timestamp + diff;
   } else {
diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c
index eba529fd33814a78475f69ae076b96d81cbc3061..527beeac87054e9b66e27043217b04deed5ec41b 100644
--- a/gst/udp/gstmultiudpsink.c
+++ b/gst/udp/gstmultiudpsink.c
@@ -474,7 +474,7 @@ gst_udp_client_new (GstMultiUDPSink * sink, const gchar * host, gint port)
   }
 #endif
 
-  client = g_slice_new0 (GstUDPClient);
+  client = g_new0 (GstUDPClient, 1);
   client->ref_count = 1;
   client->add_count = 0;
   client->host = g_strdup (host);
@@ -499,7 +499,7 @@ gst_udp_client_unref (GstUDPClient * client)
   if (--client->ref_count == 0) {
     g_object_unref (client->addr);
     g_free (client->host);
-    g_slice_free (GstUDPClient, client);
+    g_free (client);
   }
 }
 
diff --git a/gst/udp/gstudp.c b/gst/udp/gstudp.c
index d5a21f22d5afbe8bcdb1263534c25aed486192ea..4935be9f843734ce9875ee76b60745fa167da1f1 100644
--- a/gst/udp/gstudp.c
+++ b/gst/udp/gstudp.c
@@ -23,12 +23,15 @@
 
 #include "gstudpelements.h"
 
+GST_DEBUG_CATEGORY (gst_udp_debug);
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
   gboolean ret = FALSE;
 
+  GST_DEBUG_CATEGORY_INIT (gst_udp_debug, "udp", 0, "udp");
+
   ret |= GST_ELEMENT_REGISTER (udpsink, plugin);
   ret |= GST_ELEMENT_REGISTER (multiudpsink, plugin);
   ret |= GST_ELEMENT_REGISTER (dynudpsink, plugin);
diff --git a/gst/udp/gstudpnetutils.c b/gst/udp/gstudpnetutils.c
index b4dc5ef0186894e17ff694086c97278d1a2a37c4..df47c975d15de8b09f2cb806c8732883bebf400c 100644
--- a/gst/udp/gstudpnetutils.c
+++ b/gst/udp/gstudpnetutils.c
@@ -28,81 +28,109 @@
 
 #include "gstudpnetutils.h"
 
+GST_DEBUG_CATEGORY_EXTERN (gst_udp_debug);
+#define GST_CAT_DEFAULT gst_udp_debug
+
 gboolean
-gst_udp_parse_uri (const gchar * uristr, gchar ** host, guint16 * port)
+gst_udp_parse_uri (const gchar * uristr, gchar ** host, guint16 * port,
+    GPtrArray * source_list)
 {
-  gchar *protocol, *location_start;
-  gchar *location, *location_end;
-  gchar *colptr;
+  GstUri *uri;
+  const gchar *protocol;
 
-  /* consider no protocol to be udp:// */
-  protocol = gst_uri_get_protocol (uristr);
-  if (!protocol)
-    goto no_protocol;
-  if (strcmp (protocol, "udp") != 0)
-    goto wrong_protocol;
-  g_free (protocol);
-
-  location_start = gst_uri_get_location (uristr);
-  if (!location_start)
+  uri = gst_uri_from_string (uristr);
+  if (!uri) {
+    GST_ERROR ("Invalid URI string %s", uristr);
     return FALSE;
+  }
 
-  GST_DEBUG ("got location '%s'", location_start);
-
-  /* VLC compatibility, strip everything before the @ sign. VLC uses that as the
-   * remote address. */
-  location = g_strstr_len (location_start, -1, "@");
-  if (location == NULL)
-    location = location_start;
-  else
-    location += 1;
-
-  if (location[0] == '[') {
-    GST_DEBUG ("parse IPV6 address '%s'", location);
-    location_end = strchr (location, ']');
-    if (location_end == NULL)
-      goto wrong_address;
-
-    *host = g_strndup (location + 1, location_end - location - 1);
-    colptr = strrchr (location_end, ':');
-  } else {
-    GST_DEBUG ("parse IPV4 address '%s'", location);
-    colptr = strrchr (location, ':');
-
-    if (colptr != NULL) {
-      *host = g_strndup (location, colptr - location);
-    } else {
-      *host = g_strdup (location);
-    }
+  /* consider no protocol to be udp:// */
+  protocol = gst_uri_get_scheme (uri);
+  if (!protocol) {
+    GST_ERROR ("error parsing uri %s: no protocol", uristr);
+    goto error;
+  } else if (g_strcmp0 (protocol, "udp")) {
+    GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,
+        protocol);
+    goto error;
+  }
+
+  *host = g_strdup (gst_uri_get_host (uri));
+  if (*host == NULL) {
+    GST_ERROR ("Unknown host");
+    goto error;
   }
+
   GST_DEBUG ("host set to '%s'", *host);
 
-  if (colptr != NULL) {
-    *port = g_ascii_strtoll (colptr + 1, NULL, 10);
-  } else {
-    *port = 0;
+  *port = gst_uri_get_port (uri);
+
+  if (source_list) {
+    const gchar *source = gst_uri_get_query_value (uri, "multicast-source");
+    if (source)
+      gst_udp_parse_multicast_source (source, source_list);
   }
-  g_free (location_start);
 
+  gst_uri_unref (uri);
   return TRUE;
 
-  /* ERRORS */
-no_protocol:
-  {
-    GST_ERROR ("error parsing uri %s: no protocol", uristr);
-    return FALSE;
-  }
-wrong_protocol:
-  {
-    GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,
-        protocol);
-    g_free (protocol);
-    return FALSE;
-  }
-wrong_address:
-  {
-    GST_ERROR ("error parsing uri %s", uristr);
-    g_free (location);
+error:
+  gst_uri_unref (uri);
+  return FALSE;
+}
+
+static gboolean
+gst_udp_source_filter_equal_func (gconstpointer a, gconstpointer b)
+{
+  return g_strcmp0 ((const gchar *) a, (const gchar *) b) == 0;
+}
+
+gboolean
+gst_udp_parse_multicast_source (const gchar * multicast_source,
+    GPtrArray * source_list)
+{
+  gchar **list;
+  guint i;
+  gboolean found = FALSE;
+
+  if (!multicast_source || !source_list)
     return FALSE;
+
+  GST_DEBUG ("Parsing multicast source \"%s\"", multicast_source);
+
+  list = g_strsplit_set (multicast_source, "+-", 0);
+
+  for (i = 0; list[i] != NULL; i++) {
+    gchar *prefix;
+    gboolean is_positive = FALSE;
+
+    if (*list[i] == '\0')
+      continue;
+
+    prefix = g_strrstr (multicast_source, list[i]);
+    g_assert (prefix);
+
+    /* Begin without '+' or '-' prefix, assume it's positive filter */
+    if (prefix == multicast_source) {
+      GST_WARNING ("%s without prefix, assuming that it's positive filter",
+          list[i]);
+      is_positive = TRUE;
+    } else if (*(prefix - 1) == '+') {
+      is_positive = TRUE;
+    }
+
+    if (is_positive &&
+        !g_ptr_array_find_with_equal_func (source_list, list[i],
+            gst_udp_source_filter_equal_func, NULL)) {
+
+      GST_DEBUG ("Add multicast-source %s", list[i]);
+      /* Moves ownership to array */
+      g_ptr_array_add (source_list, g_strdup (list[i]));
+      found = TRUE;
+    }
   }
+
+  g_strfreev (list);
+
+  return found;
 }
diff --git a/gst/udp/gstudpnetutils.h b/gst/udp/gstudpnetutils.h
index a62be562cb8ec8da5feb6afcaac9e0b95ffad1be..f23575134b274528189c32525ffd40b0db6944a8 100644
--- a/gst/udp/gstudpnetutils.h
+++ b/gst/udp/gstudpnetutils.h
@@ -23,7 +23,13 @@
 #ifndef __GST_UDP_NET_UTILS_H__
 #define __GST_UDP_NET_UTILS_H__
 
-gboolean     gst_udp_parse_uri            (const gchar *uristr, gchar **host, guint16 *port);
+gboolean     gst_udp_parse_uri            (const gchar *uristr,
+                                           gchar **host,
+                                           guint16 *port,
+                                           GPtrArray * source_list);
+
+gboolean     gst_udp_parse_multicast_source (const gchar * multicast_source,
+                                             GPtrArray * source_list);
 
 #endif /* __GST_UDP_NET_UTILS_H__*/
 
diff --git a/gst/udp/gstudpsink.c b/gst/udp/gstudpsink.c
index e4320ad834682f24f4c73a2e2f5cfbcc079141b5..c9d87ca85f48b08bdcf025950cd08758917c6dcf 100644
--- a/gst/udp/gstudpsink.c
+++ b/gst/udp/gstudpsink.c
@@ -133,7 +133,7 @@ gst_udpsink_set_uri (GstUDPSink * sink, const gchar * uri, GError ** error)
 
   gst_multiudpsink_remove (GST_MULTIUDPSINK (sink), sink->host, sink->port);
 
-  if (!gst_udp_parse_uri (uri, &host, &port))
+  if (!gst_udp_parse_uri (uri, &host, &port, NULL))
     goto wrong_uri;
 
   g_free (sink->host);
diff --git a/gst/udp/gstudpsrc.c b/gst/udp/gstudpsrc.c
index f957007468974c1dc8a991356e2475fa721a546b..7d9b16a9d8b4066803d2a27619ba3996b6d90bdf 100644
--- a/gst/udp/gstudpsrc.c
+++ b/gst/udp/gstudpsrc.c
@@ -476,7 +476,8 @@ gst_socket_timestamp_message_deserialize (gint level,
 {
   GstSocketTimestampMessage *message;
 
-  if (level != SOL_SOCKET)
+  if (level != SOL_SOCKET
+      || type != gst_socket_timestamp_message_get_msg_type (NULL))
     return NULL;
 
   if (size < sizeof (struct timespec))
@@ -570,6 +571,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
 #define UDP_DEFAULT_LOOP               TRUE
 #define UDP_DEFAULT_RETRIEVE_SENDER_ADDRESS TRUE
 #define UDP_DEFAULT_MTU                (1492)
+#define UDP_DEFAULT_MULTICAST_SOURCE   NULL
 
 enum
 {
@@ -593,6 +595,7 @@ enum
   PROP_RETRIEVE_SENDER_ADDRESS,
   PROP_MTU,
   PROP_SOCKET_TIMESTAMP,
+  PROP_MULTICAST_SOURCE,
 };
 
 static void gst_udpsrc_uri_handler_init (gpointer g_iface, gpointer iface_data);
@@ -779,6 +782,22 @@ gst_udpsrc_class_init (GstUDPSrcClass * klass)
           GST_SOCKET_TIMESTAMP_MODE, GST_SOCKET_TIMESTAMP_MODE_REALTIME,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstUDPSrc:multicast-source:
+   *
+   * List of source to receive the stream from. (IGMPv3 SSM RFC 4604)
+   *
+   * Since: 1.24
+   */
+  g_object_class_install_property (gobject_class, PROP_MULTICAST_SOURCE,
+      g_param_spec_string ("multicast-source", "Multicast source",
+          "List of source to receive the stream with \'+\' (positive filter) or"
+          " \'-\' (negative filter, ignored for now) prefix "
+          "(e.g., \"+SOURCE0+SOURCE1+SOURCE2\"). Alternatively, user can use "
+          "URI query with the key value \"multicast-source\"",
+          UDP_DEFAULT_MULTICAST_SOURCE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
 
   gst_element_class_set_static_metadata (gstelement_class,
@@ -821,6 +840,8 @@ gst_udpsrc_init (GstUDPSrc * udpsrc)
   udpsrc->loop = UDP_DEFAULT_LOOP;
   udpsrc->retrieve_sender_address = UDP_DEFAULT_RETRIEVE_SENDER_ADDRESS;
   udpsrc->mtu = UDP_DEFAULT_MTU;
+  udpsrc->source_list =
+      g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
 
   /* configure basesrc to be a live source */
   gst_base_src_set_live (GST_BASE_SRC (udpsrc), TRUE);
@@ -863,6 +884,9 @@ gst_udpsrc_finalize (GObject * object)
     gst_memory_unref (udpsrc->extra_mem);
   udpsrc->extra_mem = NULL;
 
+  g_ptr_array_unref (udpsrc->source_list);
+  g_free (udpsrc->multicast_source);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -908,8 +932,7 @@ gst_udpsrc_free_cancellable (GstUDPSrc * src)
     g_cancellable_release_fd (src->cancellable);
     src->made_cancel_fd = FALSE;
   }
-  g_object_unref (src->cancellable);
-  src->cancellable = NULL;
+  g_clear_object (&src->cancellable);
 }
 
 static GstFlowReturn
@@ -1239,9 +1262,16 @@ gst_udpsrc_set_uri (GstUDPSrc * src, const gchar * uri, GError ** error)
 {
   gchar *address;
   guint16 port;
+  gboolean source_updated = FALSE;
+  gchar *new_source = NULL;
 
-  if (!gst_udp_parse_uri (uri, &address, &port))
+  GST_OBJECT_LOCK (src);
+  g_ptr_array_set_size (src->source_list, 0);
+
+  if (!gst_udp_parse_uri (uri, &address, &port, src->source_list)) {
+    GST_OBJECT_UNLOCK (src);
     goto wrong_uri;
+  }
 
   if (port == (guint16) - 1)
     port = UDP_DEFAULT_PORT;
@@ -1250,8 +1280,33 @@ gst_udpsrc_set_uri (GstUDPSrc * src, const gchar * uri, GError ** error)
   src->address = address;
   src->port = port;
 
+  if (src->source_list->len > 0) {
+    GString *str = g_string_new (NULL);
+    guint i;
+
+    /* FIXME: gst_udp_parse_uri() will handle only positive filters for now */
+    for (i = 0; i < src->source_list->len; i++) {
+      gchar *s = g_ptr_array_index (src->source_list, i);
+
+      g_string_append_c (str, '+');
+      g_string_append (str, s);
+    }
+
+    new_source = g_string_free (str, FALSE);
+  }
+
+  if (g_strcmp0 (src->multicast_source, new_source) != 0)
+    source_updated = TRUE;
+
+  g_free (src->multicast_source);
+  src->multicast_source = new_source;
+
   g_free (src->uri);
   src->uri = g_strdup (uri);
+  GST_OBJECT_UNLOCK (src);
+
+  if (source_updated)
+    g_object_notify (G_OBJECT (src), "multicast-source");
 
   return TRUE;
 
@@ -1374,6 +1429,17 @@ gst_udpsrc_set_property (GObject * object, guint prop_id, const GValue * value,
     case PROP_SOCKET_TIMESTAMP:
       udpsrc->socket_timestamp_mode = g_value_get_enum (value);
       break;
+    case PROP_MULTICAST_SOURCE:
+      GST_OBJECT_LOCK (udpsrc);
+      g_free (udpsrc->multicast_source);
+      udpsrc->multicast_source = g_value_dup_string (value);
+      g_ptr_array_set_size (udpsrc->source_list, 0);
+      if (udpsrc->multicast_source) {
+        gst_udp_parse_multicast_source (udpsrc->multicast_source,
+            udpsrc->source_list);
+      }
+      GST_OBJECT_UNLOCK (udpsrc);
+      break;
     default:
       break;
   }
@@ -1440,6 +1506,11 @@ gst_udpsrc_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_SOCKET_TIMESTAMP:
       g_value_set_enum (value, udpsrc->socket_timestamp_mode);
       break;
+    case PROP_MULTICAST_SOURCE:
+      GST_OBJECT_LOCK (udpsrc);
+      g_value_set_string (value, udpsrc->multicast_source);
+      GST_OBJECT_UNLOCK (udpsrc);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1518,6 +1589,11 @@ gst_udpsrc_open (GstUDPSrc * src)
   GSocketAddress *bind_saddr;
   GError *err = NULL;
 
+  if (src->source_addrs) {
+    g_list_free_full (src->source_addrs, (GDestroyNotify) g_object_unref);
+    src->source_addrs = NULL;
+  }
+
   gst_udpsrc_create_cancellable (src);
 
   if (src->socket == NULL) {
@@ -1657,19 +1733,48 @@ gst_udpsrc_open (GstUDPSrc * src)
       &&
       g_inet_address_get_is_multicast (g_inet_socket_address_get_address
           (src->addr))) {
+    guint i;
+    GList *iter;
+
+    for (i = 0; i < src->source_list->len; i++) {
+      gchar *source_addr_str = g_ptr_array_index (src->source_list, i);
+      GInetAddress *source_addr = gst_udpsrc_resolve (src, source_addr_str);
+      if (!source_addr) {
+        GST_WARNING_OBJECT (src, "Couldn't resolve address %s",
+            source_addr_str);
+      } else {
+        GST_DEBUG_OBJECT (src, "Adding multicast-source %s", source_addr_str);
+        src->source_addrs = g_list_append (src->source_addrs, source_addr);
+      }
+    }
 
     if (src->multi_iface) {
       GStrv multi_ifaces = g_strsplit (src->multi_iface, ",", -1);
       gchar **ifaces = multi_ifaces;
+
       while (*ifaces) {
         g_strstrip (*ifaces);
         GST_DEBUG_OBJECT (src, "joining multicast group %s interface %s",
             src->address, *ifaces);
-        if (!g_socket_join_multicast_group (src->used_socket,
-                g_inet_socket_address_get_address (src->addr),
-                FALSE, *ifaces, &err)) {
-          g_strfreev (multi_ifaces);
-          goto membership;
+
+        if (!src->source_addrs) {
+          if (!g_socket_join_multicast_group (src->used_socket,
+                  g_inet_socket_address_get_address (src->addr),
+                  FALSE, *ifaces, &err)) {
+            g_strfreev (multi_ifaces);
+            goto membership;
+          }
+        } else {
+          for (iter = src->source_addrs; iter; iter = g_list_next (iter)) {
+            GInetAddress *source_addr = (GInetAddress *) iter->data;
+
+            if (!g_socket_join_multicast_group_ssm (src->used_socket,
+                    g_inet_socket_address_get_address (src->addr),
+                    source_addr, *ifaces, &err)) {
+              g_strfreev (multi_ifaces);
+              goto membership;
+            }
+          }
         }
 
         ifaces++;
@@ -1677,9 +1782,23 @@ gst_udpsrc_open (GstUDPSrc * src)
       g_strfreev (multi_ifaces);
     } else {
       GST_DEBUG_OBJECT (src, "joining multicast group %s", src->address);
-      if (!g_socket_join_multicast_group (src->used_socket,
-              g_inet_socket_address_get_address (src->addr), FALSE, NULL, &err))
-        goto membership;
+
+      if (!src->source_addrs) {
+        if (!g_socket_join_multicast_group (src->used_socket,
+                g_inet_socket_address_get_address (src->addr), FALSE, NULL,
+                &err)) {
+          goto membership;
+        }
+      } else {
+        for (iter = src->source_addrs; iter; iter = g_list_next (iter)) {
+          GInetAddress *source_addr = (GInetAddress *) iter->data;
+          if (!g_socket_join_multicast_group_ssm (src->used_socket,
+                  g_inet_socket_address_get_address (src->addr),
+                  source_addr, NULL, &err)) {
+            goto membership;
+          }
+        }
+      }
     }
 
     if (g_inet_address_get_family (g_inet_socket_address_get_address
@@ -1825,7 +1944,9 @@ gst_udpsrc_unlock (GstBaseSrc * bsrc)
   src = GST_UDPSRC (bsrc);
 
   GST_LOG_OBJECT (src, "Flushing");
+  GST_OBJECT_LOCK (src);
   g_cancellable_cancel (src->cancellable);
+  GST_OBJECT_UNLOCK (src);
 
   return TRUE;
 }
@@ -1839,8 +1960,10 @@ gst_udpsrc_unlock_stop (GstBaseSrc * bsrc)
 
   GST_LOG_OBJECT (src, "No longer flushing");
 
+  GST_OBJECT_LOCK (src);
   gst_udpsrc_free_cancellable (src);
   gst_udpsrc_create_cancellable (src);
+  GST_OBJECT_UNLOCK (src);
 
   return TRUE;
 }
@@ -1856,6 +1979,7 @@ gst_udpsrc_close (GstUDPSrc * src)
         g_inet_address_get_is_multicast (g_inet_socket_address_get_address
             (src->addr))) {
       GError *err = NULL;
+      GList *iter;
 
       if (src->multi_iface) {
         GStrv multi_ifaces = g_strsplit (src->multi_iface, ",", -1);
@@ -1864,25 +1988,55 @@ gst_udpsrc_close (GstUDPSrc * src)
           g_strstrip (*ifaces);
           GST_DEBUG_OBJECT (src, "leaving multicast group %s interface %s",
               src->address, *ifaces);
-          if (!g_socket_leave_multicast_group (src->used_socket,
-                  g_inet_socket_address_get_address (src->addr),
-                  FALSE, *ifaces, &err)) {
-            GST_ERROR_OBJECT (src, "Failed to leave multicast group: %s",
-                err->message);
-            g_clear_error (&err);
+
+          if (!src->source_addrs) {
+            if (!g_socket_leave_multicast_group (src->used_socket,
+                    g_inet_socket_address_get_address (src->addr),
+                    FALSE, *ifaces, &err)) {
+              GST_ERROR_OBJECT (src, "Failed to leave multicast group: %s",
+                  err->message);
+              g_clear_error (&err);
+            }
+          } else {
+            for (iter = src->source_addrs; iter; iter = g_list_next (iter)) {
+              GInetAddress *source_addr = (GInetAddress *) iter->data;
+
+              if (!g_socket_leave_multicast_group_ssm (src->used_socket,
+                      g_inet_socket_address_get_address (src->addr),
+                      source_addr, *ifaces, &err)) {
+                GST_ERROR_OBJECT (src, "Failed to leave multicast group: %s",
+                    err->message);
+                g_clear_error (&err);
+              }
+            }
           }
+
           ifaces++;
         }
         g_strfreev (multi_ifaces);
 
       } else {
         GST_DEBUG_OBJECT (src, "leaving multicast group %s", src->address);
-        if (!g_socket_leave_multicast_group (src->used_socket,
-                g_inet_socket_address_get_address (src->addr), FALSE,
-                NULL, &err)) {
-          GST_ERROR_OBJECT (src, "Failed to leave multicast group: %s",
-              err->message);
-          g_clear_error (&err);
+        if (!src->source_addrs) {
+          if (!g_socket_leave_multicast_group (src->used_socket,
+                  g_inet_socket_address_get_address (src->addr), FALSE,
+                  NULL, &err)) {
+            GST_ERROR_OBJECT (src, "Failed to leave multicast group: %s",
+                err->message);
+            g_clear_error (&err);
+          }
+        } else {
+          for (iter = src->source_addrs; iter; iter = g_list_next (iter)) {
+            GInetAddress *source_addr = (GInetAddress *) iter->data;
+
+            if (!g_socket_leave_multicast_group_ssm (src->used_socket,
+                    g_inet_socket_address_get_address (src->addr),
+                    source_addr, NULL, &err)) {
+              GST_ERROR_OBJECT (src, "Failed to leave multicast group: %s",
+                  err->message);
+              g_clear_error (&err);
+            }
+          }
         }
       }
     }
@@ -1895,6 +2049,11 @@ gst_udpsrc_close (GstUDPSrc * src)
       }
     }
 
+    if (src->source_addrs) {
+      g_list_free_full (src->source_addrs, (GDestroyNotify) g_object_unref);
+      src->source_addrs = NULL;
+    }
+
     g_object_unref (src->used_socket);
     src->used_socket = NULL;
     g_object_unref (src->addr);
diff --git a/gst/udp/gstudpsrc.h b/gst/udp/gstudpsrc.h
index 1f7552481b18d0a8379fa9e657909277a39ad537..af713ff95a0b87264a9a90364c97405e8520518b 100644
--- a/gst/udp/gstudpsrc.h
+++ b/gst/udp/gstudpsrc.h
@@ -65,6 +65,7 @@ struct _GstUDPSrc {
   /* our sockets */
   GSocket   *used_socket;	/* hot */
   GInetSocketAddress *addr;	/* hot */
+  GList     *source_addrs;
 
   GCancellable *cancellable;	/* hot */
 
@@ -83,6 +84,7 @@ struct _GstUDPSrc {
   gboolean   reuse;
   gboolean   loop;
   GstSocketTimestampMode socket_timestamp_mode;
+  gchar     *multicast_source;
 
   /* stats */
   guint      max_size;
@@ -97,6 +99,7 @@ struct _GstUDPSrc {
   GstMemory *extra_mem;
 
   gchar     *uri;
+  GPtrArray *source_list;
 };
 
 struct _GstUDPSrcClass {
diff --git a/gst/videofilter/gstvideoflip.c b/gst/videofilter/gstvideoflip.c
index 07db89969da1aef80f0aaf2f519886d4ca71733c..799513542b63a61d6b1b5a76828e7fd7a4ce8227 100644
--- a/gst/videofilter/gstvideoflip.c
+++ b/gst/videofilter/gstvideoflip.c
@@ -1787,8 +1787,70 @@ gst_video_flip_sink_event (GstBaseTransform * trans, GstEvent * event)
       gst_event_parse_tag (event, &taglist);
 
       if (gst_video_orientation_from_tag (taglist, &method)) {
-        gst_video_flip_set_method (vf, method, TRUE);
+        if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_STREAM) {
+          vf->got_orientation_stream_tag = TRUE;
+        } else if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
+          vf->global_tag_method = method;
+        }
+
+        if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL
+            && vf->got_orientation_stream_tag) {
+          GST_DEBUG_OBJECT (vf,
+              "ignoring global tags as we received stream specific ones: %"
+              GST_PTR_FORMAT, taglist);
+        } else {
+          gst_video_flip_set_method (vf, method, TRUE);
+        }
+
+        if (vf->method == GST_VIDEO_ORIENTATION_AUTO) {
+          /* Update the orientation tag as we rotate the video accordingly.
+           * The event (and so the tag list) can be shared so always copy both. */
+          taglist = gst_tag_list_copy (taglist);
+
+          gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+              "image-orientation", "rotate-0", NULL);
+
+          gst_event_unref (event);
+          event = gst_event_new_tag (taglist);
+        }
+      } else {
+        // no orientation in tag
+        if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_STREAM) {
+          GST_DEBUG_OBJECT (vf,
+              "stream tag does not contain orientation, restore the global one: %d",
+              vf->global_tag_method);
+          vf->got_orientation_stream_tag = FALSE;
+          gst_video_flip_set_method (vf, vf->global_tag_method, TRUE);
+        } else if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
+          vf->global_tag_method = GST_VIDEO_ORIENTATION_IDENTITY;
+
+          if (!vf->got_orientation_stream_tag) {
+            GST_DEBUG_OBJECT (vf,
+                "global taglist withtout orientation, set to identity");
+            gst_video_flip_set_method (vf, GST_VIDEO_ORIENTATION_IDENTITY,
+                TRUE);
+          } else {
+            // keep using the orientation from the stream tag
+          }
+        }
       }
+
+      break;
+    case GST_EVENT_STREAM_START:
+    {
+      const gchar *stream_id;
+
+      gst_event_parse_stream_start (event, &stream_id);
+      if (g_strcmp0 (stream_id, vf->stream_id) != 0) {
+        GST_DEBUG_OBJECT (vf, "new stream, reset orientation from tags");
+        vf->got_orientation_stream_tag = FALSE;
+        vf->global_tag_method = GST_VIDEO_ORIENTATION_IDENTITY;
+        gst_video_flip_set_method (vf, GST_VIDEO_ORIENTATION_IDENTITY, TRUE);
+
+        g_clear_pointer (&vf->stream_id, g_free);
+        vf->stream_id = g_strdup (stream_id);
+      }
+    }
       break;
     default:
       break;
@@ -1833,6 +1895,46 @@ gst_video_flip_get_property (GObject * object, guint prop_id, GValue * value,
   }
 }
 
+static void
+gst_video_flip_finalize (GObject * object)
+{
+  GstVideoFlip *videoflip = GST_VIDEO_FLIP (object);
+
+  g_clear_pointer (&videoflip->stream_id, g_free);
+
+  G_OBJECT_CLASS (gst_video_flip_parent_class)->finalize (object);
+}
+
+static GstStateChangeReturn
+gst_video_flip_change_state (GstElement * element, GstStateChange transition)
+{
+  GstVideoFlip *videoflip = GST_VIDEO_FLIP (element);
+  GstStateChangeReturn result;
+
+  result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      g_clear_pointer (&videoflip->stream_id, g_free);
+      break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
+static void
+gst_video_flip_constructed (GObject * object)
+{
+  GstVideoFlip *self = GST_VIDEO_FLIP (object);
+
+  if (self->method == (GstVideoOrientationMethod) PROP_METHOD_DEFAULT) {
+    gst_video_flip_set_method (self,
+        (GstVideoOrientationMethod) PROP_METHOD_DEFAULT, FALSE);
+  }
+}
+
 static void
 gst_video_flip_class_init (GstVideoFlipClass * klass)
 {
@@ -1846,13 +1948,15 @@ gst_video_flip_class_init (GstVideoFlipClass * klass)
 
   gobject_class->set_property = gst_video_flip_set_property;
   gobject_class->get_property = gst_video_flip_get_property;
+  gobject_class->constructed = gst_video_flip_constructed;
+  gobject_class->finalize = gst_video_flip_finalize;
 
   g_object_class_install_property (gobject_class, PROP_METHOD,
       g_param_spec_enum ("method", "method",
           "method (deprecated, use video-direction instead)",
           GST_TYPE_VIDEO_FLIP_METHOD, PROP_METHOD_DEFAULT,
           GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING |
-          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_override_property (gobject_class, PROP_VIDEO_DIRECTION,
       "video-direction");
   /* override the overriden property's flags to include the mutable in playing
@@ -1860,6 +1964,8 @@ gst_video_flip_class_init (GstVideoFlipClass * klass)
   pspec = g_object_class_find_property (gobject_class, "video-direction");
   pspec->flags |= GST_PARAM_MUTABLE_PLAYING;
 
+  gstelement_class->change_state = gst_video_flip_change_state;
+
   gst_element_class_set_static_metadata (gstelement_class, "Video flipper",
       "Filter/Effect/Video",
       "Flips and rotates video", "David Schleef <ds@schleef.org>");
@@ -1886,6 +1992,12 @@ gst_video_flip_class_init (GstVideoFlipClass * klass)
 static void
 gst_video_flip_init (GstVideoFlip * videoflip)
 {
+  /* We initialize to the default and call set_method() from constructed
+   * if the value hasn't changed, this ensures set_method() does get called
+   * even if the non-construct method / direction properties aren't set
+   */
+  videoflip->method = (GstVideoOrientationMethod) PROP_METHOD_DEFAULT;
+
   /* AUTO is not valid for active method, this is just to ensure we setup the
    * method in gst_video_flip_set_method() */
   videoflip->active_method = GST_VIDEO_ORIENTATION_AUTO;
diff --git a/gst/videofilter/gstvideoflip.h b/gst/videofilter/gstvideoflip.h
index 33201531812dbdd0bac3439279fc2a57b52fc122..5d993d7132eb86db19309f25fb21d10dd7c28862 100644
--- a/gst/videofilter/gstvideoflip.h
+++ b/gst/videofilter/gstvideoflip.h
@@ -76,9 +76,14 @@ struct _GstVideoFlip {
 
   /* < private > */
   GstVideoFormat v_format;
+  gchar *stream_id;
 
   GstVideoOrientationMethod method;
   GstVideoOrientationMethod tag_method;
+  /* TRUE if we received orientation from tag event with GST_TAG_SCOPE_STREAM */
+  gboolean got_orientation_stream_tag;
+  /* orientation received from a tag with GST_TAG_SCOPE_STREAM */
+  GstVideoOrientationMethod global_tag_method;
   GstVideoOrientationMethod proposed_method;
   gboolean change_configuring_method;
   GstVideoOrientationMethod configuring_method;
diff --git a/gst/videofilter/gstvideotemplate.c b/gst/videofilter/gstvideotemplate.c
index 660526a985ceee29e7d1a561378617ec05e95d0d..93aead4e9f0d264dbdeffbdbd32e5f452341a84f 100644
--- a/gst/videofilter/gstvideotemplate.c
+++ b/gst/videofilter/gstvideotemplate.c
@@ -224,7 +224,6 @@ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     videotemplate,
     "Template for a video filter",
     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
-
      static void gst_videotemplate_setup (GstVideofilter * videofilter)
 {
   GstVideotemplate *videotemplate;
diff --git a/gst/videomixer/videomixer2.c b/gst/videomixer/videomixer2.c
index 0ecc0c922471d9068f701052bfff73c814334a18..a220aba7bce2724f3813107ed487c1273ac487d0 100644
--- a/gst/videomixer/videomixer2.c
+++ b/gst/videomixer/videomixer2.c
@@ -816,7 +816,8 @@ gst_videomixer2_update_qos (GstVideoMixer2 * mix, gdouble proportion,
   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
     if (!mix->live && G_UNLIKELY (diff > 0))
       mix->earliest_time =
-          timestamp + 2 * diff + gst_util_uint64_scale_int_round (GST_SECOND,
+          timestamp + MIN (2 * diff,
+          GST_SECOND) + gst_util_uint64_scale_int_round (GST_SECOND,
           GST_VIDEO_INFO_FPS_D (&mix->info), GST_VIDEO_INFO_FPS_N (&mix->info));
     else
       mix->earliest_time = timestamp + diff;
diff --git a/gst/wavenc/gstwavenc.c b/gst/wavenc/gstwavenc.c
index 2392afef6918f71defe2c474ff28cb4a5dc9a0c3..e767c9bd16a3e8e6507947aa3894433065bd36ba 100644
--- a/gst/wavenc/gstwavenc.c
+++ b/gst/wavenc/gstwavenc.c
@@ -533,19 +533,19 @@ gst_wavparse_tags_foreach (const GstTagList * tags, const gchar * tag,
     const gchar *tag;
   } rifftags[] = {
     {
-    GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
-    GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
-    GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
-    GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
-    GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
-    GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
-    GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
-    GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
-    GST_RIFF_INFO_IPRD, GST_TAG_ALBUM}, {
-    GST_RIFF_INFO_ISBJ, GST_TAG_ALBUM_ARTIST}, {
-    GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
-    GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
-    0, NULL}
+        GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
+        GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
+        GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
+        GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
+        GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
+        GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
+        GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
+        GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
+        GST_RIFF_INFO_IPRD, GST_TAG_ALBUM}, {
+        GST_RIFF_INFO_ISBJ, GST_TAG_ALBUM_ARTIST}, {
+        GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
+        GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
+        0, NULL}
   };
   gint n;
   size_t size;
diff --git a/gst/wavparse/gstwavparse.c b/gst/wavparse/gstwavparse.c
index 1b71ccab77195fed948299abd426b99911e9a116..f2fa1e45c578bf19ba9c60a6583ce658973ffc64 100644
--- a/gst/wavparse/gstwavparse.c
+++ b/gst/wavparse/gstwavparse.c
@@ -789,6 +789,11 @@ gst_wavparse_cue_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
     return TRUE;
   }
 
+  if (size < 4) {
+    GST_WARNING_OBJECT (wav, "broken file %d", size);
+    return FALSE;
+  }
+
   ncues = GST_READ_UINT32_LE (data);
 
   if (size < 4 + ncues * 24) {
@@ -888,6 +893,9 @@ gst_wavparse_smpl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
 {
   guint32 note_number;
 
+  if (size < 32)
+    return FALSE;
+
   /*
      manufacturer_id = GST_READ_UINT32_LE (data);
      product_id = GST_READ_UINT32_LE (data + 4);
@@ -1079,6 +1087,11 @@ parse_ds64 (GstWavParse * wav, GstBuffer * buf)
   guint32 sampleCountLow, sampleCountHigh;
 
   gst_buffer_map (buf, &map, GST_MAP_READ);
+  if (map.size < 6 * 4) {
+    GST_WARNING_OBJECT (wav, "Too small ds64 chunk (%" G_GSIZE_FORMAT ")",
+        map.size);
+    return FALSE;
+  }
   dataSizeLow = GST_READ_UINT32_LE (map.data + 2 * 4);
   dataSizeHigh = GST_READ_UINT32_LE (map.data + 3 * 4);
   sampleCountLow = GST_READ_UINT32_LE (map.data + 4 * 4);
@@ -1096,6 +1109,24 @@ parse_ds64 (GstWavParse * wav, GstBuffer * buf)
   return TRUE;
 }
 
+static GstFlowReturn
+gst_wavparse_pull_range_exact (GstWavParse * wav, guint64 offset, guint size,
+    GstBuffer ** buffer)
+{
+  GstFlowReturn res;
+
+  res = gst_pad_pull_range (wav->sinkpad, offset, size, buffer);
+  if (res != GST_FLOW_OK)
+    return res;
+
+  if (gst_buffer_get_size (*buffer) < size) {
+    gst_clear_buffer (buffer);
+    return GST_FLOW_EOS;
+  }
+
+  return res;
+}
+
 static GstFlowReturn
 gst_wavparse_stream_headers (GstWavParse * wav)
 {
@@ -1291,9 +1322,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
 
       buf = NULL;
       if ((res =
-              gst_pad_pull_range (wav->sinkpad, wav->offset, 8,
+              gst_wavparse_pull_range_exact (wav, wav->offset, 8,
                   &buf)) != GST_FLOW_OK)
-        goto header_read_error;
+        goto header_pull_error;
       gst_buffer_map (buf, &map, GST_MAP_READ);
       tag = GST_READ_UINT32_LE (map.data);
       size = GST_READ_UINT32_LE (map.data + 4);
@@ -1311,10 +1342,11 @@ gst_wavparse_stream_headers (GstWavParse * wav)
     }
 
     /* Clip to upstream size if known */
-    if (upstream_size > 0 && size + wav->offset > upstream_size) {
+    if (upstream_size > 0 && size + 8 + wav->offset > upstream_size) {
       GST_WARNING_OBJECT (wav, "Clipping chunk size to file size");
       g_assert (upstream_size >= wav->offset);
-      size = upstream_size - wav->offset;
+      g_assert (upstream_size - wav->offset >= 8);
+      size = upstream_size - wav->offset - 8;
     }
 
     /* wav is a st00pid format, we don't know for sure where data starts.
@@ -1396,9 +1428,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
             gst_buffer_unref (buf);
             buf = NULL;
             if ((res =
-                    gst_pad_pull_range (wav->sinkpad, wav->offset + 8,
+                    gst_wavparse_pull_range_exact (wav, wav->offset + 8,
                         data_size, &buf)) != GST_FLOW_OK)
-              goto header_read_error;
+              goto header_pull_error;
             gst_buffer_extract (buf, 0, &wav->fact, 4);
             wav->fact = GUINT32_FROM_LE (wav->fact);
             gst_buffer_unref (buf);
@@ -1415,8 +1447,7 @@ gst_wavparse_stream_headers (GstWavParse * wav)
         break;
       }
       case GST_RIFF_TAG_acid:{
-        const gst_riff_acid *acid = NULL;
-        const guint data_size = sizeof (gst_riff_acid);
+        const guint data_size = 24;
         gfloat tempo;
 
         GST_INFO_OBJECT (wav, "Have acid chunk");
@@ -1430,25 +1461,24 @@ gst_wavparse_stream_headers (GstWavParse * wav)
           break;
         }
         if (wav->streaming) {
+          const guint8 *data;
           if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
             goto exit;
           }
           gst_adapter_flush (wav->adapter, 8);
-          acid = (const gst_riff_acid *) gst_adapter_map (wav->adapter,
-              data_size);
-          tempo = acid->tempo;
+          data = gst_adapter_map (wav->adapter, data_size);
+          tempo = GST_READ_FLOAT_LE (data + 20);
           gst_adapter_unmap (wav->adapter);
         } else {
           GstMapInfo map;
           gst_buffer_unref (buf);
           buf = NULL;
           if ((res =
-                  gst_pad_pull_range (wav->sinkpad, wav->offset + 8,
-                      size, &buf)) != GST_FLOW_OK)
-            goto header_read_error;
+                  gst_wavparse_pull_range_exact (wav, wav->offset + 8, size,
+                      &buf)) != GST_FLOW_OK)
+            goto header_pull_error;
           gst_buffer_map (buf, &map, GST_MAP_READ);
-          acid = (const gst_riff_acid *) map.data;
-          tempo = acid->tempo;
+          tempo = GST_READ_FLOAT_LE (map.data + 20);
           gst_buffer_unmap (buf, &map);
         }
         /* send data as tags */
@@ -1470,6 +1500,10 @@ gst_wavparse_stream_headers (GstWavParse * wav)
       case GST_RIFF_TAG_LIST:{
         guint32 ltag;
 
+        /* Need at least the ltag */
+        if (size < 4)
+          goto exit;
+
         if (wav->streaming) {
           const guint8 *data = NULL;
 
@@ -1483,9 +1517,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
           gst_buffer_unref (buf);
           buf = NULL;
           if ((res =
-                  gst_pad_pull_range (wav->sinkpad, wav->offset, 12,
+                  gst_wavparse_pull_range_exact (wav, wav->offset, 12,
                       &buf)) != GST_FLOW_OK)
-            goto header_read_error;
+            goto header_pull_error;
           gst_buffer_extract (buf, 8, &ltag, 4);
           ltag = GUINT32_FROM_LE (ltag);
         }
@@ -1512,9 +1546,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
               buf = NULL;
               if (data_size > 0) {
                 if ((res =
-                        gst_pad_pull_range (wav->sinkpad, wav->offset,
+                        gst_wavparse_pull_range_exact (wav, wav->offset,
                             data_size, &buf)) != GST_FLOW_OK)
-                  goto header_read_error;
+                  goto header_pull_error;
               }
             }
             if (data_size > 0) {
@@ -1552,13 +1586,14 @@ gst_wavparse_stream_headers (GstWavParse * wav)
               buf = NULL;
               wav->offset += 12;
               if ((res =
-                      gst_pad_pull_range (wav->sinkpad, wav->offset,
+                      gst_wavparse_pull_range_exact (wav, wav->offset,
                           data_size, &buf)) != GST_FLOW_OK)
-                goto header_read_error;
+                goto header_pull_error;
               gst_buffer_map (buf, &map, GST_MAP_READ);
               gst_wavparse_adtl_chunk (wav, (const guint8 *) map.data,
                   data_size);
               gst_buffer_unmap (buf, &map);
+              gst_buffer_unref (buf);
             }
             wav->offset += GST_ROUND_UP_2 (data_size);
             break;
@@ -1597,9 +1632,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
           gst_buffer_unref (buf);
           buf = NULL;
           if ((res =
-                  gst_pad_pull_range (wav->sinkpad, wav->offset,
+                  gst_wavparse_pull_range_exact (wav, wav->offset,
                       data_size, &buf)) != GST_FLOW_OK)
-            goto header_read_error;
+            goto header_pull_error;
           gst_buffer_map (buf, &map, GST_MAP_READ);
           if (!gst_wavparse_cue_chunk (wav, (const guint8 *) map.data,
                   data_size)) {
@@ -1641,9 +1676,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
           gst_buffer_unref (buf);
           buf = NULL;
           if ((res =
-                  gst_pad_pull_range (wav->sinkpad, wav->offset,
+                  gst_wavparse_pull_range_exact (wav, wav->offset,
                       data_size, &buf)) != GST_FLOW_OK)
-            goto header_read_error;
+            goto header_pull_error;
           gst_buffer_map (buf, &map, GST_MAP_READ);
           if (!gst_wavparse_smpl_chunk (wav, (const guint8 *) map.data,
                   data_size)) {
@@ -1795,6 +1830,17 @@ header_read_error:
         ("Couldn't read in header %d (%s)", res, gst_flow_get_name (res)));
     goto fail;
   }
+header_pull_error:
+  {
+    if (res == GST_FLOW_EOS) {
+      GST_WARNING_OBJECT (wav, "Couldn't pull header %d (%s)", res,
+          gst_flow_get_name (res));
+    } else {
+      GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
+          ("Couldn't pull header %d (%s)", res, gst_flow_get_name (res)));
+    }
+    goto exit;
+  }
 }
 
 /*
diff --git a/gst/xingmux/gstxingmux.c b/gst/xingmux/gstxingmux.c
index 33087ccd699b3788404cc7be45b813a02a25d9b8..5b24396b6ea91ad3cce2ef84213aa9610a3f4bbc 100644
--- a/gst/xingmux/gstxingmux.c
+++ b/gst/xingmux/gstxingmux.c
@@ -70,13 +70,13 @@ typedef struct _GstXingSeekEntry
 static inline GstXingSeekEntry *
 gst_xing_seek_entry_new (void)
 {
-  return g_slice_new (GstXingSeekEntry);
+  return g_new (GstXingSeekEntry, 1);
 }
 
 static inline void
 gst_xing_seek_entry_free (GstXingSeekEntry * entry)
 {
-  g_slice_free (GstXingSeekEntry, entry);
+  g_free (entry);
 }
 
 static void gst_xing_mux_finalize (GObject * obj);
diff --git a/meson.build b/meson.build
index c21a5adeffd9d5ee1a2c16f070dfd80f822793aa..e79b337490fb7b310278da6573e161dcc37a5824 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
 project('gst-plugins-good', 'c',
-  version : '1.22.0',
-  meson_version : '>= 0.62',
+  version : '1.24.12',
+  meson_version : '>= 1.1',
   default_options : [ 'warning_level=1',
                       'buildtype=debugoptimized' ])
 
@@ -22,7 +22,7 @@ if have_cxx
   cxx = meson.get_compiler('cpp')
 endif
 
-glib_req = '>= 2.62.0'
+glib_req = '>= 2.64.0'
 orc_req = '>= 0.4.17'
 
 if gst_version_is_stable
@@ -68,8 +68,6 @@ if cc.get_id() == 'msvc'
       '/we4053', # one void operand for '?:'
       '/we4062', # enumerator 'identifier' in switch of enum 'enumeration' is not handled
       '/we4098', # 'function' : void function returning a value
-      '/we4101', # 'identifier' : unreferenced local variable
-      '/we4189', # 'identifier' : local variable is initialized but not referenced
     ])
   endif
   if have_cxx
@@ -216,7 +214,7 @@ cdata.set('SIZEOF_INT', cc.sizeof('int'))
 cdata.set('SIZEOF_LONG', cc.sizeof('long'))
 cdata.set('SIZEOF_SHORT', cc.sizeof('short'))
 cdata.set('SIZEOF_VOIDP', cc.sizeof('void*'))
-cdata.set('SIZEOF_OFF_T', cc.sizeof('off_t'))
+cdata.set('SIZEOF_OFF_T', cc.sizeof('off_t', prefix : '#include<sys/types.h>'))
 
 have_rtld_noload = cc.has_header_symbol('dlfcn.h', 'RTLD_NOLOAD')
 cdata.set('HAVE_RTLD_NOLOAD', have_rtld_noload)
@@ -231,6 +229,7 @@ cdata.set_quoted('GST_LICENSE', 'LGPL')
 cdata.set_quoted('PACKAGE', 'gst-plugins-good')
 cdata.set_quoted('GETTEXT_PACKAGE', 'gst-plugins-good-1.0')
 cdata.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
+cdata.set_quoted('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/new')
 
 warning_flags = [
   '-Wmissing-declarations',
@@ -321,6 +320,7 @@ gstglproto_dep = dependency('', required : false)
 gstglx11_dep = dependency('', required : false)
 gstglwayland_dep = dependency('', required : false)
 gstglegl_dep = dependency('', required : false)
+gstglviv_fb_dep = dependency('', required : false)
 
 have_gstgl = gstgl_dep.found()
 
@@ -340,9 +340,12 @@ if have_gstgl
   message('GStreamer OpenGL platforms: @0@'.format(' '.join(gst_gl_platforms)))
   message('GStreamer OpenGL apis: @0@'.format(' '.join(gst_gl_apis)))
 
-  foreach ws : ['x11', 'wayland', 'android', 'cocoa', 'eagl', 'win32', 'dispmanx', 'viv_fb']
+  foreach ws : ['x11', 'wayland', 'android', 'cocoa', 'eagl', 'win32', 'dispmanx']
     set_variable('gst_gl_have_window_@0@'.format(ws), gst_gl_winsys.contains(ws))
   endforeach
+  # Handling viv-fb separately, because the winsys is called "viv-fb", but the
+  # variable suffix must be "viv_fb" (dashes are not allowed in variable names).
+  set_variable('gst_gl_have_window_viv_fb', gst_gl_winsys.contains('viv-fb'))
 
   foreach p : ['glx', 'egl', 'cgl', 'eagl', 'wgl']
     set_variable('gst_gl_have_platform_@0@'.format(p), gst_gl_platforms.contains(p))
@@ -370,6 +373,10 @@ if have_gstgl
     gstglegl_dep = dependency('gstreamer-gl-egl-1.0', version : gst_req,
         fallback : ['gst-plugins-base', 'gstglegl_dep'], required: true)
   endif
+  if gst_gl_have_window_viv_fb
+    gstglviv_fb_dep = dependency('gstreamer-gl-viv-fb-1.0', version : gst_req,
+        fallback : ['gst-plugins-base', 'gstglviv_fb_dep'], required: true)
+  endif
 endif
 
 zlib_dep = dependency('zlib')
@@ -449,13 +456,28 @@ endif
 
 if gst_debug_disabled
   message('GStreamer debug system is disabled')
-  if cc.has_argument('-Wno-unused')
-    add_project_arguments('-Wno-unused', language: 'c')
-  endif
-  if have_cxx and cxx.has_argument ('-Wno-unused')
-    add_project_arguments('-Wno-unused', language: 'cpp')
+  if cc.get_argument_syntax() == 'msvc'
+    msvc_args = cc.get_supported_arguments([
+      '/wd4101', # 'identifier' : unreferenced local variable
+      '/wd4189', # 'identifier' : local variable is initialized but not referenced
+    ])
+    add_project_arguments(msvc_args, language: ['c', 'cpp'])
+  else
+    if cc.has_argument('-Wno-unused')
+      add_project_arguments('-Wno-unused', language: 'c')
+    endif
+    if have_cxx and cxx.has_argument ('-Wno-unused')
+      add_project_arguments('-Wno-unused', language: 'cpp')
+    endif
   endif
 else
+  if cc.get_argument_syntax() == 'msvc' and gst_version_is_dev
+    msvc_args = cc.get_supported_arguments([
+      '/we4101', # 'identifier' : unreferenced local variable
+      '/we4189', # 'identifier' : local variable is initialized but not referenced
+    ])
+    add_project_arguments(msvc_args, language: ['c', 'cpp'])
+  endif
   message('GStreamer debug system is enabled')
 endif
 
@@ -520,7 +542,7 @@ endif
 
 configure_file(output : 'config.h', configuration : cdata)
 
-meson.add_dist_script('scripts/gen-changelog.py', meson.project_name(), '1.20.0', meson.project_version())
+meson.add_dist_script('scripts/gen-changelog.py', meson.project_name(), '1.22.0', meson.project_version())
 
 plugin_names = []
 gst_plugins = []
diff --git a/meson_options.txt b/meson_options.txt
index 3e472bd9bcd19d3cf98cb3ff4b878015b3d8e40e..f57883d5daa156b1bdabab46b7769e7693a85af0 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -47,6 +47,8 @@ option('y4m', type : 'feature', value : 'auto')
 # Feature options for plugins with external deps
 option('adaptivedemux2', type : 'feature', value : 'auto', description : '2nd generation adaptive demuxer plugin')
 option('aalib', type : 'feature', value : 'auto', description : 'aalib text console video sink plugin')
+option('amrnb', type : 'feature', value : 'auto', description : 'Adaptive Multi-Rate Narrow-Band audio codec plugin')
+option('amrwbdec', type : 'feature', value : 'auto', description : 'Adaptive Multi-Rate Wide-Band audio decoder plugin')
 option('bz2', type : 'feature', value : 'auto', description : 'libbz2 support in the matroska plugin')
 option('cairo', type : 'feature', value : 'auto', description : 'Cairo overlay plugin')
 option('directsound', type : 'feature', value : 'auto', description : 'Directsound audio source/sink plugin')
@@ -67,7 +69,6 @@ option('osxvideo', type : 'feature', value : 'auto', description : 'macOS Cocoa
 option('png', type : 'feature', value : 'auto', description : 'PNG image codec plugin')
 option('pulse', type : 'feature', value : 'auto', description : 'Pulseaudio audio source/sink plugin')
 option('shout2', type : 'feature', value : 'auto', description : 'Shout-casting network sink plugin based on libshout2')
-option('soup', type : 'feature', value : 'auto', description : 'libsoup HTTP client source/sink plugin')
 option('speex', type : 'feature', value : 'auto', description : 'Speex audio codec plugin')
 option('taglib', type : 'feature', value : 'auto', description : 'Tag-writing plugin based on taglib')
 option('twolame', type : 'feature', value : 'auto', description : 'twolame mp2 audio encoder plugin')
@@ -84,9 +85,19 @@ option('rpicamsrc', type : 'feature', value : 'auto', description : 'Raspberry P
 option('rpi-header-dir', type : 'string', value : '/opt/vc/include', description : 'Directory where VideoCore/MMAL headers and bcm_host.h can be found')
 option('rpi-lib-dir', type : 'string', value : '/opt/vc/lib', description : 'Directory where VideoCore/MMAL libraries can be found')
 
+# soup plugin options
+option('soup', type : 'feature', value : 'auto', description : 'libsoup HTTP client source/sink plugin')
+option('soup-lookup-dep', type : 'boolean', value : false,
+       description : 'Lookup libsoup dep at build time even when building a shared plugin')
+option('soup-version', type : 'combo', value : 'auto', choices : ['auto', '2', '3'],
+       description: 'Force a specific libsoup version if linking to it (N/A for shared builds on Linux)')
+
 # Qt plugin options
 option('qt-method', type: 'combo', value: 'auto', choices: ['auto', 'pkg-config', 'qmake'],
        description: 'Method to use to find Qt')
+option('qt-egl', type: 'feature', value: 'auto', description: 'EGLFS support in the Qt plugins')
+option('qt-wayland', type: 'feature', value: 'auto', description: 'Wayland support in the Qt plugins')
+option('qt-x11', type: 'feature', value: 'auto', description: 'X11 support in the Qt plugins')
 option('qt5', type : 'feature', value : 'auto', yield : true, description : 'Qt5 QML video sink plugin')
 option('qt6', type : 'feature', value : 'auto', yield : true, description : 'Qt6 QML video sink plugin')
 
@@ -95,6 +106,7 @@ option('ximagesrc', type : 'feature', value : 'auto', description : 'X11 ximages
 option('ximagesrc-xshm', type : 'feature', value : 'auto', description : 'X11 ximagesrc plugin (XSHM support)')
 option('ximagesrc-xfixes', type : 'feature', value : 'auto', description : 'X11 ximagesrc plugin (XFixes support)')
 option('ximagesrc-xdamage', type : 'feature', value : 'auto', description : 'X11 ximagesrc plugin (XDamage support)')
+option('ximagesrc-navigation', type : 'feature', value : 'auto', description : 'X11 ximagesrc plugin (Navigation support)')
 
 # v4l2 plugin options
 option('v4l2', type : 'feature', value : 'auto', description : 'Build video4linux2 source/sink plugin')
diff --git a/po/af.po b/po/af.po
index a75e66aff4880e37dd06614e72f0c0b460681290..a003fd707167199f1fadc2fd1148f166afdf0bf5 100644
--- a/po/af.po
+++ b/po/af.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins 0.7.6\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2004-03-18 14:16+0200\n"
 "Last-Translator: Petri Jooste <rkwjpj@puk.ac.za>\n"
 "Language-Team: Afrikaans <i18n@af.org.za>\n"
@@ -93,6 +93,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -284,10 +290,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -302,6 +304,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/az.po b/po/az.po
index 4d3daf176de404be0755ab37e2e14ef73b174212..45fd38269f584e7c7469a6db1baec8c8c561bc44 100644
--- a/po/az.po
+++ b/po/az.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-0.8.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2004-03-19 18:29+0200\n"
 "Last-Translator: Metin Amiroff <metin@karegen.com>\n"
 "Language-Team: Azerbaijani <translation-team-az@lists.sourceforge.net>\n"
@@ -94,6 +94,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -285,10 +291,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -303,6 +305,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/bg.po b/po/bg.po
index d811893f7c7f264b36987585e01be6a4c3649670..6fb0c3b47b2feb7bec1fb9b53829567e7a22e7bd 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,17 +1,17 @@
 # Bulgarian translation of gst-plugins-good.
 # Copyright (C) 2007, 2008, 2009, 2010, 2011, 2016 Free Software Foundation, Inc.
 # Copyright (C) 2017 Free Software Foundation, Inc.
-# Copyright (C) 2021 Alexander Shopov.
+# Copyright (C) 2021, 2024 Alexander Shopov.
 # This file is distributed under the same license as the gst-plugins-good package.
 # Alexander Shopov <ash@kambanaria.org>, 2007, 2008, 2009, 2010, 2011, 2016.
-# Alexander Shopov <ash@kambanaria.org>, 2017, 2021.
+# Alexander Shopov <ash@kambanaria.org>, 2017, 2021, 2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-10-02 17:43+0200\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-09 11:35+0100\n"
 "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
 "Language-Team: Bulgarian <dict@ludost.net>\n"
 "Language: bg\n"
@@ -99,6 +99,12 @@ msgstr "Този файл е повреден и не може да бъде и
 msgid "Invalid atom size."
 msgstr "Неправилен размер на атом."
 
+msgid "Cannot query file size"
+msgstr "Неуспешно запитване за размера на файл"
+
+msgid "Cannot demux file"
+msgstr "Файлът не може да бъде разделен (демултиплексиран)"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Този файл е непълен и не може да бъде изпълнен."
 
@@ -300,10 +306,6 @@ msgstr "Конверторът на устройството „%s“ не по
 msgid "Decoder on device %s has no supported input format"
 msgstr "Декодерът устройството „%s“ не поддържа познати метод за вход."
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Декодерът на устройството „%s“ не поддържа познати метод за изход."
-
 msgid "Failed to start decoding thread."
 msgstr "Неуспешно стартиране на нишката за декодиране."
 
@@ -318,6 +320,9 @@ msgstr "Кодерът на устройството „%s“ не поддър
 msgid "Encoder on device %s has no supported input format"
 msgstr "Кодерът устройството „%s“ не поддържа познати метод за вход."
 
+msgid "Failed to force keyframe."
+msgstr "Неуспешно принудително задаване на кадър."
+
 msgid "Failed to start encoding thread."
 msgstr "Неуспешно стартиране на нишката за кодиране."
 
@@ -426,6 +431,10 @@ msgstr "Неуспешно задаване на изход %u на устрой
 msgid "Cannot operate without a clock"
 msgstr "Работата без часовник е невъзможна."
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Декодерът на устройството „%s“ не поддържа познати метод за изход."
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr ""
 #~ "Промяната на разделителната способност по време на работа все още не се "
diff --git a/po/ca.po b/po/ca.po
index c8d05d6965d9eaeca0be65535f96b76f648d23f1..b9e705ed68573c7be2800d3eab5fcf27e8920c36 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 0.10.28.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2012-01-01 14:19+0100\n"
 "Last-Translator: Gil Forcada <gforcada@gnome.org>\n"
 "Language-Team: Catalan <ca@dodds.net>\n"
@@ -98,6 +98,12 @@ msgstr "Aquest fitxer està malmès i no es pot reproduir."
 msgid "Invalid atom size."
 msgstr "La mida de l'àtom no és vàlida."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Aquest fitxer no està complet i no es pot reproduir."
 
@@ -323,12 +329,6 @@ msgstr ""
 "El controlador del dispositiu «%s» no és compatible amb cap mètode de "
 "captura conegut."
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"El controlador del dispositiu «%s» no és compatible amb cap mètode de "
-"captura conegut."
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "Ha fallat en descodificar la imatge JPEG"
@@ -344,6 +344,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/cs.po b/po/cs.po
index f3d10635f7a87bffc723fb4ac8eeda22b307b6e1..4ef79c705dd4dbb174f3c94c84588e2ff2e4d79d 100644
--- a/po/cs.po
+++ b/po/cs.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-1.12.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2017-09-13 15:00+0200\n"
 "Last-Translator: Marek Černocký <marek@manet.cz>\n"
 "Language-Team: Czech <translation-team-cs@lists.sourceforge.net>\n"
@@ -97,6 +97,12 @@ msgstr "Tento soubor je poškozen a nelze jej přehrát."
 msgid "Invalid atom size."
 msgstr "Neplatná atomická velikost."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Tento soubor není úplný a nelze jej přehrát."
 
@@ -302,10 +308,6 @@ msgstr "Převodník na zařízení „%s“ nepodporuje výstupní formát"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Kodér na zařízení „%s“ nepodporuje vstupní formát"
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Kodér na zařízení „%s“ nepodporuje výstupní formát"
-
 msgid "Failed to start decoding thread."
 msgstr "Selhalo spuštění vlákna dekodéru."
 
@@ -320,6 +322,10 @@ msgstr "Kodér na zařízení „%s“ nepodporuje výstupní formát"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Kodér na zařízení „%s“ nepodporuje vstupní formát"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Selhalo zpracování snímku."
+
 #, fuzzy
 msgid "Failed to start encoding thread."
 msgstr "Selhalo spuštění vlákna dekodéru."
diff --git a/po/da.po b/po/da.po
index fdc6f95bedd6e4b15d697871515ab33e506245a0..9389477133177cfd72cfce9e59cc881eedfef90a 100644
--- a/po/da.po
+++ b/po/da.po
@@ -11,7 +11,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-1.15.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2019-02-17 23:54+0200\n"
 "Last-Translator: Joe Hansen <joedalton2@yahoo.dk>\n"
 "Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
@@ -102,6 +102,12 @@ msgstr "Denne fil er ødelagt og kan ikke afspilles."
 msgid "Invalid atom size."
 msgstr "Ugyldig atomstørrelse."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Denne fil er ufuldstændig og kan ikke afspilles."
 
@@ -309,10 +315,6 @@ msgstr "Konverteringsprogram på enhed %s har ikke et understøttet uddataformat
 msgid "Decoder on device %s has no supported input format"
 msgstr "Afkoderen på enheden %s har ikke et understøttet inddataformat"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Afkoderen på enheden %s har ikke et understøttet uddataformat"
-
 msgid "Failed to start decoding thread."
 msgstr "Kunne ikke starte afkodning af tråd."
 
@@ -327,6 +329,10 @@ msgstr "Koder på enhed %s har ikke et understøttet uddataformat"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Koder på enhed %s har ikke et understøttet inddataformat"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Kunne ikke behandle billed."
+
 msgid "Failed to start encoding thread."
 msgstr "Kunne ikke starte kodningstråd."
 
@@ -432,6 +438,10 @@ msgstr "Kunne ikke sætte uddata %d for enhed %s."
 msgid "Cannot operate without a clock"
 msgstr "Kan ikke fungere uden et ur"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Afkoderen på enheden %s har ikke et understøttet uddataformat"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Ændring af opløsning under kørsel er endnu ikke understøttet."
 
diff --git a/po/de.po b/po/de.po
index e60ce7457de853fbddf07e1df7d4d791fcad0526..15e3afd778b4ee817df9cad2501bea6c8d028c2e 100644
--- a/po/de.po
+++ b/po/de.po
@@ -8,23 +8,23 @@
 # Pipeline = Weiterleitung
 # Stream = Strom
 # mixer  = Mischer
-# Christian Kirbach <christian.kirbach@gmail.com>, 2010, 2011, 2012, 2013, 2014, 2015, 2019, 2021.
+# Christian Kirbach <christian.kirbach@gmail.com>, 2010, 2011, 2012, 2013, 2014, 2015, 2019, 2021, 2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-11-06 23:20+0100\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-17 00:05+0100\n"
 "Last-Translator: Christian Kirbach <christian.kirbach@gmail.com>\n"
 "Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
 "Language: de\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Bugs: Report translation errors to the Language-Team address.\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 3.0\n"
+"X-Bugs: Report translation errors to the Language-Team address.\n"
+"X-Generator: Poedit 3.4.2\n"
 
 msgid "Jack server not found"
 msgstr "Jack-Server nicht gefunden"
@@ -110,6 +110,12 @@ msgstr "Diese Datei ist beschädigt und kann nicht wiedergegeben werden."
 msgid "Invalid atom size."
 msgstr "Ungültige Atom-Größe."
 
+msgid "Cannot query file size"
+msgstr "Die Dateigröße kann nicht erfragt werden"
+
+msgid "Cannot demux file"
+msgstr "Die Datei kann nicht de-multiplext werden"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Diese Datei ist unvollständig und kann nicht wiedergegeben werden."
 
@@ -320,10 +326,6 @@ msgstr "Der Umwandler auf Gerät %s hat kein unterstütztes Ausgabeformat"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Der Dekodierer auf Gerät %s hat kein unterstütztes Eingabeformat"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Der Dekodierer auf Gerät %s hat kein unterstütztes Ausgabeformat"
-
 msgid "Failed to start decoding thread."
 msgstr "Starten des Thread zur Dekodierung ist fehlgeschlagen."
 
@@ -338,6 +340,9 @@ msgstr "Der Kodierer auf Gerät %s hat kein unterstütztes Ausgabeformat"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Der Kodierer auf Gerät %s hat kein unterstütztes Eingabeformat"
 
+msgid "Failed to force keyframe."
+msgstr "Erzwingen eines Schlüssel-Einzelbilds schlug fehl."
+
 msgid "Failed to start encoding thread."
 msgstr "Starten des Thread zur Dekodierung schlug fehl."
 
@@ -450,6 +455,10 @@ msgstr "Festlegen der Ausgabe »%u« des Geräts »%s« schlug fehl."
 msgid "Cannot operate without a clock"
 msgstr "Es kann nicht ohne einen Taktgeber gearbeitet werden."
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Der Dekodierer auf Gerät %s hat kein unterstütztes Ausgabeformat"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Ein Wechsel der Auflösung zur Laufzeit wird noch nicht unterstützt."
 
diff --git a/po/el.po b/po/el.po
index 899e97b456db7e28a45f4ead64a616acd596f4fe..cc5b83ec252f36b3f57d91c59d6c21eb6bd6b984 100644
--- a/po/el.po
+++ b/po/el.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 0.10.28.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2012-05-05 19:05+0100\n"
 "Last-Translator: Savvas Radevic <vicedar@gmail.com>\n"
 "Language-Team: Greek <team@lists.gnome.gr>\n"
@@ -97,6 +97,12 @@ msgstr "Το αρχείο αυτό είναι κατεστραμμένο και
 msgid "Invalid atom size."
 msgstr "Άκυρο μέγεθος ατόμου."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Το αρχείο αυτό είναι ανολοκλήρωτο και δεν μπορεί να αναπαραχθεί."
 
@@ -304,10 +310,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr "Ο οδηγός της συσκευής '%s' δεν υποστηρίζει κάποια γνωστή μέθοδο λήψης."
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Ο οδηγός της συσκευής '%s' δεν υποστηρίζει κάποια γνωστή μέθοδο λήψης."
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "Αποτυχία αποκωδικοποίησης της εικόνας JPEG"
@@ -323,6 +325,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/en_GB.po b/po/en_GB.po
index 6536535a55a29d5bc94caa6f36c3f508c3c5f561..b6ee82c85b5a8b64846fefba89c932e30c548148 100644
--- a/po/en_GB.po
+++ b/po/en_GB.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins 0.8.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2004-04-26 10:41-0400\n"
 "Last-Translator: Gareth Owen <gowen72@yahoo.com>\n"
 "Language-Team: English (British) <en_gb@li.org>\n"
@@ -93,6 +93,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -285,10 +291,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -303,6 +305,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/eo.po b/po/eo.po
index 113fd74107e5f56bc1d11097a38b08ffcfa325f0..4d253eae38fcdb2156d7e59ca0782466ea3717ad 100644
--- a/po/eo.po
+++ b/po/eo.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.19.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2023-01-08 12:06-0300\n"
 "Last-Translator: Felipe Castro <fefcas@gmail.com>\n"
 "Language-Team: Esperanto <translation-team-eo@lists.sourceforge.net>\n"
@@ -98,6 +98,12 @@ msgstr "Tiu ĉi dosiero estas difektita kaj ne povas esti ludata."
 msgid "Invalid atom size."
 msgstr "Malvalida grando de ATOM-fluo."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Tiu ĉi dosiero estas malkompleta kaj ne povas esti ludata."
 
@@ -301,10 +307,6 @@ msgstr "Konvertilo en la aparato %s havas neniun subtenatan eligan formon"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Malkodilo en la aparato %s havas neniun subtenatan enigan formon"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Malkodilo en la aparato %s havas neniun subtenatan eligan formon"
-
 msgid "Failed to start decoding thread."
 msgstr "Malsukcesis lanĉi malkodilan fadenon."
 
@@ -319,6 +321,10 @@ msgstr "Enkodilo en la aparato %s havas neniun subtenatan eligan formon"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Enkodilo en la aparato %s havas neniun subtenatan enigan formon"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Malsukcesis procezi kadron."
+
 msgid "Failed to start encoding thread."
 msgstr "Malsukcesis lanĉi enkodigan fadenon."
 
@@ -426,6 +432,10 @@ msgstr "Malsukcesis difini eligon %u en aparato %s."
 msgid "Cannot operate without a clock"
 msgstr "Ne eblas funkcii sen horloĝo"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Malkodilo en la aparato %s havas neniun subtenatan eligan formon"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Ŝanĝo de distingpovon dum plenumado ankoraŭ ne estas subtenata."
 
diff --git a/po/es.po b/po/es.po
index 095a52477b098eb3a1ad0750faae8d65154ea35d..e446d52855026fdaf00218c64800f9ac739abab1 100644
--- a/po/es.po
+++ b/po/es.po
@@ -1,15 +1,15 @@
-# translation of gst-plugins-good-1.19.2.po to Español
+# translation of gst-plugins-good-1.24.0.po to Español
 # spanish translation for gst-plugins-good
 # This file is put in the public domain.
 # Jorge González González <aloriel@gmail.com>, 2007, 2008, 2009, 2010, 2011.
-# Cristian Othón Martínez Vera <cfuga@cfuga.mx>, 2022
+# Cristian Othón Martínez Vera <cfuga@cfuga.mx>, 2022, 2023, 2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2022-03-04 17:35-0600\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-07 11:34-0600\n"
 "Last-Translator: Cristian Othón Martínez Vera <cfuga@cfuga.mx>\n"
 "Language-Team: Spanish <es@tp.org.es>\n"
 "Language: es\n"
@@ -101,6 +101,12 @@ msgstr "Este archivo está corrupto y no se puede reproducir."
 msgid "Invalid atom size."
 msgstr "El tamaño atom no es válido."
 
+msgid "Cannot query file size"
+msgstr "No se puede obtener el tamaño del archivo"
+
+msgid "Cannot demux file"
+msgstr "No se puede decodificar el archivo"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Este archivo está incompleto y no se puede reproducir."
 
@@ -314,11 +320,6 @@ msgstr ""
 "El decodificador en el dispositivo %s no tiene un formato de entrada "
 "soportado"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"El decodificador en el dispositivo %s no tiene un formato de salida soportado"
-
 msgid "Failed to start decoding thread."
 msgstr "Falló al iniciar el hilo de decodificación."
 
@@ -335,6 +336,9 @@ msgid "Encoder on device %s has no supported input format"
 msgstr ""
 "El codificador en el dispositivo %s no tiene un formato de entrada soportado."
 
+msgid "Failed to force keyframe."
+msgstr "Falló al forzar el fotograma."
+
 msgid "Failed to start encoding thread."
 msgstr "Falló al iniciar el hilo de codificación."
 
@@ -449,6 +453,12 @@ msgstr "Falló al establecer la salida %u en el dispositivo %s."
 msgid "Cannot operate without a clock"
 msgstr "No se puede operar sin reloj"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "El decodificador en el dispositivo %s no tiene un formato de salida "
+#~ "soportado"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr ""
 #~ "El cambio de resolución durante la reproducción aún no está soportado."
diff --git a/po/eu.po b/po/eu.po
index 87317a2518a7803b7251feeb3888d6bc31df46be..386c8916fd82fbdb8e35cabee56db303146457a5 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-0.10.18.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2010-03-25 12:37+0100\n"
 "Last-Translator: Mikel Olasagasti Uranga <hey_neken@mundurat.net>\n"
 "Language-Team: Basque <translation-team-eu@lists.sourceforge.net>\n"
@@ -99,6 +99,12 @@ msgstr "Fitxategi hau hondatua dago, eta ezin da erreproduzitu."
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Fitxategi hau osatu gabea dago, eta ezin da erreproduzitu."
 
@@ -319,11 +325,6 @@ msgid "Decoder on device %s has no supported input format"
 msgstr ""
 "'%s' gailuaren kontrolatzaileak ez du onartzen kaptura-metodo ezagunik."
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"'%s' gailuaren kontrolatzaileak ez du onartzen kaptura-metodo ezagunik."
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "Huts egin du JPEG irudia deskodetzean"
@@ -339,6 +340,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/fi.po b/po/fi.po
index 405ade0da498a1c24c00234b64dc496a734d5275..302e786e15754bcac06dfd654f9eb5ae0276c5e5 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -11,7 +11,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 0.10.25.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2010-11-17 23:03+0200\n"
 "Last-Translator: Tommi Vainikainen <Tommi.Vainikainen@iki.fi>\n"
 "Language-Team: Finnish <translation-team-fi@lists.sourceforge.net>\n"
@@ -100,6 +100,12 @@ msgstr "Tiedosto on vioittunut eikä sitä voida näyttää."
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Tiedosto on vajaa eikä sitä voida esittää."
 
@@ -306,10 +312,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr "Laitteen ”%s” ajuri ei tue mitään tunnettua kaappaustapaa."
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Laitteen ”%s” ajuri ei tue mitään tunnettua kaappaustapaa."
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "JPEG-kuvan purku epäonnistui"
@@ -325,6 +327,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/fr.po b/po/fr.po
index b7618cd2bf4ea34ea4c88161b586ee4e91f083d1..fd760532338f6861bc4afa7b90b4201b0ba44983 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.19.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2022-07-05 00:15+0200\n"
 "Last-Translator: Stéphane Aulery <lkppo@free.fr>\n"
 "Language-Team: French <traduc@traduc.org>\n"
@@ -99,6 +99,12 @@ msgstr "Ce fichier est corrompu et ne peut pas être lu."
 msgid "Invalid atom size."
 msgstr "Taille d’atome non valide."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Ce fichier n’est pas complet et ne peut donc pas être lu."
 
@@ -320,11 +326,6 @@ msgid "Decoder on device %s has no supported input format"
 msgstr ""
 "Le décodeur du périphérique « %s » ne prend en charge aucun format d’entrée"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"Le décodeur du périphérique « %s » ne prend en charge aucun format de sortie"
-
 msgid "Failed to start decoding thread."
 msgstr "Échec de démarrage du processus de décodage."
 
@@ -341,6 +342,10 @@ msgid "Encoder on device %s has no supported input format"
 msgstr ""
 "L’encodeur du périphérique « %s » ne prend en charge aucun format d’entrée"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Échec du traitement de frame."
+
 msgid "Failed to start encoding thread."
 msgstr "Échec de démarrage du processus d'encodage."
 
@@ -459,6 +464,12 @@ msgstr "Impossible de définir la sortie %u du périphérique %s."
 msgid "Cannot operate without a clock"
 msgstr "Impossible de fonctionner sans horloge"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "Le décodeur du périphérique « %s » ne prend en charge aucun format de "
+#~ "sortie"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr ""
 #~ "La modification de la résolution au cours de l’exécution n’est pas encore "
diff --git a/po/fur.po b/po/fur.po
index 0d8d161a1a43482e226ba4888843d4aea9b9c2f1..8cca99ffcf33a9fe71e7028e341b76e10654142f 100644
--- a/po/fur.po
+++ b/po/fur.po
@@ -1,22 +1,24 @@
-# SOME DESCRIPTIVE TITLE.
+# translation of gst-plugins-good-1.19.2.po to Friulian
+# friulian translation for gst-plugins-good
 # This file is put in the public domain.
-# FABIO TOMAT <f.t.public@gmail.com>, 2017.
+# Fabio Tomat <f.t.public@gmail.com>, 2023.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.10.0\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2017-02-17 10:54+0100\n"
-"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-10-03 14:10+0000\n"
+"Last-Translator: Fabio T. <f.t.public@gmail.com>\n"
 "Language-Team: Friulian <f.t.public@gmail.com>\n"
 "Language: fur\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
+"X-Editor: HaiPO 2.0 beta\n"
 "X-Generator: Poedit 1.8.11\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 msgid "Jack server not found"
 msgstr "Servidôr Jack no cjatât"
@@ -24,13 +26,14 @@ msgstr "Servidôr Jack no cjatât"
 msgid "Failed to decode JPEG image"
 msgstr "No si è rivâts a decodificâ la imagjin JPEG"
 
-#, fuzzy
 msgid "Failed to read memory"
-msgstr "No si è rivâts a assegnâ la memorie domandade."
+msgstr "Nol è stât pussibil lei la memorie"
 
 msgid ""
 "Failed to configure LAME mp3 audio encoder. Check your encoding parameters."
 msgstr ""
+"Nol è stât pussibil configurâ il codificadôr audio mp3 LAME. Controle i tiei "
+"parametris di codifiche."
 
 #. <php-emulation-mode>three underscores for ___rate is really really really
 #. * private as opposed to one underscore<php-emulation-mode>
@@ -41,6 +44,8 @@ msgid ""
 "The requested bitrate %d kbit/s for property '%s' is not allowed. The "
 "bitrate was changed to %d kbit/s."
 msgstr ""
+"Il bitrate domandât %d kbit/s pe proprietât '%s' nol è ametût. Il bitrate al "
+"è stât cambiât a %d kbit/s."
 
 #. TRANSLATORS: 'song title' by 'artist name'
 #, c-format
@@ -76,6 +81,8 @@ msgstr "Il servidôr nol supuarte la ricercje."
 
 msgid "Failed to configure TwoLAME encoder. Check your encoding parameters."
 msgstr ""
+"Nol è stât pussibil configurâ il codificadôr TwoLAME. Controle i tiei "
+"parametris pe codifiche."
 
 msgid "No or invalid input audio, AVI stream will be corrupt."
 msgstr "Nissun o invalit audio in jentrade, il flus AVI al sarà ruvinât."
@@ -96,6 +103,12 @@ msgstr "Chest file al è ruvinât e nol pues jessi riprodot."
 msgid "Invalid atom size."
 msgstr "Dimension atom no valide."
 
+msgid "Cannot query file size"
+msgstr "Impussibil domandâ la dimension dal file"
+
+msgid "Cannot demux file"
+msgstr "Impussibil demultiplâ il file"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Chest file nol è complet e nol pues jessi riprodot."
 
@@ -171,7 +184,7 @@ msgid "CoreAudio device could not be opened"
 msgstr "Nol è stât pussibil vierzi il dispositîf CoreAudio"
 
 msgid "Raspberry Pi Camera Module"
-msgstr ""
+msgstr "Modul fotocjamare Raspberry Pi"
 
 #, c-format
 msgid "Error reading %d bytes from device '%s'."
@@ -195,17 +208,16 @@ msgstr "Il driver dal dispositîf '%s' nol supuarte il metodi IO %d"
 msgid "The driver of device '%s' does not support any known IO method."
 msgstr "Il driver dal dispositîf '%s' nol supuarte nissun metodi IO cognossût."
 
-#, fuzzy
 msgid "Invalid caps"
-msgstr "Dimension atom no valide."
+msgstr "Capacitâts no validis"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' has no supported format"
-msgstr "Il codificadôr sul dispositîf %s nol à formâts di jentrade supuartâts"
+msgstr "Il dispositîf '%s' nol à un formât supuartâts"
 
 #, c-format
 msgid "Device '%s' failed during initialization"
-msgstr ""
+msgstr "Il dispositîf '%s' al à falât dilunc la inizializazion"
 
 #, c-format
 msgid "Device '%s' is busy"
@@ -221,15 +233,15 @@ msgstr "Il dispositîf '%s' nol pues caturâ tal formât specificât"
 
 #, c-format
 msgid "Device '%s' does support non-contiguous planes"
-msgstr ""
+msgstr "Il dispositîf '%s' al supuarte i “plans no-contiguis”"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s interlacing"
-msgstr "Il servidôr nol supuarte la ricercje."
+msgstr "Il dispositîf '%s' nol supuarte l'interlaçament %s"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s colorimetry"
-msgstr "Il driver dal dispositîf '%s' nol supuarte il metodi IO %d"
+msgstr "Il dispositîf '%s' nol supuarte la colorimetrie %s"
 
 #, c-format
 msgid "Could not get parameters on device '%s'"
@@ -246,22 +258,20 @@ msgstr "Il dispositîf video nol à furnît un formât di jessude."
 msgid "Video device returned invalid dimensions."
 msgstr "Il dispositîf video al à tornât dimensions no validis."
 
-#, fuzzy
 msgid "Video device uses an unsupported interlacing method."
-msgstr "Il dispositîf video al dopre un metodi di interlazament no supuartât"
+msgstr "Il dispositîf video al dopre un metodi di interlaçament no supuartât."
 
-#, fuzzy
 msgid "Video device uses an unsupported pixel format."
-msgstr "Il dispositîf video al dopre un formât pixel no supuartât."
+msgstr "Il dispositîf video al dopre un formât pixel no supuartât."
 
 msgid "Failed to configure internal buffer pool."
-msgstr ""
+msgstr "Nol è stât pussibil configurâ il bacin de memorie tampon interne."
 
 msgid "Video device did not suggest any buffer size."
 msgstr "Il dispositîf video nol à sugjerît nissune dimension di buffer."
 
 msgid "No downstream pool to import from."
-msgstr ""
+msgstr "Nissun bacin di “flus dâts in jù” di dulà impuartâ."
 
 #, c-format
 msgid "Failed to get settings of tuner %d on device '%s'."
@@ -289,7 +299,7 @@ msgid "Failed to change mute state for device '%s'."
 msgstr "No si è rivâts a cambiâ il stât di cidin pal dispositîf '%s'."
 
 msgid "Failed to allocated required memory."
-msgstr ""
+msgstr "Nol è stât pussibil assegnâ la memorie necessarie."
 
 msgid "Failed to allocate required memory."
 msgstr "No si è rivâts a assegnâ la memorie domandade."
@@ -302,16 +312,13 @@ msgstr "Il convertidôr sul dispositîf %s nol à formâts di jentrade supuartâ
 msgid "Converter on device %s has no supported output format"
 msgstr "Il convertidôr sul dispositîf %s nol à formâts di jessude supuartâts"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Decoder on device %s has no supported input format"
-msgstr "Il codificadôr sul dispositîf %s nol à formâts di jentrade supuartâts"
-
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Il codificadôr sul dispositîf %s nol à formâts di jessude supuartâts"
+msgstr ""
+"Il decodificadôr sul dispositîf %s nol à un formât di jentrade supuartât"
 
 msgid "Failed to start decoding thread."
-msgstr ""
+msgstr "Nol è stât pussibil inviâ il thread di decodifiche."
 
 msgid "Failed to process frame."
 msgstr "Impussibil processâ fotogram."
@@ -324,8 +331,11 @@ msgstr "Il codificadôr sul dispositîf %s nol à formâts di jessude supuartât
 msgid "Encoder on device %s has no supported input format"
 msgstr "Il codificadôr sul dispositîf %s nol à formâts di jentrade supuartâts"
 
+msgid "Failed to force keyframe."
+msgstr "Impussibil sfuarçâ il fotogram clâf."
+
 msgid "Failed to start encoding thread."
-msgstr ""
+msgstr "Nol è stât pussibil inviâ il thread di codifiche."
 
 #, c-format
 msgid ""
@@ -379,7 +389,7 @@ msgstr "Il dispositîf '%s' nol è un dispositîf M2M."
 
 #, c-format
 msgid "Could not dup device '%s' for reading and writing."
-msgstr ""
+msgstr "Impussibil duplicâ il dispositîf '%s' pe leture e scriture."
 
 #, c-format
 msgid "Failed to set norm for device '%s'."
@@ -409,9 +419,10 @@ msgstr "No si è rivâts a vê il valôr pal control %d sul dispositîf '%s'."
 msgid "Failed to set value %d for control %d on device '%s'."
 msgstr "No si è rivâts a meti il valôr %d pal control %d sul dispositîf '%s'."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set value %s for control %d on device '%s'."
-msgstr "No si è rivâts a meti il valôr %d pal control %d sul dispositîf '%s'."
+msgstr ""
+"Nol è stât pussibil stabilî il valôr %s pal control %d sul dispositîf '%s'."
 
 #, c-format
 msgid "Failed to get current input on device '%s'. May be it is a radio device"
@@ -419,9 +430,9 @@ msgstr ""
 "No si è rivâts a vê la jentrade atuâl sul dispositîf '%s'. Al podarès jessi "
 "un dispositîf radio"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set input %u on device %s."
-msgstr "No si è rivâts a stabilî la jentrade %d sul dispositîf %s."
+msgstr "Nol è stât pussibil stabilî la jentrade %u sul dispositîf %s."
 
 #, c-format
 msgid ""
@@ -430,14 +441,18 @@ msgstr ""
 "No si è rivâts a vê la jessude atuâl sul dispositîf '%s'. Al podarès jessi "
 "un dispositîf radio"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set output %u on device %s."
-msgstr "No si è rivâts a stabilî la jessude %d sul dispositîf %s."
+msgstr "Nol è stât pussibil stabilî la jessude %u sul dispositîf %s."
 
 msgid "Cannot operate without a clock"
 msgstr "Impussibil operâ cence un orloi"
 
 #, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "Il decodificadôr sul dispositîf %s nol à un formât di jessude supuartât"
+
 #~ msgid "This file contains too many streams. Only playing first %d"
 #~ msgstr "Chest file al conten masse flus. Si riprodusarà nome i prins %d"
 
diff --git a/po/gl.po b/po/gl.po
index 14959f64dfc5d5d5dd9849c30053338ab96cb506..2163716584baf1c698cdc5e7de19238de8000af1 100644
--- a/po/gl.po
+++ b/po/gl.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.0.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2012-12-15 03:46+0200\n"
 "Last-Translator: Fran Dieguez <frandieguez@ubuntu.com>\n"
 "Language-Team: Galician <proxecto@trasno.net>\n"
@@ -98,6 +98,12 @@ msgstr "Este ficheiro está danado e non pode reproducirse."
 msgid "Invalid atom size."
 msgstr "Tamaño atom non válido."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Este ficheiro está incompleto e non pode reproducirse."
 
@@ -314,10 +320,6 @@ msgstr "O controlador do dispositivo «%s» non admite o método IO %d"
 msgid "Decoder on device %s has no supported input format"
 msgstr "O controlador do dispositivo «%s» non admite o método IO %d"
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "O controlador do dispositivo «%s» non admite o método IO %d"
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "Produciuse un erro ao descodificar a imaxe JPEG"
@@ -333,6 +335,9 @@ msgstr "O controlador do dispositivo «%s» non admite o método IO %d"
 msgid "Encoder on device %s has no supported input format"
 msgstr "O controlador do dispositivo «%s» non admite o método IO %d"
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/gst-plugins-good-1.0.pot b/po/gst-plugins-good-1.0.pot
index b2fc4a2abc81ac682306777843e23c057dcb20b5..7007684f330dbedda478a64cd7a440f1b7ebe1cf 100644
--- a/po/gst-plugins-good-1.0.pot
+++ b/po/gst-plugins-good-1.0.pot
@@ -6,9 +6,9 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.22.0\n"
+"Project-Id-Version: gst-plugins-good-1.24.12\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-23 19:32+0000\n"
+"POT-Creation-Date: 2025-01-29 20:13+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,18 +17,18 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ext/jack/gstjackaudiosink.c:359 ext/jack/gstjackaudiosrc.c:364
+#: ext/jack/gstjackaudiosink.c:360 ext/jack/gstjackaudiosrc.c:365
 msgid "Jack server not found"
 msgstr ""
 
-#: ext/jpeg/gstjpegdec.c:983 ext/jpeg/gstjpegdec.c:1255
-#: ext/jpeg/gstjpegdec.c:1264 ext/jpeg/gstjpegdec.c:1274
-#: ext/jpeg/gstjpegdec.c:1283 ext/jpeg/gstjpegdec.c:1567
-#: ext/jpeg/gstjpegdec.c:1595
+#: ext/jpeg/gstjpegdec.c:983 ext/jpeg/gstjpegdec.c:1260
+#: ext/jpeg/gstjpegdec.c:1269 ext/jpeg/gstjpegdec.c:1279
+#: ext/jpeg/gstjpegdec.c:1288 ext/jpeg/gstjpegdec.c:1580
+#: ext/jpeg/gstjpegdec.c:1608
 msgid "Failed to decode JPEG image"
 msgstr ""
 
-#: ext/jpeg/gstjpegdec.c:1555
+#: ext/jpeg/gstjpegdec.c:1562
 msgid "Failed to read memory"
 msgstr ""
 
@@ -54,36 +54,36 @@ msgstr ""
 msgid "'%s' by '%s'"
 msgstr ""
 
-#: ext/shout2/gstshout2.c:706 ext/shout2/gstshout2.c:716
+#: ext/shout2/gstshout2.c:717 ext/shout2/gstshout2.c:727
 msgid "Could not connect to server"
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1108
+#: ext/soup/gstsouphttpsrc.c:1145
 msgid "No URL set."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1623
+#: ext/soup/gstsouphttpsrc.c:1664
 msgid "Could not resolve server name."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1628
+#: ext/soup/gstsouphttpsrc.c:1669
 msgid "Could not establish connection to server."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1632
+#: ext/soup/gstsouphttpsrc.c:1673
 msgid "Secure connection setup failed."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1638
+#: ext/soup/gstsouphttpsrc.c:1679
 msgid ""
 "A network error occurred, or the server closed the connection unexpectedly."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1643
+#: ext/soup/gstsouphttpsrc.c:1684
 msgid "Server sent bad data."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1949
+#: ext/soup/gstsouphttpsrc.c:1984
 msgid "Server does not support seeking."
 msgstr ""
 
@@ -95,45 +95,53 @@ msgstr ""
 msgid "No or invalid input audio, AVI stream will be corrupt."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:505 gst/isomp4/qtdemux.c:510
+#: gst/isomp4/qtdemux.c:508 gst/isomp4/qtdemux.c:513
 msgid "This file contains no playable streams."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:556 gst/isomp4/qtdemux.c:7333 gst/isomp4/qtdemux.c:7402
-#: gst/isomp4/qtdemux.c:7708 gst/isomp4/qtdemux.c:9101
+#: gst/isomp4/qtdemux.c:559 gst/isomp4/qtdemux.c:8013 gst/isomp4/qtdemux.c:8082
+#: gst/isomp4/qtdemux.c:8398 gst/isomp4/qtdemux.c:9783
 msgid "This file is invalid and cannot be played."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:3039
+#: gst/isomp4/qtdemux.c:3071
 msgid "Cannot play stream because it is encrypted with PlayReady DRM."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:4356 gst/isomp4/qtdemux.c:8488
-#: gst/isomp4/qtdemux.c:8495 gst/isomp4/qtdemux.c:9803
-#: gst/isomp4/qtdemux.c:10245 gst/isomp4/qtdemux.c:10252
-#: gst/isomp4/qtdemux.c:13389
+#: gst/isomp4/qtdemux.c:4727 gst/isomp4/qtdemux.c:9175
+#: gst/isomp4/qtdemux.c:9182 gst/isomp4/qtdemux.c:10507
+#: gst/isomp4/qtdemux.c:10949 gst/isomp4/qtdemux.c:10956
+#: gst/isomp4/qtdemux.c:14275
 msgid "This file is corrupt and cannot be played."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:4598
+#: gst/isomp4/qtdemux.c:4969
 msgid "Invalid atom size."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:4677
+#: gst/isomp4/qtdemux.c:5022 gst/isomp4/qtdemux.c:5029
+msgid "Cannot query file size"
+msgstr ""
+
+#: gst/isomp4/qtdemux.c:5038
+msgid "Cannot demux file"
+msgstr ""
+
+#: gst/isomp4/qtdemux.c:5078
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:11386
+#: gst/isomp4/qtdemux.c:12167
 msgid "The video in this file might not play correctly."
 msgstr ""
 
-#: gst/rtsp/gstrtspsrc.c:7885
+#: gst/rtsp/gstrtspsrc.c:8074
 msgid ""
 "No supported stream was found. You might need to install a GStreamer RTSP "
 "extension plugin for Real media streams."
 msgstr ""
 
-#: gst/rtsp/gstrtspsrc.c:7890
+#: gst/rtsp/gstrtspsrc.c:8079
 msgid ""
 "No supported stream was found. You might need to allow more transport "
 "protocols or may otherwise be missing the right GStreamer RTSP extension "
@@ -200,109 +208,109 @@ msgstr ""
 msgid "Raspberry Pi Camera Module"
 msgstr ""
 
-#: sys/v4l2/gstv4l2bufferpool.c:1930
+#: sys/v4l2/gstv4l2bufferpool.c:1870
 #, c-format
 msgid "Error reading %d bytes from device '%s'."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:1241
+#: sys/v4l2/gstv4l2object.c:1313
 #, c-format
 msgid "Failed to enumerate possible video formats device '%s' can work with"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:3192
+#: sys/v4l2/gstv4l2object.c:3238
 #, c-format
 msgid "Could not map buffers from device '%s'"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:3200
+#: sys/v4l2/gstv4l2object.c:3246
 #, c-format
 msgid "The driver of device '%s' does not support the IO method %d"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:3207
+#: sys/v4l2/gstv4l2object.c:3253
 #, c-format
 msgid "The driver of device '%s' does not support any known IO method."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4079
+#: sys/v4l2/gstv4l2object.c:4278
 msgid "Invalid caps"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4086 sys/v4l2/gstv4l2object.c:4110
+#: sys/v4l2/gstv4l2object.c:4285 sys/v4l2/gstv4l2object.c:4309
 #, c-format
 msgid "Device '%s' has no supported format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4092 sys/v4l2/gstv4l2object.c:4116
+#: sys/v4l2/gstv4l2object.c:4291 sys/v4l2/gstv4l2object.c:4315
 #, c-format
 msgid "Device '%s' failed during initialization"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4104
+#: sys/v4l2/gstv4l2object.c:4303
 #, c-format
 msgid "Device '%s' is busy"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4127
+#: sys/v4l2/gstv4l2object.c:4326
 #, c-format
 msgid "Device '%s' cannot capture at %dx%d"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4136
+#: sys/v4l2/gstv4l2object.c:4335
 #, c-format
 msgid "Device '%s' cannot capture in the specified format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4147
+#: sys/v4l2/gstv4l2object.c:4346
 #, c-format
 msgid "Device '%s' does support non-contiguous planes"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4162
+#: sys/v4l2/gstv4l2object.c:4361
 #, c-format
 msgid "Device '%s' does not support %s interlacing"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4176
+#: sys/v4l2/gstv4l2object.c:4375
 #, c-format
 msgid "Device '%s' does not support %s colorimetry"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4188
+#: sys/v4l2/gstv4l2object.c:4387
 #, c-format
 msgid "Could not get parameters on device '%s'"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4196
+#: sys/v4l2/gstv4l2object.c:4395
 msgid "Video device did not accept new frame rate setting."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4337
+#: sys/v4l2/gstv4l2object.c:4538
 msgid "Video device did not provide output format."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4343
+#: sys/v4l2/gstv4l2object.c:4544
 msgid "Video device returned invalid dimensions."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4351
+#: sys/v4l2/gstv4l2object.c:4552
 msgid "Video device uses an unsupported interlacing method."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4358
+#: sys/v4l2/gstv4l2object.c:4559
 msgid "Video device uses an unsupported pixel format."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:5256
+#: sys/v4l2/gstv4l2object.c:5504
 msgid "Failed to configure internal buffer pool."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:5262
+#: sys/v4l2/gstv4l2object.c:5510
 msgid "Video device did not suggest any buffer size."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:5268
+#: sys/v4l2/gstv4l2object.c:5516
 msgid "No downstream pool to import from."
 msgstr ""
 
@@ -340,8 +348,8 @@ msgstr ""
 msgid "Failed to allocated required memory."
 msgstr ""
 
-#: sys/v4l2/gstv4l2src.c:977 sys/v4l2/gstv4l2videodec.c:676
-#: sys/v4l2/gstv4l2videodec.c:992 sys/v4l2/gstv4l2videoenc.c:850
+#: sys/v4l2/gstv4l2src.c:957 sys/v4l2/gstv4l2videodec.c:524
+#: sys/v4l2/gstv4l2videodec.c:1062 sys/v4l2/gstv4l2videoenc.c:900
 msgid "Failed to allocate required memory."
 msgstr ""
 
@@ -355,35 +363,34 @@ msgstr ""
 msgid "Converter on device %s has no supported output format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videodec.c:145
+#: sys/v4l2/gstv4l2videodec.c:149
 #, c-format
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videodec.c:322
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
-#: sys/v4l2/gstv4l2videodec.c:1006
+#: sys/v4l2/gstv4l2videodec.c:1076
 msgid "Failed to start decoding thread."
 msgstr ""
 
-#: sys/v4l2/gstv4l2videodec.c:1013 sys/v4l2/gstv4l2videoenc.c:871
+#: sys/v4l2/gstv4l2videodec.c:1083 sys/v4l2/gstv4l2videoenc.c:921
 msgid "Failed to process frame."
 msgstr ""
 
-#: sys/v4l2/gstv4l2videoenc.c:142
+#: sys/v4l2/gstv4l2videoenc.c:151
 #, c-format
 msgid "Encoder on device %s has no supported output format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videoenc.c:149
+#: sys/v4l2/gstv4l2videoenc.c:158
 #, c-format
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videoenc.c:863
+#: sys/v4l2/gstv4l2videoenc.c:865
+msgid "Failed to force keyframe."
+msgstr ""
+
+#: sys/v4l2/gstv4l2videoenc.c:913
 msgid "Failed to start encoding thread."
 msgstr ""
 
@@ -399,112 +406,112 @@ msgstr ""
 msgid "Failed to query attributes of input %d in device %s"
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:187
+#: sys/v4l2/v4l2_calls.c:188
 #, c-format
 msgid "Failed to get setting of tuner %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:235
+#: sys/v4l2/v4l2_calls.c:236
 #, c-format
 msgid "Failed to query norm on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:417
+#: sys/v4l2/v4l2_calls.c:422
 #, c-format
 msgid "Failed getting controls attributes on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:609
+#: sys/v4l2/v4l2_calls.c:614
 #, c-format
 msgid "Cannot identify device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:616
+#: sys/v4l2/v4l2_calls.c:621
 #, c-format
 msgid "This isn't a device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:623
+#: sys/v4l2/v4l2_calls.c:628
 #, c-format
 msgid "Could not open device '%s' for reading and writing."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:630
+#: sys/v4l2/v4l2_calls.c:635
 #, c-format
 msgid "Device '%s' is not a capture device."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:637
+#: sys/v4l2/v4l2_calls.c:642
 #, c-format
 msgid "Device '%s' is not a output device."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:644
+#: sys/v4l2/v4l2_calls.c:649
 #, c-format
 msgid "Device '%s' is not a M2M device."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:696
+#: sys/v4l2/v4l2_calls.c:701
 #, c-format
 msgid "Could not dup device '%s' for reading and writing."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:780
+#: sys/v4l2/v4l2_calls.c:785
 #, c-format
 msgid "Failed to set norm for device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:818
+#: sys/v4l2/v4l2_calls.c:823
 #, c-format
 msgid "Failed to get current tuner frequency for device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:860
+#: sys/v4l2/v4l2_calls.c:865
 #, c-format
 msgid "Failed to set current tuner frequency for device '%s' to %lu Hz."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:894
+#: sys/v4l2/v4l2_calls.c:899
 #, c-format
 msgid "Failed to get signal strength for device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:930
+#: sys/v4l2/v4l2_calls.c:935
 #, c-format
 msgid "Failed to get value for control %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:965
+#: sys/v4l2/v4l2_calls.c:970
 #, c-format
 msgid "Failed to set value %d for control %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1025
+#: sys/v4l2/v4l2_calls.c:1030
 #, c-format
 msgid "Failed to set value %s for control %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1114
+#: sys/v4l2/v4l2_calls.c:1119
 #, c-format
 msgid "Failed to get current input on device '%s'. May be it is a radio device"
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1139
+#: sys/v4l2/v4l2_calls.c:1144
 #, c-format
 msgid "Failed to set input %u on device %s."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1186
+#: sys/v4l2/v4l2_calls.c:1191
 #, c-format
 msgid ""
 "Failed to get current output on device '%s'. May be it is a radio device"
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1211
+#: sys/v4l2/v4l2_calls.c:1216
 #, c-format
 msgid "Failed to set output %u on device %s."
 msgstr ""
 
-#: sys/ximage/gstximagesrc.c:856
+#: sys/ximage/gstximagesrc.c:873
 msgid "Cannot operate without a clock"
 msgstr ""
diff --git a/po/gst-plugins-good.pot b/po/gst-plugins-good.pot
index b2fc4a2abc81ac682306777843e23c057dcb20b5..7007684f330dbedda478a64cd7a440f1b7ebe1cf 100644
--- a/po/gst-plugins-good.pot
+++ b/po/gst-plugins-good.pot
@@ -6,9 +6,9 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.22.0\n"
+"Project-Id-Version: gst-plugins-good-1.24.12\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-23 19:32+0000\n"
+"POT-Creation-Date: 2025-01-29 20:13+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,18 +17,18 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ext/jack/gstjackaudiosink.c:359 ext/jack/gstjackaudiosrc.c:364
+#: ext/jack/gstjackaudiosink.c:360 ext/jack/gstjackaudiosrc.c:365
 msgid "Jack server not found"
 msgstr ""
 
-#: ext/jpeg/gstjpegdec.c:983 ext/jpeg/gstjpegdec.c:1255
-#: ext/jpeg/gstjpegdec.c:1264 ext/jpeg/gstjpegdec.c:1274
-#: ext/jpeg/gstjpegdec.c:1283 ext/jpeg/gstjpegdec.c:1567
-#: ext/jpeg/gstjpegdec.c:1595
+#: ext/jpeg/gstjpegdec.c:983 ext/jpeg/gstjpegdec.c:1260
+#: ext/jpeg/gstjpegdec.c:1269 ext/jpeg/gstjpegdec.c:1279
+#: ext/jpeg/gstjpegdec.c:1288 ext/jpeg/gstjpegdec.c:1580
+#: ext/jpeg/gstjpegdec.c:1608
 msgid "Failed to decode JPEG image"
 msgstr ""
 
-#: ext/jpeg/gstjpegdec.c:1555
+#: ext/jpeg/gstjpegdec.c:1562
 msgid "Failed to read memory"
 msgstr ""
 
@@ -54,36 +54,36 @@ msgstr ""
 msgid "'%s' by '%s'"
 msgstr ""
 
-#: ext/shout2/gstshout2.c:706 ext/shout2/gstshout2.c:716
+#: ext/shout2/gstshout2.c:717 ext/shout2/gstshout2.c:727
 msgid "Could not connect to server"
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1108
+#: ext/soup/gstsouphttpsrc.c:1145
 msgid "No URL set."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1623
+#: ext/soup/gstsouphttpsrc.c:1664
 msgid "Could not resolve server name."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1628
+#: ext/soup/gstsouphttpsrc.c:1669
 msgid "Could not establish connection to server."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1632
+#: ext/soup/gstsouphttpsrc.c:1673
 msgid "Secure connection setup failed."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1638
+#: ext/soup/gstsouphttpsrc.c:1679
 msgid ""
 "A network error occurred, or the server closed the connection unexpectedly."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1643
+#: ext/soup/gstsouphttpsrc.c:1684
 msgid "Server sent bad data."
 msgstr ""
 
-#: ext/soup/gstsouphttpsrc.c:1949
+#: ext/soup/gstsouphttpsrc.c:1984
 msgid "Server does not support seeking."
 msgstr ""
 
@@ -95,45 +95,53 @@ msgstr ""
 msgid "No or invalid input audio, AVI stream will be corrupt."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:505 gst/isomp4/qtdemux.c:510
+#: gst/isomp4/qtdemux.c:508 gst/isomp4/qtdemux.c:513
 msgid "This file contains no playable streams."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:556 gst/isomp4/qtdemux.c:7333 gst/isomp4/qtdemux.c:7402
-#: gst/isomp4/qtdemux.c:7708 gst/isomp4/qtdemux.c:9101
+#: gst/isomp4/qtdemux.c:559 gst/isomp4/qtdemux.c:8013 gst/isomp4/qtdemux.c:8082
+#: gst/isomp4/qtdemux.c:8398 gst/isomp4/qtdemux.c:9783
 msgid "This file is invalid and cannot be played."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:3039
+#: gst/isomp4/qtdemux.c:3071
 msgid "Cannot play stream because it is encrypted with PlayReady DRM."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:4356 gst/isomp4/qtdemux.c:8488
-#: gst/isomp4/qtdemux.c:8495 gst/isomp4/qtdemux.c:9803
-#: gst/isomp4/qtdemux.c:10245 gst/isomp4/qtdemux.c:10252
-#: gst/isomp4/qtdemux.c:13389
+#: gst/isomp4/qtdemux.c:4727 gst/isomp4/qtdemux.c:9175
+#: gst/isomp4/qtdemux.c:9182 gst/isomp4/qtdemux.c:10507
+#: gst/isomp4/qtdemux.c:10949 gst/isomp4/qtdemux.c:10956
+#: gst/isomp4/qtdemux.c:14275
 msgid "This file is corrupt and cannot be played."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:4598
+#: gst/isomp4/qtdemux.c:4969
 msgid "Invalid atom size."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:4677
+#: gst/isomp4/qtdemux.c:5022 gst/isomp4/qtdemux.c:5029
+msgid "Cannot query file size"
+msgstr ""
+
+#: gst/isomp4/qtdemux.c:5038
+msgid "Cannot demux file"
+msgstr ""
+
+#: gst/isomp4/qtdemux.c:5078
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
-#: gst/isomp4/qtdemux.c:11386
+#: gst/isomp4/qtdemux.c:12167
 msgid "The video in this file might not play correctly."
 msgstr ""
 
-#: gst/rtsp/gstrtspsrc.c:7885
+#: gst/rtsp/gstrtspsrc.c:8074
 msgid ""
 "No supported stream was found. You might need to install a GStreamer RTSP "
 "extension plugin for Real media streams."
 msgstr ""
 
-#: gst/rtsp/gstrtspsrc.c:7890
+#: gst/rtsp/gstrtspsrc.c:8079
 msgid ""
 "No supported stream was found. You might need to allow more transport "
 "protocols or may otherwise be missing the right GStreamer RTSP extension "
@@ -200,109 +208,109 @@ msgstr ""
 msgid "Raspberry Pi Camera Module"
 msgstr ""
 
-#: sys/v4l2/gstv4l2bufferpool.c:1930
+#: sys/v4l2/gstv4l2bufferpool.c:1870
 #, c-format
 msgid "Error reading %d bytes from device '%s'."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:1241
+#: sys/v4l2/gstv4l2object.c:1313
 #, c-format
 msgid "Failed to enumerate possible video formats device '%s' can work with"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:3192
+#: sys/v4l2/gstv4l2object.c:3238
 #, c-format
 msgid "Could not map buffers from device '%s'"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:3200
+#: sys/v4l2/gstv4l2object.c:3246
 #, c-format
 msgid "The driver of device '%s' does not support the IO method %d"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:3207
+#: sys/v4l2/gstv4l2object.c:3253
 #, c-format
 msgid "The driver of device '%s' does not support any known IO method."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4079
+#: sys/v4l2/gstv4l2object.c:4278
 msgid "Invalid caps"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4086 sys/v4l2/gstv4l2object.c:4110
+#: sys/v4l2/gstv4l2object.c:4285 sys/v4l2/gstv4l2object.c:4309
 #, c-format
 msgid "Device '%s' has no supported format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4092 sys/v4l2/gstv4l2object.c:4116
+#: sys/v4l2/gstv4l2object.c:4291 sys/v4l2/gstv4l2object.c:4315
 #, c-format
 msgid "Device '%s' failed during initialization"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4104
+#: sys/v4l2/gstv4l2object.c:4303
 #, c-format
 msgid "Device '%s' is busy"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4127
+#: sys/v4l2/gstv4l2object.c:4326
 #, c-format
 msgid "Device '%s' cannot capture at %dx%d"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4136
+#: sys/v4l2/gstv4l2object.c:4335
 #, c-format
 msgid "Device '%s' cannot capture in the specified format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4147
+#: sys/v4l2/gstv4l2object.c:4346
 #, c-format
 msgid "Device '%s' does support non-contiguous planes"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4162
+#: sys/v4l2/gstv4l2object.c:4361
 #, c-format
 msgid "Device '%s' does not support %s interlacing"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4176
+#: sys/v4l2/gstv4l2object.c:4375
 #, c-format
 msgid "Device '%s' does not support %s colorimetry"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4188
+#: sys/v4l2/gstv4l2object.c:4387
 #, c-format
 msgid "Could not get parameters on device '%s'"
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4196
+#: sys/v4l2/gstv4l2object.c:4395
 msgid "Video device did not accept new frame rate setting."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4337
+#: sys/v4l2/gstv4l2object.c:4538
 msgid "Video device did not provide output format."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4343
+#: sys/v4l2/gstv4l2object.c:4544
 msgid "Video device returned invalid dimensions."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4351
+#: sys/v4l2/gstv4l2object.c:4552
 msgid "Video device uses an unsupported interlacing method."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:4358
+#: sys/v4l2/gstv4l2object.c:4559
 msgid "Video device uses an unsupported pixel format."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:5256
+#: sys/v4l2/gstv4l2object.c:5504
 msgid "Failed to configure internal buffer pool."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:5262
+#: sys/v4l2/gstv4l2object.c:5510
 msgid "Video device did not suggest any buffer size."
 msgstr ""
 
-#: sys/v4l2/gstv4l2object.c:5268
+#: sys/v4l2/gstv4l2object.c:5516
 msgid "No downstream pool to import from."
 msgstr ""
 
@@ -340,8 +348,8 @@ msgstr ""
 msgid "Failed to allocated required memory."
 msgstr ""
 
-#: sys/v4l2/gstv4l2src.c:977 sys/v4l2/gstv4l2videodec.c:676
-#: sys/v4l2/gstv4l2videodec.c:992 sys/v4l2/gstv4l2videoenc.c:850
+#: sys/v4l2/gstv4l2src.c:957 sys/v4l2/gstv4l2videodec.c:524
+#: sys/v4l2/gstv4l2videodec.c:1062 sys/v4l2/gstv4l2videoenc.c:900
 msgid "Failed to allocate required memory."
 msgstr ""
 
@@ -355,35 +363,34 @@ msgstr ""
 msgid "Converter on device %s has no supported output format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videodec.c:145
+#: sys/v4l2/gstv4l2videodec.c:149
 #, c-format
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videodec.c:322
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
-#: sys/v4l2/gstv4l2videodec.c:1006
+#: sys/v4l2/gstv4l2videodec.c:1076
 msgid "Failed to start decoding thread."
 msgstr ""
 
-#: sys/v4l2/gstv4l2videodec.c:1013 sys/v4l2/gstv4l2videoenc.c:871
+#: sys/v4l2/gstv4l2videodec.c:1083 sys/v4l2/gstv4l2videoenc.c:921
 msgid "Failed to process frame."
 msgstr ""
 
-#: sys/v4l2/gstv4l2videoenc.c:142
+#: sys/v4l2/gstv4l2videoenc.c:151
 #, c-format
 msgid "Encoder on device %s has no supported output format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videoenc.c:149
+#: sys/v4l2/gstv4l2videoenc.c:158
 #, c-format
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
-#: sys/v4l2/gstv4l2videoenc.c:863
+#: sys/v4l2/gstv4l2videoenc.c:865
+msgid "Failed to force keyframe."
+msgstr ""
+
+#: sys/v4l2/gstv4l2videoenc.c:913
 msgid "Failed to start encoding thread."
 msgstr ""
 
@@ -399,112 +406,112 @@ msgstr ""
 msgid "Failed to query attributes of input %d in device %s"
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:187
+#: sys/v4l2/v4l2_calls.c:188
 #, c-format
 msgid "Failed to get setting of tuner %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:235
+#: sys/v4l2/v4l2_calls.c:236
 #, c-format
 msgid "Failed to query norm on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:417
+#: sys/v4l2/v4l2_calls.c:422
 #, c-format
 msgid "Failed getting controls attributes on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:609
+#: sys/v4l2/v4l2_calls.c:614
 #, c-format
 msgid "Cannot identify device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:616
+#: sys/v4l2/v4l2_calls.c:621
 #, c-format
 msgid "This isn't a device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:623
+#: sys/v4l2/v4l2_calls.c:628
 #, c-format
 msgid "Could not open device '%s' for reading and writing."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:630
+#: sys/v4l2/v4l2_calls.c:635
 #, c-format
 msgid "Device '%s' is not a capture device."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:637
+#: sys/v4l2/v4l2_calls.c:642
 #, c-format
 msgid "Device '%s' is not a output device."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:644
+#: sys/v4l2/v4l2_calls.c:649
 #, c-format
 msgid "Device '%s' is not a M2M device."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:696
+#: sys/v4l2/v4l2_calls.c:701
 #, c-format
 msgid "Could not dup device '%s' for reading and writing."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:780
+#: sys/v4l2/v4l2_calls.c:785
 #, c-format
 msgid "Failed to set norm for device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:818
+#: sys/v4l2/v4l2_calls.c:823
 #, c-format
 msgid "Failed to get current tuner frequency for device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:860
+#: sys/v4l2/v4l2_calls.c:865
 #, c-format
 msgid "Failed to set current tuner frequency for device '%s' to %lu Hz."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:894
+#: sys/v4l2/v4l2_calls.c:899
 #, c-format
 msgid "Failed to get signal strength for device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:930
+#: sys/v4l2/v4l2_calls.c:935
 #, c-format
 msgid "Failed to get value for control %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:965
+#: sys/v4l2/v4l2_calls.c:970
 #, c-format
 msgid "Failed to set value %d for control %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1025
+#: sys/v4l2/v4l2_calls.c:1030
 #, c-format
 msgid "Failed to set value %s for control %d on device '%s'."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1114
+#: sys/v4l2/v4l2_calls.c:1119
 #, c-format
 msgid "Failed to get current input on device '%s'. May be it is a radio device"
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1139
+#: sys/v4l2/v4l2_calls.c:1144
 #, c-format
 msgid "Failed to set input %u on device %s."
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1186
+#: sys/v4l2/v4l2_calls.c:1191
 #, c-format
 msgid ""
 "Failed to get current output on device '%s'. May be it is a radio device"
 msgstr ""
 
-#: sys/v4l2/v4l2_calls.c:1211
+#: sys/v4l2/v4l2_calls.c:1216
 #, c-format
 msgid "Failed to set output %u on device %s."
 msgstr ""
 
-#: sys/ximage/gstximagesrc.c:856
+#: sys/ximage/gstximagesrc.c:873
 msgid "Cannot operate without a clock"
 msgstr ""
diff --git a/po/hr.po b/po/hr.po
index aad07bb96d0b8a14f48fc0f8c91d89044994c5ce..246aa3d842e9df46574265c222c8edb40eaaa6f4 100644
--- a/po/hr.po
+++ b/po/hr.po
@@ -1,16 +1,16 @@
-# Translation of gst-plugins-good messages to Croatian.
+# Transltion of gst-plugins-good messages to Croatian.
 # This file is put in the public domain.
 # Copyright (C) 2004-2010, 2019 GStreamer core team.
 # This file is distributed under the same license as the gst-plugins-good package.
 #
 # Tomislav Krznar <tomislav.krznar@gmail.com>, 2012.
-# Božidar Putanec <bozidarp@yahoo.com>, 2016, 2017, 2018, 2019, 2021, 2022.
+# Božidar Putanec <bozidarp@yahoo.com>, 2016, 2017, 2018, 2019, 2021, 2022, 2004.
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.21.90\n"
+"Project-Id-Version: gst-plugins-good-1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-23 16:27+0000\n"
-"PO-Revision-Date: 2023-01-15 19:31-0800\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-08 10:45-0800\n"
 "Last-Translator: Božidar Putanec <bozidarp@yahoo.com>\n"
 "Language-Team: Croatian <lokalizacija@linux.hr>\n"
 "Language: hr\n"
@@ -101,6 +101,12 @@ msgstr "Datoteka je oštećena i ne može se reproducirati."
 msgid "Invalid atom size."
 msgstr "Veličina atoma nije valjana."
 
+msgid "Cannot query file size"
+msgstr "Ne može zatražiti (dobiti) veličinu datoteke"
+
+msgid "Cannot demux file"
+msgstr "Ne može demultipleksirati datoteku"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Datoteka je nepotpuna i ne može se reproducirati."
 
@@ -307,10 +313,6 @@ msgstr "Pretvarač na uređaju %s nema podržani izlazni format"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Dekoder na uređaju %s nema podržani ulazni format"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Dekoder na uređaju %s nema podržani izlazni format"
-
 msgid "Failed to start decoding thread."
 msgstr "Nije uspjelo započeti dekodiranje dretve."
 
@@ -325,6 +327,9 @@ msgstr "Koder na uređaju %s nema podržani izlazni format"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Koder na uređaju %s nema podržani ulazni format"
 
+msgid "Failed to force keyframe."
+msgstr "Nije uspjelo nametnuti ključni okvir"
+
 msgid "Failed to start encoding thread."
 msgstr "Nije uspjelo započeti kodiranje dretve."
 
@@ -432,6 +437,10 @@ msgstr "Nije uspjelo postaviti izlaz %u na uređaju %s."
 msgid "Cannot operate without a clock"
 msgstr "Nije moguće raditi bez sata (clock)"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Dekoder na uređaju %s nema podržani izlazni format"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Promjena rezolucije u tijeku rada (runtime) još nije podržana."
 
@@ -843,4 +852,4 @@ msgstr "Nije moguće raditi bez sata (clock)"
 #~ msgstr "Nisam uspio pokušavajući dobiti video okvire iz uređaja „%s”."
 
 #~ msgid "Failed after %d tries. device %s. system error: %s"
-#~ msgstr "Nisam uspio nakon %d pokušaja. uređaj %s. greška sustava: %s"
+#~ msgstr "ggggggNisam uspio nakon %d pokušaja. uređaj %s. greška sustava: %s"
diff --git a/po/hu.po b/po/hu.po
index d784ff8a155c3d3309a2ec76d1980f245fd578a5..61f32fe7100e0e76d4fd501cd4c3b3d4a84af635 100644
--- a/po/hu.po
+++ b/po/hu.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.19.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2022-10-18 21:22+0200\n"
 "Last-Translator: Balázs Úr <ur.balazs@fsf.hu>\n"
 "Language-Team: Hungarian <translation-team-hu@lists.sourceforge.net>\n"
@@ -105,6 +105,12 @@ msgstr "A fájl sérült és nem játszható le."
 msgid "Invalid atom size."
 msgstr "Érvénytelen atom méret."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "A fájl nem teljes és nem játszható le."
 
@@ -309,10 +315,6 @@ msgstr "A(z) %s eszközön lévő átalakítónak nincs támogatott kimeneti for
 msgid "Decoder on device %s has no supported input format"
 msgstr "A(z) %s eszközön lévő dekódolónak nincs támogatott bemeneti formátuma"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "A(z) %s eszközön lévő dekódolónak nincs támogatott kimeneti formátuma"
-
 msgid "Failed to start decoding thread."
 msgstr "Nem sikerült elindítani a dekódolási szálat."
 
@@ -327,6 +329,10 @@ msgstr "A(z) %s eszközön lévő kódolónak nincs támogatott kimeneti formát
 msgid "Encoder on device %s has no supported input format"
 msgstr "A(z) %s eszközön lévő kódolónak nincs támogatott bemeneti formátuma"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Nem sikerült feldolgozni a keretet."
+
 msgid "Failed to start encoding thread."
 msgstr "Nem sikerült elindítani a kódolási szálat."
 
@@ -441,5 +447,10 @@ msgstr "A(z) %u. kimenet beállítása meghiúsult a(z) „%s” eszközön."
 msgid "Cannot operate without a clock"
 msgstr "Óra nélkül lehetetlen a működés"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "A(z) %s eszközön lévő dekódolónak nincs támogatott kimeneti formátuma"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "A felbontás módosítása futás közben még nem támogatott."
diff --git a/po/id.po b/po/id.po
index f315bf78c9ca2b351431fe40dc30b755fd07e9ca..dd479b016d9eb02fcb362d2e4daa1dfcbf7ac161 100644
--- a/po/id.po
+++ b/po/id.po
@@ -2,23 +2,23 @@
 # This file is put in the public domain.
 #
 # Andhika Padmawan <andhika.padmawan@gmail.com>, 2011-2016.
-# Andika Triwidada <andika@gmail.com>, 2013, 2021.
+# Andika Triwidada <andika@gmail.com>, 2013, 2021, 2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-09-24 21:55+0700\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-07 20:46+0700\n"
 "Last-Translator: Andika Triwidada <andika@gmail.com>\n"
 "Language-Team: Indonesian <translation-team-id@lists.sourceforge.net>\n"
 "Language: id\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Bugs: Report translation errors to the Language-Team address.\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 2.4.2\n"
+"X-Bugs: Report translation errors to the Language-Team address.\n"
+"X-Generator: Poedit 3.4.2\n"
 
 msgid "Jack server not found"
 msgstr "Server Jack tak ditemukan"
@@ -100,6 +100,12 @@ msgstr "Berkas ini rusak dan tak dapat diputar."
 msgid "Invalid atom size."
 msgstr "Ukuran atom tidak sah."
 
+msgid "Cannot query file size"
+msgstr "Tidak bisa meng-kuiri ukuran berkas"
+
+msgid "Cannot demux file"
+msgstr "Tidak bisa men-demux berkas"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Berkas ini tidak lengkap dan tak dapat diputar."
 
@@ -305,10 +311,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr "Dekoder pada peranti %s tidak memiliki format masukan yang didukung"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Dekoder pada peranti %s tidak mempunyai format keluaran yang didukung"
-
 msgid "Failed to start decoding thread."
 msgstr "Gagal untuk menjalankan tali awasandi."
 
@@ -323,6 +325,9 @@ msgstr "Penyandi di peranti %s tidak mempunyai format keluaran yang didukung"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Penyandi di peranti %s tidak mempunyai format masukan yang didukung"
 
+msgid "Failed to force keyframe."
+msgstr "Gagal memaksa bingkai kunci."
+
 msgid "Failed to start encoding thread."
 msgstr "Gagal memulai pengodean thread."
 
@@ -427,6 +432,3 @@ msgstr "Gagal mengatur keluaran %u di peranti %s."
 
 msgid "Cannot operate without a clock"
 msgstr "Tak dapat beroperasi tanpa jam"
-
-#~ msgid "Changing resolution at runtime is not yet supported."
-#~ msgstr "Mengubah resolusi saat waktu berjalan belum didukung."
diff --git a/po/it.po b/po/it.po
index 1f98b86023cc56a4898a9e13c6aef1523dd19f2d..90552ff10836976bab143d8d0d2c3af66d9fae38 100644
--- a/po/it.po
+++ b/po/it.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-1.15.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2019-01-25 14:26+0100\n"
 "Last-Translator: Milo Casagrande <milo@milo.name>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
@@ -105,6 +105,12 @@ msgstr "Questo file è danneggiato e non può essere riprodotto."
 msgid "Invalid atom size."
 msgstr "Dimensione atomo non valida."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Questo file è incompleto e non può essere riprodotto."
 
@@ -318,11 +324,6 @@ msgid "Decoder on device %s has no supported input format"
 msgstr ""
 "Il decodificatore sul dispositivo %s non ha un formato d'ingresso supportato"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"Il decodificatore sul dispositivo %s non ha un formato di uscita supportato"
-
 msgid "Failed to start decoding thread."
 msgstr "Avvio del thread di decodifica non riuscito."
 
@@ -339,6 +340,10 @@ msgid "Encoder on device %s has no supported input format"
 msgstr ""
 "Il codificatore sul dispositivo %s non ha un formato d'ingresso supportato"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Elaborazione del fotogramma non riuscita."
+
 msgid "Failed to start encoding thread."
 msgstr "Avvio del thread di codifica non riuscito."
 
@@ -468,6 +473,12 @@ msgstr "Impostazione dell'uscita %d sul dispositivo «%s» non riuscita."
 msgid "Cannot operate without a clock"
 msgstr "Impossibile operare senza un clock"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "Il decodificatore sul dispositivo %s non ha un formato di uscita "
+#~ "supportato"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr ""
 #~ "Il cambio della risoluzione durante l'esecuzione non è ancora supportato."
diff --git a/po/ja.po b/po/ja.po
index dc959fff3558497a6e5fa05f61f5ed33913ca95d..e264ec3160c98b7ab71b30a186f1ec702b08d68e 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.0.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2012-12-22 20:13+0900\n"
 "Last-Translator: Takeshi Hamasaki <hmatrjp@users.sourceforge.jp>\n"
 "Language-Team: Japanese <translation-team-ja@lists.sourceforge.net>\n"
@@ -97,6 +97,12 @@ msgstr "このファイルは壊れているため再生することができま
 msgid "Invalid atom size."
 msgstr "不正なアトムサイズ"
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "このファイルは正常ではないため、再生することができません"
 
@@ -310,10 +316,6 @@ msgstr "デバイス '%s' のドライバはIOメソッド%dをサポートし
 msgid "Decoder on device %s has no supported input format"
 msgstr "デバイス '%s' のドライバはIOメソッド%dをサポートしていません"
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "デバイス '%s' のドライバはIOメソッド%dをサポートしていません"
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "JPEG画像のデコードに失敗しました"
@@ -329,6 +331,9 @@ msgstr "デバイス '%s' のドライバはIOメソッド%dをサポートし
 msgid "Encoder on device %s has no supported input format"
 msgstr "デバイス '%s' のドライバはIOメソッド%dをサポートしていません"
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/ka.po b/po/ka.po
index 8ed0cbfc572154f420570223cddd1430feb399b2..6e5f5c7fbeb82d83be3958234f99e3fb5f69e8ba 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-1.19.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2022-10-15 15:03+0200\n"
 "Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
 "Language-Team: Georgian <(nothing)>\n"
@@ -98,6 +98,12 @@ msgstr "ფაილი დაზიანებულია და მისი
 msgid "Invalid atom size."
 msgstr "ატომის არასწორი ზომა."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "ფაილი დაუსრულებელია და მისი დაკვრა შეუძლებელია."
 
@@ -304,10 +310,6 @@ msgstr "გადამყვანს მოწყობილობაზე %
 msgid "Decoder on device %s has no supported input format"
 msgstr "დეკოდერს მოწყობილობაზე %s მხარდაჭერილი შეყვანის ფორმატი არ გააჩნია"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "დეკოდერს მოწყობილობაზე %s მხარდაჭერილი გამოტანის ფორმატი არ გააჩნია"
-
 msgid "Failed to start decoding thread."
 msgstr "დეკოდერის ნაკადის გაშვების შეცდომა."
 
@@ -322,6 +324,10 @@ msgstr "ენკოდერს მოწყობილობაზე %s მ
 msgid "Encoder on device %s has no supported input format"
 msgstr "ენკოდერს მოწყობილობაზე %s მხარდაჭერილი შეყვანის ფორმატი არ გააჩნია"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "კადრის დამუშავების შეცდომა."
+
 msgid "Failed to start encoding thread."
 msgstr "ენკოდერის ნაკადის გაშვების შეცდომა."
 
@@ -425,5 +431,9 @@ msgstr "გამოტანის %u მოწყობილობაზე %
 msgid "Cannot operate without a clock"
 msgstr "საათის გარეშე მუშაობა შეუძლებელია"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "დეკოდერს მოწყობილობაზე %s მხარდაჭერილი გამოტანის ფორმატი არ გააჩნია"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "გაშვებულ მდგომარეობაში გაფართოების შეცვლა ჯერ მხარდაჭერილი არაა."
diff --git a/po/ky.po b/po/ky.po
index 26d9e482d1f721a6f4cf0a84083689da00db96bd..2d135821dd5f43503ccfa2735c8d22023635005d 100644
--- a/po/ky.po
+++ b/po/ky.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 0.10.23\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2007-11-13 17:16+0600\n"
 "Last-Translator: Ilyas Bakirov <just_ilyas@yahoo.com>\n"
 "Language-Team: Kirghiz <i18n-team-ky-kyrgyz@lists.sourceforge.net>\n"
@@ -91,6 +91,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -283,10 +289,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -301,6 +303,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/lt.po b/po/lt.po
index d9c8aebcaa14bf603f9bb8dd39308bd9b44ba26a..d01d5082ff38c38918703df0b81102b01ca30a37 100644
--- a/po/lt.po
+++ b/po/lt.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 0.10.23.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2010-07-16 19:34+0300\n"
 "Last-Translator: Žygimantas Beručka <uid0@akl.lt>\n"
 "Language-Team: Lithuanian <komp_lt@konferencijos.lt>\n"
@@ -97,6 +97,12 @@ msgstr "Failas sugadintas ir negali būti atkurtas."
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Failas nebaigtas ir negali būti atkurtas."
 
@@ -301,10 +307,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr "Įrenginio „%s“ tvarkyklė nepalaiko jokių žinomų įrašymo būdų."
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Įrenginio „%s“ tvarkyklė nepalaiko jokių žinomų įrašymo būdų."
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "Nepavyko dekoduoti JPEG paveikslÄ—lio"
@@ -320,6 +322,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/lv.po b/po/lv.po
index fcd6b06b7a8d8dbda495ef6402288bb350cb60e1..820fbd07f2a279b80b1eb67239b110fa2f827c53 100644
--- a/po/lv.po
+++ b/po/lv.po
@@ -5,36 +5,38 @@
 # Rihards Prieditis <rprieditis@gmail.com>, 2010.
 # Rihards Prieditis <rprieditis@gmail.com>, 2011.
 # Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>, 2014.
+# Rihards Prieditis <rprieditis@gmail.com>, 2023.
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.2.1\n"
+"Project-Id-Version: gst-plugins-good 1.21.90\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2014-04-20 16:38+0300\n"
-"Last-Translator: Rihards Prieditis <rprieditis@gmail.com>\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2023-12-24 18:57+0000\n"
+"Last-Translator: Rihards Priedītis <rprieditis@gmail.com>\n"
 "Language-Team: Latvian <translation-team-lv@lists.sourceforge.net>\n"
 "Language: lv\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==0 || (n%100>=11 && n%100<=19) ? 0 : "
+"n%10==1 && n%100!=11 ? 1 : 2);\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : "
-"2);\n"
-"X-Generator: Lokalize 1.5\n"
+"X-Generator: Poedit 3.4.2\n"
 
 msgid "Jack server not found"
-msgstr "Nav atrasts Jack serveris"
+msgstr "Jack serveris nav atrasts"
 
 msgid "Failed to decode JPEG image"
 msgstr "Neizdevās atkodēt JPEG attēlu"
 
-#, fuzzy
 msgid "Failed to read memory"
-msgstr "Neizdevās atkodēt JPEG attēlu"
+msgstr "Neizdevās nolasīt atmiņu"
 
 msgid ""
 "Failed to configure LAME mp3 audio encoder. Check your encoding parameters."
 msgstr ""
+"Neizdevās konfigurēt LAME mp3 audio kodētāju. Pārbaudiet kodēšanas "
+"parametrus."
 
 #. <php-emulation-mode>three underscores for ___rate is really really really
 #. * private as opposed to one underscore<php-emulation-mode>
@@ -45,14 +47,16 @@ msgid ""
 "The requested bitrate %d kbit/s for property '%s' is not allowed. The "
 "bitrate was changed to %d kbit/s."
 msgstr ""
+"Pieprasītais bitrate %d kbit/s īpašībai \"%s\" nav atļauts. Bitu pārraides "
+"ātrums tika mainīts uz %d kbit/s."
 
 #. TRANSLATORS: 'song title' by 'artist name'
 #, c-format
 msgid "'%s' by '%s'"
-msgstr "“%s” izpilda “%s”"
+msgstr "'%s' pēc '%s'"
 
 msgid "Could not connect to server"
-msgstr "Nevarēja savienoties ar serveri"
+msgstr "Nevar izveidot savienojumu ar serveri"
 
 msgid "No URL set."
 msgstr "URL nav iestatīts."
@@ -66,85 +70,91 @@ msgstr "Nevar izveidot savienojumu ar serveri."
 msgid "Secure connection setup failed."
 msgstr "Neizdevās izveidot drošo savienojumu."
 
-#, fuzzy
 msgid ""
 "A network error occurred, or the server closed the connection unexpectedly."
-msgstr "Notika tīkla kļūda, vai serveris negaidīti aizvēra savienojumu."
+msgstr "Notika tīkla kļūda vai serveris negaidīti slēdza savienojumu."
 
 msgid "Server sent bad data."
-msgstr "Serveris nosūtīja sliktus datus."
+msgstr "Serveris nosūtīja nepareizus datus."
 
 msgid "Server does not support seeking."
 msgstr "Serveris neatbalsta meklēšanu."
 
 msgid "Failed to configure TwoLAME encoder. Check your encoding parameters."
 msgstr ""
+"Neizdevās konfigurēt TwoLAME kodētāju. Pārbaudiet kodēšanas parametrus."
 
 msgid "No or invalid input audio, AVI stream will be corrupt."
-msgstr "Nav vai nederīgs ievades audio, AVI straume tiks bojāta."
+msgstr "Nav ievades audio vai tas ir nederīgs, AVI plūsma būs bojāta."
 
 msgid "This file contains no playable streams."
-msgstr "Šī datne nesatur nevienu atskaņojamu straumi."
+msgstr "Šajā failā nav atskaņojamu plūsmu."
 
 msgid "This file is invalid and cannot be played."
-msgstr "Šī datne ir nederīga un to nevar atskaņot."
+msgstr "Šis fails ir nederīgs, un to nevar atskaņot."
 
 msgid "Cannot play stream because it is encrypted with PlayReady DRM."
 msgstr "Nevar atskaņot straumi, jo tā ir šifrēta ar PlayReady DRM."
 
 msgid "This file is corrupt and cannot be played."
-msgstr "Šī datne ir bojāta un to nevar atskaņot."
+msgstr "Šis fails ir bojāts, un to nevar atskaņot."
 
 msgid "Invalid atom size."
-msgstr "Nederīgs atoma izmērs."
+msgstr "Nederīgs atoma lielums."
+
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
 
 msgid "This file is incomplete and cannot be played."
-msgstr "Šī datne ir nepabeigta un to nevar atskaņot."
+msgstr "Šis fails ir nepilnīgs, un to nevar atskaņot."
 
 msgid "The video in this file might not play correctly."
-msgstr "Video šajā datnē varētu tikt atskaņots nepareizi."
+msgstr "Šajā failā esošais video var netikt atskaņots pareizi."
 
 msgid ""
 "No supported stream was found. You might need to install a GStreamer RTSP "
 "extension plugin for Real media streams."
 msgstr ""
-"Netika atrasta atbalstīta straume. Iespējams, jums jāinstalē GStreamer RTSP "
-"paplašinājuma spraudni Real media straumēm."
+"Netika atrasta atbalstīta plūsma. Iespējams, ir nepieciešams instalēt "
+"GStreamer RTSP paplašinājuma spraudni Real multivides plūsmām."
 
 msgid ""
 "No supported stream was found. You might need to allow more transport "
 "protocols or may otherwise be missing the right GStreamer RTSP extension "
 "plugin."
 msgstr ""
-"Netika atrasta atbalstīta straume. Iespējams, jums jāatļauj vairāk "
-"transporta protokoli vai arī trūkst GStreamer RTSP paplašinājuma spraudnis. "
+"Netika atrasta atbalstīta plūsma. Iespējams, ir nepieciešams atļaut vairāk "
+"transporta protokolu vai arī trūkst pareizā GStreamer RTSP paplašinājuma "
+"spraudņa."
 
 msgid ""
 "Could not open audio device for playback. Device is being used by another "
 "application."
 msgstr ""
-"Nevarēja atvērt audio ierīci atskaņošanai. Ierīci pašlaik izmanto cita "
-"lietotne."
+"Nevar atvērt audio ierīci atskaņošanai. Ierīci izmanto cita "
+"lietojumprogramma."
 
 msgid ""
 "Could not open audio device for playback. You don't have permission to open "
 "the device."
 msgstr ""
-"Nevar atvērt audio ierīci atskaņošanai. Jums nav nepieciešamās atļaujas, lai "
-"atvērtu ierīci."
+"Nevar atvērt audio ierīci atskaņošanai. Jums nav atļaujas atvērt ierīci."
 
 msgid "Could not open audio device for playback."
-msgstr "Nevarēja atvērt audio ierīci atskaņošanai."
+msgstr "Nevar atvērt audio ierīci atskaņošanai."
 
 msgid ""
 "Could not open audio device for playback. This version of the Open Sound "
 "System is not supported by this element."
 msgstr ""
-"Nevar atvērt audio ierīci atskaņošanai. Šī Atvērtās Skaņas Sistēmas versija "
-"neatbalsta šo elementu."
+"Nevar atvērt audio ierīci atskaņošanai. Šis elements neatbalsta šo Open "
+"Sound System versiju."
 
 msgid "Playback is not supported by this audio device."
-msgstr "Šī audio ierīce neatbalsta atskaņošanu."
+msgstr "Šī audioierīce neatbalsta atskaņošanu."
 
 msgid "Audio playback error."
 msgstr "Audio atskaņošanas kļūda."
@@ -153,694 +163,277 @@ msgid "Recording is not supported by this audio device."
 msgstr "Šī audio ierīce neatbalsta ierakstīšanu."
 
 msgid "Error recording from audio device."
-msgstr "Kļūda, ierakstot no audio ierīces."
+msgstr "Kļūda ierakstīšanā no audio ierīces."
 
 msgid ""
 "Could not open audio device for recording. You don't have permission to open "
 "the device."
 msgstr ""
-"Nevar atvērt audio ierīci ierakstīšanai. Jums nav nepieciešamo piekļuves "
-"tiesību, lai atvērtu ierīci."
+"Nevar atvērt audio ierīci ierakstīšanai. Jums nav atļaujas atvērt ierīci."
 
 msgid "Could not open audio device for recording."
-msgstr "Nevarēja atvērt audio ierīci ierakstīšanai."
+msgstr "Nevar atvērt audio ierīci ierakstīšanai."
 
 msgid "CoreAudio device not found"
-msgstr ""
+msgstr "CoreAudio ierīce nav atrasta"
 
-#, fuzzy
 msgid "CoreAudio device could not be opened"
-msgstr "Video ierīce nevarēja izveidot bufera pūlu."
+msgstr "CoreAudio ierīci nav iespējams atvērt"
 
 msgid "Raspberry Pi Camera Module"
-msgstr ""
+msgstr "Raspberry Pi kameras modulis"
 
 #, c-format
 msgid "Error reading %d bytes from device '%s'."
-msgstr "Radās kļūda, nolasot %d baitus no ierīces “%s”."
+msgstr "Kļūda nolasot %d baitus no ierīces '%s'."
 
 #, c-format
 msgid "Failed to enumerate possible video formats device '%s' can work with"
-msgstr "Neizdevās uzskaitīt iespējamos video formātus, ar ko var strādāt “%s”"
+msgstr ""
+"Neizdevās uzskaitīt iespējamos video formātus, ar kuriem var strādāt ierīce "
+"'%s'"
 
 #, c-format
 msgid "Could not map buffers from device '%s'"
-msgstr "Nevar izvietot buferus no ierīces “%s”"
+msgstr "Nevar kartēt buferus no ierīces '%s'"
 
 #, c-format
 msgid "The driver of device '%s' does not support the IO method %d"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Ierīces '%s' draiveris neatbalsta IO metodi %d"
 
 #, c-format
 msgid "The driver of device '%s' does not support any known IO method."
-msgstr "Ierīces “%s” draiveris neatbalsta nevienu zināmo ierakstīšanas metodi."
+msgstr "Ierīces \"%s\" draiveris neatbalsta nevienu zināmu IO metodi."
 
-#, fuzzy
 msgid "Invalid caps"
-msgstr "Nederīgs atoma izmērs."
+msgstr "Nederīgi vāciņi"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' has no supported format"
-msgstr "Ierīce “%s” neatbalsta video tveršanu"
+msgstr "Ierīcei '%s' nav atbalstīta formāta"
 
 #, c-format
 msgid "Device '%s' failed during initialization"
-msgstr ""
+msgstr "Ierīce '%s' inicializācijas laikā notika kļūda"
 
 #, c-format
 msgid "Device '%s' is busy"
-msgstr "Ierīce “%s” ir aizņemta"
+msgstr "Ierīce '%s' ir aizņemta"
 
 #, c-format
 msgid "Device '%s' cannot capture at %dx%d"
-msgstr "Ierīce “%s” nevar tvert ar %dx%d"
+msgstr "Ierīce '%s' nevar tvert pie %dx%d"
 
 #, c-format
 msgid "Device '%s' cannot capture in the specified format"
-msgstr "Ierīce “%s” nevar tvert norādītajā formātā"
+msgstr "Ierīce '%s' nevar tvert norādītajā formātā"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does support non-contiguous planes"
-msgstr "Ierīce “%s” neatbalsta video tveršanu"
+msgstr "Ierīce '%s' atbalsta nesaistītas plaknes"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s interlacing"
-msgstr "Ierīce “%s” neatbalsta video tveršanu"
+msgstr "Ierīce '%s' neatbalsta %s pārklāšanu"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s colorimetry"
-msgstr "Ierīce “%s” neatbalsta video tveršanu"
+msgstr "Ierīce '%s' neatbalsta %s kolorimetriju"
 
 #, c-format
 msgid "Could not get parameters on device '%s'"
-msgstr "Nevar nolasīt parametrus no ierīces “%s”"
+msgstr "Nevar iegūt parametrus no ierīces '%s'"
 
 msgid "Video device did not accept new frame rate setting."
-msgstr "Video ierīce nepieņēma jaunos kadru ātruma iestatījumus."
+msgstr "Video ierīce nepieņēma jauno kadru ātruma iestatījumu."
 
-#, fuzzy
 msgid "Video device did not provide output format."
-msgstr "Video ierīce nepieņēma jaunos kadru ātruma iestatījumus."
+msgstr "Video ierīce nenodrošināja izvada formātu."
 
 msgid "Video device returned invalid dimensions."
-msgstr ""
+msgstr "Video ierīce atgrieza nederīgus izmērus."
 
-#, fuzzy
 msgid "Video device uses an unsupported interlacing method."
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Video ierīce izmanto neatbalstītu pārklāšanas metodi."
 
 msgid "Video device uses an unsupported pixel format."
-msgstr ""
+msgstr "Video ierīce izmanto neatbalstīts pikseļu formāts."
 
-#, fuzzy
 msgid "Failed to configure internal buffer pool."
-msgstr "Video ierīce nevarēja izveidot bufera pūlu."
+msgstr "Neizdevās konfigurēt iekšējo bufera baseinu."
 
-#, fuzzy
 msgid "Video device did not suggest any buffer size."
-msgstr "Video ierīce nevarēja izveidot bufera pūlu."
+msgstr "Video ierīce neieteica nekādu bufera lielumu."
 
 msgid "No downstream pool to import from."
-msgstr ""
+msgstr "Nav lejupstraumes baseina, no kura importēt."
 
 #, c-format
 msgid "Failed to get settings of tuner %d on device '%s'."
-msgstr "Neizdevās saņemt skaņotāja %d iestatījumus no ierīces “%s”."
+msgstr "Neizdevās iegūt uztvērēja %d iestatījumus ierīcē '%s'."
 
 #, c-format
 msgid "Error getting capabilities for device '%s'."
-msgstr "Kļūda, saņemot iespējas no ierīces “%s”."
+msgstr "Kļūda, iegūstot ierīces '%s' iespējas."
 
 #, c-format
 msgid "Device '%s' is not a tuner."
-msgstr "Ierīce “%s” nav skaņotājs."
+msgstr "Ierīce '%s' nav uztvērējs."
 
 #, c-format
 msgid "Failed to get radio input on device '%s'. "
-msgstr "Neizdevās saņemt radio ievadi uz ierīces “%s”."
+msgstr "Ierīcē '%s' neizdevās iegūt radio ievadi. "
 
 #, c-format
 msgid "Failed to set input %d on device %s."
-msgstr "Neizdevās iestatīt ievadi %d uz ierīces %s."
+msgstr "Neizdevās iestatīt ievadi %d ierīcē %s."
 
 #, c-format
 msgid "Failed to change mute state for device '%s'."
-msgstr "Neizdevās mainīt apklusināšanas stāvokli ierīcei “%s”."
+msgstr "Ierīcei '%s' neizdevās mainīt neaktīvo stāvokli."
 
 msgid "Failed to allocated required memory."
-msgstr ""
+msgstr "Neizdevās piešķirt nepieciešamo atmiņu."
 
 msgid "Failed to allocate required memory."
-msgstr ""
+msgstr "Neizdevās piešķirt nepieciešamo atmiņu."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Converter on device %s has no supported input format"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Ierīces %s pārveidotājam nav atbalstītu ieejas formātu"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Converter on device %s has no supported output format"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Ierīces %s pārveidotājam nav atbalstītu izvada formātu"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Decoder on device %s has no supported input format"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Ierīces %s dekodētājam nav atbalstītu ieejas formātu"
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
-
-#, fuzzy
 msgid "Failed to start decoding thread."
-msgstr "Neizdevās atkodēt JPEG attēlu"
+msgstr "Neizdevās sākt dekodēšanas pavedienu."
 
 msgid "Failed to process frame."
-msgstr ""
+msgstr "Neizdevās apstrādāt kadru."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Encoder on device %s has no supported output format"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Ierīces %s kodētājam nav atbalstītu izvada formātu"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Encoder on device %s has no supported input format"
-msgstr "Ierīces \"%s\" draiveris neatbalsta IO metodi %d"
+msgstr "Ierīces %s kodētājam nav atbalstītu ieejas formātu"
+
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Neizdevās apstrādāt kadru."
 
 msgid "Failed to start encoding thread."
-msgstr ""
+msgstr "Neizdevās sākt kodēšanas pavedienu."
 
 #, c-format
 msgid ""
 "Error getting capabilities for device '%s': It isn't a v4l2 driver. Check if "
 "it is a v4l1 driver."
 msgstr ""
-"Radās kļūda, saņemot ierīces “%s” iespējas: iespējams tas nav v4I2 "
-"draiveris. Pārbaudiet vai tas ir v4I1 draiveris."
+"Kļūda, iegūstot ierīces '%s' iespējas: Tas nav v4l2 draiveris. Pārbaudiet, "
+"vai tas ir v4l1 draiveris."
 
 #, c-format
 msgid "Failed to query attributes of input %d in device %s"
-msgstr "Neizdevās noteikt atribūtus ievadei %d no ierīces %s"
+msgstr "Neizdevās pieprasīt ievades atribūtus %d no ierīces %s"
 
 #, c-format
 msgid "Failed to get setting of tuner %d on device '%s'."
-msgstr "Neizdevās saņemt skaņotāja %d iestatījumus no ierīces “%s”."
+msgstr "Neizdevās iegūt uztvērēja %d iestatījumu ierīcē '%s'."
 
 #, c-format
 msgid "Failed to query norm on device '%s'."
-msgstr "Neizdevās noskaidrot normu ierīcei “%s”."
+msgstr "Neizdevās pieprasīt normu ierīcē '%s'."
 
 #, c-format
 msgid "Failed getting controls attributes on device '%s'."
-msgstr "Neizdevās iegūt kontroles atribūtus ierīcei “%s”."
+msgstr "Neizdevās iegūt vadības atribūtus ierīcē '%s'."
 
 #, c-format
 msgid "Cannot identify device '%s'."
-msgstr "Nevar atpazīt ierīci “%s”."
+msgstr "Nevar identificēt ierīci '%s'."
 
 #, c-format
 msgid "This isn't a device '%s'."
-msgstr "Šī nav ierīce “%s”."
+msgstr "Šī nav ierīce '%s'."
 
 #, c-format
 msgid "Could not open device '%s' for reading and writing."
-msgstr "Nevarēja atvērt ierīci “%s” lasīšanai vai rakstīšanai."
+msgstr "Nevar atvērt ierīci '%s' lasīšanai un rakstīšanai."
 
 #, c-format
 msgid "Device '%s' is not a capture device."
-msgstr "Ierīce “%s” nav ierakstīšanas ierīce."
+msgstr "Ierīce '%s' nav uztveršanas ierīce."
 
 #, c-format
 msgid "Device '%s' is not a output device."
-msgstr "Ierīce “%s” nav izvades ierīce."
+msgstr "Ierīce '%s' nav izvada ierīce."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' is not a M2M device."
-msgstr "Ierīce “%s” nav izvades ierīce."
+msgstr "Ierīce '%s' nav M2M ierīce."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Could not dup device '%s' for reading and writing."
-msgstr "Nevarēja atvērt ierīci “%s” lasīšanai vai rakstīšanai."
+msgstr "Nevar dublēt ierīci '%s' lasīšanai un rakstīšanai."
 
 #, c-format
 msgid "Failed to set norm for device '%s'."
-msgstr "Neizdevās iestatīt normu ierīcei “%s”."
+msgstr "Neizdevās iestatīt normu ierīcei '%s'."
 
 #, c-format
 msgid "Failed to get current tuner frequency for device '%s'."
-msgstr "Neizdevās saņemt pašreizējo atskaņošanas frekvenci no ierīces “%s”."
+msgstr "Neizdevās iegūt ierīces '%s' pašreizējo uztvērēja frekvenci."
 
 #, c-format
 msgid "Failed to set current tuner frequency for device '%s' to %lu Hz."
 msgstr ""
-"Neizdevās iestatīt ierīces “%s” pašreizējo skaņotāja frekvenci uz %lu Hz."
+"Neizdevās iestatīt ierīces '%s' pašreizējo uztvērēja frekvenci uz %lu Hz."
 
 #, c-format
 msgid "Failed to get signal strength for device '%s'."
-msgstr "Neizdevās saņemt signāla stiprumu no ierīces “%s”."
+msgstr "Neizdevās iegūt signāla stiprumu ierīcei '%s'."
 
 #, c-format
 msgid "Failed to get value for control %d on device '%s'."
-msgstr "Neizdevās saņemt vadīklas vērtību %d ierīcei “%s”."
+msgstr "Neizdevās iegūt kontroles %d vērtību ierīcē '%s'."
 
 #, c-format
 msgid "Failed to set value %d for control %d on device '%s'."
-msgstr "Neizdevās iestatīt vērtību %d vadīklai %d no ierīces “%s”."
+msgstr "Neizdevās iestatīt vērtību %d vadībai %d ierīcē '%s'."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set value %s for control %d on device '%s'."
-msgstr "Neizdevās iestatīt vērtību %d vadīklai %d no ierīces “%s”."
+msgstr "Neizdevās iestatīt vērtību %s vadībai %d ierīcē '%s'."
 
 #, c-format
 msgid "Failed to get current input on device '%s'. May be it is a radio device"
 msgstr ""
-"Neizdevās saņemt pašreizējo ievadi no ierīces “%s”. Iespējams tā ir radio "
-"ierīce"
+"Neizdevās iegūt pašreizējo ievadi ierīcē '%s'. Iespējams, tā ir radio ierīce"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set input %u on device %s."
-msgstr "Neizdevās iestatīt ievadi %d uz ierīces %s."
+msgstr "Neizdevās iestatīt ievadi %u ierīcē %s."
 
 #, c-format
 msgid ""
 "Failed to get current output on device '%s'. May be it is a radio device"
 msgstr ""
-"Neizdevās saņemt pašreizējo izvadu no ierīces “%s”. Iespējams tā ir radio "
-"ierīce"
+"Neizdevās iegūt ierīces '%s' pašreizējo izvadi. Iespējams, tā ir radio ierīce"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set output %u on device %s."
-msgstr "Neizdevās iestatīt izvadi %d uz ierīces %s."
+msgstr "Neizdevās iestatīt izvadu %u ierīcē %s."
 
 msgid "Cannot operate without a clock"
-msgstr "Nevar veikt darbības bez pulksteņa"
-
-#~ msgid "Internal data stream error."
-#~ msgstr "Iekšēja datu plūsmas kļūda."
+msgstr "Nevar darboties bez pulksteņa"
 
 #, c-format
-#~ msgid "This file contains too many streams. Only playing first %d"
-#~ msgstr "Šī datne satur pārāk daudz straumju. Tiek atskaņota tikai pirmā %d"
-
-#~ msgid "Internal data flow error."
-#~ msgstr "Iekšēja datu plūsmas kļūda."
-
-#~ msgid "Record Source"
-#~ msgstr "Ierakstīšanas avots"
-
-#~ msgid "Microphone"
-#~ msgstr "Mikrofons"
-
-#~ msgid "Line In"
-#~ msgstr "Skaņas ieeja"
-
-#~ msgid "Internal CD"
-#~ msgstr "Iekšējais CD"
-
-#~ msgid "SPDIF In"
-#~ msgstr "SPDIF ieeja"
-
-#~ msgid "AUX 1 In"
-#~ msgstr "AUX 1 ieeja"
-
-#~ msgid "AUX 2 In"
-#~ msgstr "AUX 2 ieeja"
-
-#~ msgid "Codec Loopback"
-#~ msgstr "Kodeka atgriezeniskais cikls"
-
-#~ msgid "SunVTS Loopback"
-#~ msgstr "SunVTS atgriezeniskais cikls"
-
-#~ msgid "Volume"
-#~ msgstr "Skaļums"
-
-#~ msgid "Gain"
-#~ msgstr "Pieaugums"
-
-#~ msgid "Monitor"
-#~ msgstr "Monitors"
-
-#~ msgid "Built-in Speaker"
-#~ msgstr "Iebūvētais skaļrunis"
-
-#~ msgid "Headphone"
-#~ msgstr "Austiņas"
-
-#~ msgid "Line Out"
-#~ msgstr "Skaņas izeja"
-
-#~ msgid "SPDIF Out"
-#~ msgstr "SPDIF izeja"
-
-#~ msgid "AUX 1 Out"
-#~ msgstr "AUX 1 izeja"
-
-#~ msgid "AUX 2 Out"
-#~ msgstr "AUX 2 izeja"
-
-#~ msgid "Changing resolution at runtime is not yet supported."
-#~ msgstr "Izšķirtspējas mainīšana izpildlaikā pašlaik netiek atbalstīta."
-
-#~ msgid "Could not establish connection to sound server"
-#~ msgstr "Nevar izveidot savienojumu ar skaņas serveri"
-
-#~ msgid "Failed to query sound server capabilities"
-#~ msgstr "Neizdevās noskaidrot skaņas servera iespējas"
-
-#~ msgid "Bass"
-#~ msgstr "Basi"
-
-#~ msgid "Treble"
-#~ msgstr "Diskants"
-
-#~ msgid "Synth"
-#~ msgstr "Sintēze"
-
-#~ msgid "PCM"
-#~ msgstr "PCM"
-
-#~ msgid "Speaker"
-#~ msgstr "Skaļrunis"
-
-#~ msgid "Line-in"
-#~ msgstr "Skaņas ieeja"
-
-#~ msgid "CD"
-#~ msgstr "CD"
-
-#~ msgid "Mixer"
-#~ msgstr "Jaucējs"
-
-#~ msgid "PCM-2"
-#~ msgstr "PCM-2"
-
-#~ msgid "Record"
-#~ msgstr "Ierakstīt"
-
-#~ msgid "In-gain"
-#~ msgstr "Ievades pieaugums"
-
-#~ msgid "Out-gain"
-#~ msgstr "Izvades pieaugums"
-
-#~ msgid "Line-1"
-#~ msgstr "Līnija-1"
-
-#~ msgid "Line-2"
-#~ msgstr "Līnija-2"
-
-#~ msgid "Line-3"
-#~ msgstr "Līnija-3"
-
-#~ msgid "Digital-1"
-#~ msgstr "Digitālais-1"
-
-#~ msgid "Digital-2"
-#~ msgstr "Digitālais-2"
-
-#~ msgid "Digital-3"
-#~ msgstr "Digitālais-3"
-
-#~ msgid "Phone-in"
-#~ msgstr "Telefona ievade"
-
-#~ msgid "Phone-out"
-#~ msgstr "Telefona izvade"
-
-#~ msgid "Video"
-#~ msgstr "Video"
-
-#~ msgid "Radio"
-#~ msgstr "Radio"
-
-#~ msgid "Could not open audio device for mixer control handling."
-#~ msgstr "Nevarēja atvērt audio ierīci miksera kontroles vadīšanai."
-
-#~ msgid ""
-#~ "Could not open audio device for mixer control handling. This version of "
-#~ "the Open Sound System is not supported by this element."
-#~ msgstr ""
-#~ "Nevar atvērt audio ierīci miksera kontrolas vadīšanai. Šī Atvertās Skaņas "
-#~ "Sistemas versija neatbalsta šo elementu."
-
-#~ msgid "Master"
-#~ msgstr "Galvenais"
-
-#~ msgid "Front"
-#~ msgstr "Priekšējais"
-
-#~ msgid "Rear"
-#~ msgstr "Sāni"
-
-#~ msgid "Headphones"
-#~ msgstr "Austiņas"
-
-#~ msgid "Center"
-#~ msgstr "Centrs"
-
-#~ msgid "LFE"
-#~ msgstr "LFE"
-
-#~ msgid "Surround"
-#~ msgstr "Ieskaujošā"
-
-#~ msgid "Side"
-#~ msgstr "Puse"
-
-#~ msgid "AUX Out"
-#~ msgstr "AUX izeja"
-
-#~ msgid "3D Depth"
-#~ msgstr "3D dziļums"
-
-#~ msgid "3D Center"
-#~ msgstr "3D centrs"
-
-#~ msgid "3D Enhance"
-#~ msgstr "3D uzlabojums"
-
-#~ msgid "Telephone"
-#~ msgstr "Telefons"
-
-#~ msgid "Video In"
-#~ msgstr "Video ieeja"
-
-#~ msgid "AUX In"
-#~ msgstr "AUC ieeja"
-
-#~ msgid "Record Gain"
-#~ msgstr "Ierakstīta pieaugums"
-
-#~ msgid "Output Gain"
-#~ msgstr "Izvada pieaugums"
-
-#~ msgid "Microphone Boost"
-#~ msgstr "Mikrofona pastiprinājums"
-
-#~ msgid "Diagnostic"
-#~ msgstr "Diagnostika"
-
-#~ msgid "Bass Boost"
-#~ msgstr "Bassa pastiprinājums"
-
-#~ msgid "Playback Ports"
-#~ msgstr "Atskaņošanas porti"
-
-#~ msgid "Input"
-#~ msgstr "Ievads"
-
-#~ msgid "Monitor Source"
-#~ msgstr "Monitora avots"
-
-#~ msgid "Keyboard Beep"
-#~ msgstr "Klaviatūras pīkstieni"
-
-#~ msgid "Simulate Stereo"
-#~ msgstr "Simulēt stereo"
-
-#~ msgid "Stereo"
-#~ msgstr "Stereo"
-
-#~ msgid "Surround Sound"
-#~ msgstr "Ieskaujošā skaņa"
-
-#~ msgid "Microphone Gain"
-#~ msgstr "Mikrofona pieaugums"
-
-#~ msgid "Speaker Source"
-#~ msgstr "Skaļruņa avots"
-
-#~ msgid "Microphone Source"
-#~ msgstr "Mikrofona avots"
-
-#~ msgid "Jack"
-#~ msgstr "Ligzda"
-
-#~ msgid "Center / LFE"
-#~ msgstr "Centrs / LFE"
-
-#~ msgid "Stereo Mix"
-#~ msgstr "Stereo mikseris"
-
-#~ msgid "Mono Mix"
-#~ msgstr "Mono miskeris"
-
-#~ msgid "Input Mix"
-#~ msgstr "Ieejas mikseris"
-
-#~ msgid "Microphone 1"
-#~ msgstr "Mikrofons 1"
-
-#~ msgid "Microphone 2"
-#~ msgstr "Mikrofons 2"
-
-#~ msgid "Digital Out"
-#~ msgstr "Digitālā izeja"
-
-#~ msgid "Digital In"
-#~ msgstr "Digitālā ieeja"
-
-#~ msgid "HDMI"
-#~ msgstr "HDMI"
-
-#~ msgid "Modem"
-#~ msgstr "Modēms"
-
-#~ msgid "Handset"
-#~ msgstr "Klausule"
-
-#~ msgid "Other"
-#~ msgstr "Cits"
-
-#~ msgid "None"
-#~ msgstr "Nekas"
-
-#~ msgid "On"
-#~ msgstr "Ieslēgts"
-
-#~ msgid "Off"
-#~ msgstr "Izslēgts"
-
-#~ msgid "Mute"
-#~ msgstr "Klusums"
-
-#~ msgid "Fast"
-#~ msgstr "Ä€trs"
-
-#~ msgid "Very Low"
-#~ msgstr "Ļoti zems"
-
-#~ msgid "Low"
-#~ msgstr "Zems"
-
-#~ msgid "Medium"
-#~ msgstr "Vidējs"
-
-#~ msgid "High"
-#~ msgstr "Augsts"
-
-#~ msgid "Very High"
-#~ msgstr "Ļoti augsts"
-
-#~ msgid "Production"
-#~ msgstr "Produkcija"
-
-#~ msgid "Front Panel Microphone"
-#~ msgstr "Priekšējā paneļa mikrofons"
-
-#~ msgid "Front Panel Line In"
-#~ msgstr "Priekšējā paneļa ieejas līnija"
-
-#~ msgid "Front Panel Headphones"
-#~ msgstr "Priekšējā paneļa austiņas"
-
-#~ msgid "Front Panel Line Out"
-#~ msgstr "Priekšējā paneļa līnijas izeja"
-
-#~ msgid "Green Connector"
-#~ msgstr "Zaļais savienotājs"
-
-#~ msgid "Pink Connector"
-#~ msgstr "Rozā savienotājs"
-
-#~ msgid "Blue Connector"
-#~ msgstr "Zilais savienotājs"
-
-#~ msgid "White Connector"
-#~ msgstr "Baltais savienotājs"
-
-#~ msgid "Black Connector"
-#~ msgstr "Melnais savienotājs"
-
-#~ msgid "Gray Connector"
-#~ msgstr "Pelākais savienotājs"
-
-#~ msgid "Orange Connector"
-#~ msgstr "Oranžais savienotājs"
-
-#~ msgid "Red Connector"
-#~ msgstr "Sarkanais savienotājs"
-
-#~ msgid "Yellow Connector"
-#~ msgstr "Dzeltains savienotājs"
-
-#~ msgid "Green Front Panel Connector"
-#~ msgstr "Zaļais priekšējā paneļa savienotājs"
-
-#~ msgid "Pink Front Panel Connector"
-#~ msgstr "Rozā priekšējā paneļa savienotājs"
-
-#~ msgid "Blue Front Panel Connector"
-#~ msgstr "Zilais priekšējā paneļa savienotājs"
-
-#~ msgid "White Front Panel Connector"
-#~ msgstr "Baltais priekšējā paneļa savienotājs"
-
-#~ msgid "Black Front Panel Connector"
-#~ msgstr "Melnais priekšējā paneļa savienotājs"
-
-#~ msgid "Gray Front Panel Connector"
-#~ msgstr "Pelēkais priekšējā paneļa savienotājs"
-
-#~ msgid "Orange Front Panel Connector"
-#~ msgstr "Oranžais priekšējā paneļa savienotājs"
-
-#~ msgid "Red Front Panel Connector"
-#~ msgstr "Sarkanais priekšējā paneļa savienotājs"
-
-#~ msgid "Yellow Front Panel Connector"
-#~ msgstr "Dzeltenais priekšējā paneļa savienotājs"
-
-#~ msgid "Spread Output"
-#~ msgstr "Izplest izvadu"
-
-#~ msgid "Downmix"
-#~ msgstr "Lejupmiksēšana"
-
-#~ msgid "Virtual Mixer Input"
-#~ msgstr "Virtuālā miksera ievads"
-
-#~ msgid "Virtual Mixer Output"
-#~ msgstr "Virtuālā miksera izvads"
-
-#~ msgid "Virtual Mixer Channels"
-#~ msgstr "Virtuālā miksera kanāli"
-
-#~ msgid "%s %d Function"
-#~ msgstr "%s %d funkcija"
-
-#~ msgid "%s Function"
-#~ msgstr "%s funkcija"
-
-#~ msgid "Got unexpected frame size of %u instead of %u."
-#~ msgstr "Saņēmu negaidītu kadra izmēru %u, nevis %u."
-
-#~ msgid "Error reading %d bytes on device '%s'."
-#~ msgstr "Radās kļūda nolasot %d baitus no ierīces \"%s\"."
-
-#~ msgid "Could not enqueue buffers in device '%s'."
-#~ msgstr "Nevar ierindod buferus ierīcē \"%s\"."
-
-#~ msgid "Failed trying to get video frames from device '%s'."
-#~ msgstr "Neizdevās saņemt video kadrus no ierīces \"%s\"."
-
-#~ msgid "Failed after %d tries. device %s. system error: %s"
-#~ msgstr "Neveiksme pēc %d mēģinājumiem. ierīce %s. sistēmas kļūda: %s"
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Ierīces %s dekodētājam nav atbalstītu izvada formātu"
diff --git a/po/mt.po b/po/mt.po
index cb02e463bdd764d168fb1cc25844669e12ccce86..0d1823fe12b6dc627095d934ef6fda7927ec1779 100644
--- a/po/mt.po
+++ b/po/mt.po
@@ -5,7 +5,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-0.10.10.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2008-10-26 19:09+0100\n"
 "Last-Translator: Michel Bugeja <michelbugeja@rabatmalta.com>\n"
 "Language-Team: Maltese <translation-team-mt@lists.sourceforge.net>\n"
@@ -95,6 +95,12 @@ msgstr "Il-fajl huwa korrott u ma jistax jinfetaħ."
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Il-fajl mhux komplut u ma jistax jindaqq,"
 
@@ -301,11 +307,6 @@ msgid "Decoder on device %s has no supported input format"
 msgstr ""
 "Id-driver tal-apparat '%s' ma jissapportja l-ebda capture method mifhum."
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"Id-driver tal-apparat '%s' ma jissapportja l-ebda capture method mifhum."
-
 #, fuzzy
 msgid "Failed to start decoding thread."
 msgstr "Problem fid-decoding tal-istampa JPEG"
@@ -321,6 +322,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/nb.po b/po/nb.po
index 196b82339852fd32e28f6faadc7951c0763a5949..0ccd6030e8b8534388ba84f03667313d100cf7d5 100644
--- a/po/nb.po
+++ b/po/nb.po
@@ -2,14 +2,14 @@
 # This file is put in the public domain.
 #
 # Kjartan Maraas <kmaraas@gnome.org>, 2004-2010.
-# Johnny A. Solbu <johnny@solbu.net>, 2012-2021
+# Johnny A. Solbu <johnny@solbu.net>, 2012-2024
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-09-24 18:03+0200\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-09-11 12:17+0200\n"
 "Last-Translator: Johnny A. Solbu <johnny@solbu.net>\n"
 "Language-Team: Norwegian Bokmaal <l10n-no@lister.huftis.org>\n"
 "Language: nb_NO\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Generator: Poedit 2.2.3\n"
+"X-Generator: Poedit 2.4.2\n"
 
 msgid "Jack server not found"
 msgstr "Jack-server ikke funnet"
@@ -51,7 +51,7 @@ msgid "'%s' by '%s'"
 msgstr "«%s» av «%s»"
 
 msgid "Could not connect to server"
-msgstr "Kunne ikke koble til tjener."
+msgstr "Kunne ikke koble til tjener"
 
 msgid "No URL set."
 msgstr "Ingen URL satt."
@@ -97,6 +97,12 @@ msgstr "Filen er skadet og kan ikke spilles."
 msgid "Invalid atom size."
 msgstr "Ugyldig atomstørrelse."
 
+msgid "Cannot query file size"
+msgstr "Kan ikke spørre etter filstørrelsee"
+
+msgid "Cannot demux file"
+msgstr "Kan ikke demux-e fil"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Filen er ufullstendig og kan ikke spilles."
 
@@ -147,7 +153,7 @@ msgid "Playback is not supported by this audio device."
 msgstr "Avspilling støttes ikke av denne lydenheten."
 
 msgid "Audio playback error."
-msgstr "Lydavspillingsfeil"
+msgstr "Lydavspillingsfeil."
 
 msgid "Recording is not supported by this audio device."
 msgstr "Opptak støttes ikke av denne lydenheten."
@@ -249,7 +255,7 @@ msgid "Video device uses an unsupported pixel format."
 msgstr "Videoenhet bruker et ustøttet pikselformat."
 
 msgid "Failed to configure internal buffer pool."
-msgstr "Klarte ikke å konfigurere internt bufferområde"
+msgstr "Klarte ikke å konfigurere internt bufferområde."
 
 msgid "Video device did not suggest any buffer size."
 msgstr "Videoenhet foreslo ikke noen bufferstørrelse."
@@ -271,7 +277,7 @@ msgstr "Enheten «%s» er ikke en tuner."
 
 #, c-format
 msgid "Failed to get radio input on device '%s'. "
-msgstr "Mislyktes i å få radio-inndata på enheten «%s»."
+msgstr "Mislyktes i å få radio-inndata på enheten «%s». "
 
 #, c-format
 msgid "Failed to set input %d on device %s."
@@ -299,10 +305,6 @@ msgstr "Konverter på enhet «%s» har ingen støttede utdataformat"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Dekoder på enhet «%s» har ingen støttede inndataformat"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Dekoder på enhet «%s» har ingen støttede utdataformat"
-
 msgid "Failed to start decoding thread."
 msgstr "Klarte ikke å starte dekoding av tråden."
 
@@ -317,6 +319,9 @@ msgstr "Enkoder på enhet «%s» har ingen støttede utdataformat"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Enkoder på enhet «%s» har ingen støttede inndataformat"
 
+msgid "Failed to force keyframe."
+msgstr "Kunne ikke tvinge nøkkelbilde."
+
 msgid "Failed to start encoding thread."
 msgstr "Klarte ikke å starte kodingstråd."
 
@@ -423,6 +428,10 @@ msgstr "Klarte ikke å sette utgang %u på enhet %s."
 msgid "Cannot operate without a clock"
 msgstr "Kan ikke operere uten en klokke"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Dekoder på enhet «%s» har ingen støttede utdataformat"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Endring av oppløsning under kjøring støttes ikke enda."
 
diff --git a/po/nl.po b/po/nl.po
index 4a29cdd4c19f3e933feaf44302a467bdf005b987..834c5902eef4ce497d918cd59cd43f29ddce08b1 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -1,13 +1,14 @@
 # translation of gst-plugins-good.nl.po to Dutch
 # This file is put in the public domain.
 #
-# Freek de Kruijf <f.de.kruijf@gmail.com>, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2022.
+# SPDX-FileCopyrightText: 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2022, 2024 Freek de Kruijf <f.de.kruijf@gmail.com>
+# Freek de Kruijf <f.de.kruijf@gmail.com>, 2024
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2022-02-08 14:13+0100\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-10-14 13:59+0200\n"
 "Last-Translator: Freek de Kruijf <f.de.kruijf@gmail.com>\n"
 "Language-Team: Dutch <vertaling@vrijschrift.org>\n"
 "Language: nl\n"
@@ -15,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Generator: Lokalize 21.12.2\n"
+"X-Generator: Lokalize 24.08.2\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 
 msgid "Jack server not found"
@@ -100,6 +101,12 @@ msgstr "Dit bestand is beschadigd en kan niet afgespeeld worden."
 msgid "Invalid atom size."
 msgstr "Ongeldige \"atom\"-grootte."
 
+msgid "Cannot query file size"
+msgstr "Kan bestandsgrootte niet opvragen "
+
+msgid "Cannot demux file"
+msgstr "Kan demux niet op bestand toepassen"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Dit bestand is incompleet en kan niet afgespeeld worden."
 
@@ -304,10 +311,6 @@ msgstr "Converter op apparaat %s heeft geen ondersteund uitvoerformaat"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Decodeerder op apparaat %s heeft geen ondersteund invoerformaat"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Decodeerder op apparaat %s heeft geen ondersteund uitvoerformaat"
-
 msgid "Failed to start decoding thread."
 msgstr "Beginnen met decoderen van thread is mislukt."
 
@@ -322,6 +325,9 @@ msgstr "Codeerder op apparaat %s heeft geen ondersteund uitvoerformaat"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Codeerder op apparaat %s heeft geen ondersteund invoerformaat"
 
+msgid "Failed to force keyframe."
+msgstr "Keyframe afdwingen is mislukt."
+
 msgid "Failed to start encoding thread."
 msgstr "Beginnen met coderen van thread is mislukt."
 
@@ -430,6 +436,10 @@ msgstr "Kan uitvoer %u op apparaat %s niet zetten."
 msgid "Cannot operate without a clock"
 msgstr "Kan niet werken zonder een klok."
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Decodeerder op apparaat %s heeft geen ondersteund uitvoerformaat"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Tijdens het draaien kan de resolutie nog niet gewijzigd worden."
 
diff --git a/po/or.po b/po/or.po
index d377afe047122840b7b5c08e146a22c11f3f6b3a..a1208f6f15dc6e03a017f940c1d4a8edd4aa3cdb 100644
--- a/po/or.po
+++ b/po/or.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-0.8.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2004-09-27 13:32+0530\n"
 "Last-Translator: Gora Mohanty <gora_mohanty@yahoo.co.in>\n"
 "Language-Team: Oriya <gora_mohanty@yahoo.co.in>\n"
@@ -95,6 +95,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -289,10 +295,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -307,6 +309,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/pl.po b/po/pl.po
index a2724c0c625ea59848e8009e631b1fbf4e16a6af..ddda4dd012ff836bf7b028599c63acc067c016f3 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1,13 +1,13 @@
 # Polish translation for gst-plugins-good.
 # This file is distributed under the same license as the gst-plugins-good package.
-# Jakub Bogusz <qboosh@pld-linux.org>, 2007-2021.
+# Jakub Bogusz <qboosh@pld-linux.org>, 2007-2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-09-25 08:52+0200\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-07 21:16+0100\n"
 "Last-Translator: Jakub Bogusz <qboosh@pld-linux.org>\n"
 "Language-Team: Polish <translation-team-pl@lists.sourceforge.net>\n"
 "Language: pl\n"
@@ -97,6 +97,12 @@ msgstr "Ten plik jest uszkodzony i nie może być odtworzony."
 msgid "Invalid atom size."
 msgstr "Błędny rozmiar atomu."
 
+msgid "Cannot query file size"
+msgstr "Nie można odczytać rozmiaru pliku"
+
+msgid "Cannot demux file"
+msgstr "Nie można zdemultipleksować pliku"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Ten plik jest niekompletny i nie może być odtworzony."
 
@@ -302,15 +308,11 @@ msgstr "Konwerter urządzenia %s nie ma obsługiwanego formatu wyjściowego"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Dekoder urządzenia %s nie ma obsługiwanego formatu wejściowego"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Dekoder urządzenia %s nie ma obsługiwanego formatu wyjściowego"
-
 msgid "Failed to start decoding thread."
 msgstr "Nie udało się uruchomić wątku dekodującego."
 
 msgid "Failed to process frame."
-msgstr "Nie udało się przetworzyć ramki."
+msgstr "Nie udało się przetworzyć klatki."
 
 #, c-format
 msgid "Encoder on device %s has no supported output format"
@@ -320,6 +322,9 @@ msgstr "Koder urządzenia %s nie ma obsługiwanego formatu wyjściowego"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Koder urządzenia %s nie ma obsługiwanego formatu wejściowego"
 
+msgid "Failed to force keyframe."
+msgstr "Nie udało się wymusić klatki kluczowej."
+
 msgid "Failed to start encoding thread."
 msgstr "Nie udało się uruchomić wątku kodującego."
 
@@ -427,7 +432,3 @@ msgstr "Nie udało się ustawić wyjścia %u urządzenia %s."
 
 msgid "Cannot operate without a clock"
 msgstr "Nie można pracować bez zegara"
-
-#~ msgid "Changing resolution at runtime is not yet supported."
-#~ msgstr ""
-#~ "Zmiana rozdzielczości w czasie działania nie jest jeszcze obsługiwana."
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 5edc17e867b6c8a949200fb4074956354eaddb10..06c71902d902afe87709be9a91197793df2d07f9 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -1,7 +1,8 @@
 # Brazilian Portuguese translation of gst-plugins-good.
 # This file is distributed under the same license as the gst-plugins-good package.
-# Copyright (C) 2008-2019 Free Software Foundation, Inc.
+# Copyright (C) 2008-2024 Free Software Foundation, Inc.
 # Fabrício Godoy <skarllot@gmail.com>, 2008-2019.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2024.
 #
 # data flow -> fluxo de dados
 # streaming -> fluxo contínuo
@@ -11,11 +12,11 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.19.2\n"
+"Project-Id-Version: gst-plugins-good-1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-09-26 11:49-0300\n"
-"Last-Translator: Fabrício Godoy <skarllot@gmail.com>\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-10-09 11:06-0300\n"
+"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
 "Language-Team: Brazilian Portuguese <ldpbr-translation@lists.sourceforge."
 "net>\n"
 "Language: pt_BR\n"
@@ -23,8 +24,8 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Poedit 3.0\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Generator: Gtranslator 47.0\n"
 
 msgid "Jack server not found"
 msgstr "Servidor Jack não encontrado"
@@ -109,6 +110,12 @@ msgstr "Este arquivo está corrompido e não pôde ser reproduzido."
 msgid "Invalid atom size."
 msgstr "Tamanho de Atom inválido."
 
+msgid "Cannot query file size"
+msgstr "Não foi possível consultar tamanho do arquivo"
+
+msgid "Cannot demux file"
+msgstr "Não foi possível fazer desmultiplexar o arquivo"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Este arquivo está incompleto e não pôde ser reproduzido."
 
@@ -319,11 +326,6 @@ msgid "Decoder on device %s has no supported input format"
 msgstr ""
 "O decodificador no dispositivo %s não tem um formato de entrada com suporte"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"O decodificador no dispositivo %s não tem um formato de saída com suporte"
-
 msgid "Failed to start decoding thread."
 msgstr "Falha ao iniciar o segmento de decodificação."
 
@@ -340,6 +342,9 @@ msgid "Encoder on device %s has no supported input format"
 msgstr ""
 "O codificador no dispositivo %s não tem um formato de entrada com suporte"
 
+msgid "Failed to force keyframe."
+msgstr "Falha ao forçar o quadro chave."
+
 msgid "Failed to start encoding thread."
 msgstr "Falha ao iniciar o segmento de codificação."
 
@@ -450,6 +455,11 @@ msgstr "Falha ao definir a saída %u no dispositivo %s."
 msgid "Cannot operate without a clock"
 msgstr "Não é possível operar sem um temporizador"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "O decodificador no dispositivo %s não tem um formato de saída com suporte"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr ""
 #~ "Ainda não há suporte a mudança de resolução enquanto está executando."
diff --git a/po/ro.po b/po/ro.po
index ccbe6f63968c6cff6e1669b46d3ce81db137557d..ca954b6ead84287037fe45d5074f1be327cfd5b8 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -1,9 +1,10 @@
-# Romanian translation for gst-plugins-good
+# Romanian translation for gst-plugins-good.
+# Mesajele în limba română pentru pachetul gst-plugins-good.
 # This file is distributed under the same license as the gst-plugins-good package.
 #
 # Lucian Adrian Grijincu <lucian.grijincu@gmail.com>, 2010.
 # Florentina Mușat <florentina.musat.28@gmail.com>, 2020.
-# Remus-Gabriel Chelu <remusgabriel.chelu@disroot.org>. 2022 - 2023.
+# Remus-Gabriel Chelu <remusgabriel.chelu@disroot.org>, 2022 - 2024.
 #
 # Cronologia traducerii fișierului „gst-plugins-good”:
 # Traducerea inițială, făcută de LAG, pentru versiunea gst-plugins-good 0.10.23.2
@@ -11,15 +12,17 @@
 # Actualizare a mesajelor, de la fișierul „gst-plugins-good-1.19.2.pot”.
 # Eliminare a mesajelor ce-au dispărut în ultima versiune.
 # Actualizare a traducerii pentru versiunea 1.19.2, făcută de R-GC, ian-2022.
-# Actualizare a traducerii pentru versiunea 1.21.90, făcută de R-GC, ian-2023
+# Actualizare a traducerii pentru versiunea 1.21.90, făcută de R-GC, ian-2023.
+# Revizuire și corectare a traducerii pentru versiunea 1.21.90, făcută de R-GC, noi-2023.
+# Actualizare a traducerii pentru versiunea 1.24.0, făcută de R-GC, mar-2024.
 # Actualizare a traducerii pentru versiunea Y, făcută de X, Z(anul).
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.21.90\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-23 16:27+0000\n"
-"PO-Revision-Date: 2023-01-15 20:09+0100\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-08 10:53+0100\n"
 "Last-Translator: Remus-Gabriel Chelu <remusgabriel.chelu@disroot.org>\n"
 "Language-Team: Romanian <translation-team-ro@lists.sourceforge.net>\n"
 "Language: ro\n"
@@ -27,7 +30,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
-"20)) ? 1 : 2);;\n"
+"20)) ? 1 : 2);\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
 "X-Generator: Poedit 3.2.2\n"
 "X-Launchpad-Export-Date: 2010-08-16 00:08+0000\n"
@@ -114,6 +117,12 @@ msgstr "Acest fișier este corupt și nu poate fi redat."
 msgid "Invalid atom size."
 msgstr "Dimensiune de atom nevalidă."
 
+msgid "Cannot query file size"
+msgstr "Nu se poate obține dimensiunea fișierului"
+
+msgid "Cannot demux file"
+msgstr "Nu se poate demultiplexa fișierul"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Acest fișier nu este complet și nu poate fi redat."
 
@@ -167,13 +176,13 @@ msgstr ""
 "OSS (Open Sound System) nu este suportată de acest element."
 
 msgid "Playback is not supported by this audio device."
-msgstr "Redarea nu este suportată de acest dispozitiv audio."
+msgstr "Redarea nu este asigurată de acest dispozitiv audio."
 
 msgid "Audio playback error."
 msgstr "Eroare redare audio."
 
 msgid "Recording is not supported by this audio device."
-msgstr "Înregistrarea nu este suportată de acest dispozitiv audio."
+msgstr "Înregistrarea nu este asigurată de acest dispozitiv audio."
 
 msgid "Error recording from audio device."
 msgstr "Eroare la înregistrarea de la dispozitivul audio."
@@ -226,14 +235,15 @@ msgstr "Nu s-a putut cartografia memoria tampon din dispozitivul „%s”"
 
 #, c-format
 msgid "The driver of device '%s' does not support the IO method %d"
-msgstr "Driverul dispozitivului „%s” nu suportă metoda IO %d"
+msgstr "Controlorul dispozitivului „%s” nu acceptă metoda de In/Ieș %d"
 
 # R-Gc, scrie:
 # s-a modificat de la:
 # „Driverul dispozitivului „%s” nu suportă orice metodă IO cunoscută.”
 #, c-format
 msgid "The driver of device '%s' does not support any known IO method."
-msgstr "Driverul dispozitivului „%s” nu suportă nicio metodă IO cunoscută."
+msgstr ""
+"Controlorul dispozitivului „%s” nu acceptă nicio metodă de In/Ieș cunoscută."
 
 # DȘ, spune:
 # „→ aici cred că „caps” vine de la „capitals” care
@@ -259,7 +269,7 @@ msgstr "Caracteristici incorecte"
 
 #, c-format
 msgid "Device '%s' has no supported format"
-msgstr "Dispozitivul „%s” nu are niciun format suportat"
+msgstr "Dispozitivul „%s” nu are un format compatibil"
 
 #, c-format
 msgid "Device '%s' failed during initialization"
@@ -294,7 +304,7 @@ msgid "Could not get parameters on device '%s'"
 msgstr "Nu s-au putut obține parametrii pentru dispozitivul „%s”"
 
 msgid "Video device did not accept new frame rate setting."
-msgstr "Dispozitivul video nu a acceptat o configurare de rată de cadru nouă."
+msgstr "Dispozitivul video nu a acceptat noua valoare a frecvenței cadrelor."
 
 msgid "Video device did not provide output format."
 msgstr "Dispozitivul video nu a furnizat un format de ieșire."
@@ -309,10 +319,11 @@ msgid "Video device uses an unsupported pixel format."
 msgstr "Dispozitivul video utilizează un format de pixel neacceptat."
 
 msgid "Failed to configure internal buffer pool."
-msgstr "Nu s-a putut configura rezerva internă de preîncărcare."
+msgstr "Nu s-a putut configura rezerva internă de memorie tampon."
 
 msgid "Video device did not suggest any buffer size."
-msgstr "Dispozitivul video nu a sugerat nicio dimensiune de preîncărcare."
+msgstr ""
+"Dispozitivul video nu a sugerat nicio dimensiune pentru memoria tampon."
 
 msgid "No downstream pool to import from."
 msgstr "Nu există o rezervă în aval din care să se importe."
@@ -324,8 +335,8 @@ msgstr "Nu există o rezervă în aval din care să se importe."
 #, c-format
 msgid "Failed to get settings of tuner %d on device '%s'."
 msgstr ""
-"Nu s-au putut obține configurările a receptorului de semnal %d pe "
-"dispozitivul „%s”."
+"Nu s-au putut obține configurările receptorului de semnal %d pe dispozitivul "
+"„%s”."
 
 # R-GC, scrie:
 # modificat de la:
@@ -348,10 +359,10 @@ msgstr "Definirea valorii %d pentru dispozitivul „%s” a eșuat."
 
 #, c-format
 msgid "Failed to change mute state for device '%s'."
-msgstr "Nu s-a putut modifica starea „fără sunet” pe dispozitivul „%s”."
+msgstr "Nu s-a putut modifica starea „sunet dezactivat” pe dispozitivul „%s”."
 
 msgid "Failed to allocated required memory."
-msgstr "Nu s-a putut aloca memoria necesară."
+msgstr "Nu s-a reușit să se aloce memoria necesară."
 
 msgid "Failed to allocate required memory."
 msgstr "Nu s-a putut aloca memoria necesară."
@@ -371,11 +382,6 @@ msgid "Decoder on device %s has no supported input format"
 msgstr ""
 "Decodificatorul pe dispozitivul %s nu are niciun format de intrare acceptat"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"Decodificatorul pe dispozitivul %s nu are niciun format de ieșire acceptat"
-
 msgid "Failed to start decoding thread."
 msgstr "Nu s-a putut începe firul de decodificare."
 
@@ -392,16 +398,20 @@ msgid "Encoder on device %s has no supported input format"
 msgstr ""
 "Codificatorul pe dispozitivul %s nu are niciun format de intrare acceptat"
 
+msgid "Failed to force keyframe."
+msgstr "Nu s-a putut forța cadrul cheie."
+
 msgid "Failed to start encoding thread."
-msgstr "Nu s-a putut începe firul de codare."
+msgstr "Nu s-a putut începe firul de codificare."
 
 #, c-format
 msgid ""
 "Error getting capabilities for device '%s': It isn't a v4l2 driver. Check if "
 "it is a v4l1 driver."
 msgstr ""
-"Eroare la obținerea caracteristicilor pentru dispozitivul „%s”: Nu este un "
-"driver v4l2. Verificați dacă este un driver v4l1."
+"Eroare la obținerea caracteristicilor pentru dispozitivul „%s”: Controlorul "
+"dispozitivului nu este un controlor v4l2. Verificați dacă este un controlor "
+"v4l1."
 
 #, c-format
 msgid "Failed to query attributes of input %d in device %s"
@@ -513,5 +523,10 @@ msgstr "Nu s-a putut stabili ieșirea %u pe dispozitivul %s."
 msgid "Cannot operate without a clock"
 msgstr "Nu se poate opera fără un ceas"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "Decodificatorul pe dispozitivul %s nu are niciun format de ieșire acceptat"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Schimbarea rezoluției pe durata rulării nu este încă suportată."
diff --git a/po/ru.po b/po/ru.po
index b5525391a9397e923a7a6f538566cdb0cf1f837b..652965caffdddb4a45a99e21123392892ff290fe 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -3,14 +3,14 @@
 #
 # Артём Попов <artfwo@gmail.com>, 2009.
 # Pavel Maryanov <acid_jack@ukr.net>, 2009.
-# Yuri Kozlov <yuray@komyakino.ru>, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021.
+# Yuri Kozlov <yuray@komyakino.ru>, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021, 2024.
 # Pavel Maryanov <acid@jack.kiev.ua>, 2013.
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-09-28 07:08+0300\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-27 05:21+0300\n"
 "Last-Translator: Yuri Kozlov <yuray@komyakino.ru>\n"
 "Language-Team: Russian <gnu@d07.ru>\n"
 "Language: ru\n"
@@ -18,7 +18,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Generator: Lokalize 20.12.0\n"
+"X-Generator: Lokalize 22.12.3\n"
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 
@@ -105,6 +105,12 @@ msgstr "Файл повреждён и не может быть воспроиз
 msgid "Invalid atom size."
 msgstr "Неверный атомарный размер."
 
+msgid "Cannot query file size"
+msgstr "Не удалось запросить размер файла"
+
+msgid "Cannot demux file"
+msgstr "Не удалось демультиплексировать файл"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Файл не полон и не может быть воспроизведён."
 
@@ -311,10 +317,6 @@ msgstr "Преобразователь на устройстве %s не под
 msgid "Decoder on device %s has no supported input format"
 msgstr "Раскодировщик на устройстве %s не поддерживает входной формат"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Раскодировщик на устройстве %s не поддерживает выходной формат"
-
 msgid "Failed to start decoding thread."
 msgstr "Не удалось запустить нить декодирования."
 
@@ -329,6 +331,9 @@ msgstr "Кодировщик на устройстве %s не поддержи
 msgid "Encoder on device %s has no supported input format"
 msgstr "Кодировщик на устройстве %s не поддерживает входной формат"
 
+msgid "Failed to force keyframe."
+msgstr "Не удалось принудительно задать ключевой кадр."
+
 msgid "Failed to start encoding thread."
 msgstr "Не удалось запустить нить кодирования."
 
@@ -440,6 +445,10 @@ msgstr "Не удалось выбрать выход %u на устройств
 msgid "Cannot operate without a clock"
 msgstr "Операция невозможна без часов"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Раскодировщик на устройстве %s не поддерживает выходной формат"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr ""
 #~ "Изменение разрешения видео во время выполнения не поддерживается в "
diff --git a/po/sk.po b/po/sk.po
index 7b3b276583a4b23b35382ac76b4c95247e903962..95fa2f4e787958f8803551fff65c2b0224d9390e 100644
--- a/po/sk.po
+++ b/po/sk.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.15.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2020-06-11 10:01+0200\n"
 "Last-Translator: Peter Tuhársky <tuharsky@misbb.sk>\n"
 "Language-Team: Slovak <sk-i18n@lists.linux.sk>\n"
@@ -100,6 +100,12 @@ msgstr "Tento súbor je poškodený a nedá sa prehrať."
 msgid "Invalid atom size."
 msgstr "Nesprávna atomická veľkosť."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Tento súbor je neúplný a nedá sa prehrať."
 
@@ -305,10 +311,6 @@ msgstr "Konvertor na zariadení %s nepodporuje žiadny výstupný formát"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Dekodér na zariadení %s nemá žiadny podporovaný vstupný formát"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Dekodér na zariadení %s nemá žiadny podporovaný výstupný formát"
-
 msgid "Failed to start decoding thread."
 msgstr "Nepodarilo sa spustiť dekódovacie vlákno."
 
@@ -323,6 +325,10 @@ msgstr "Enkodér na zariadení %s nepodporuje žiadny výstupný formát"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Enkodér na zariadení %s nepodporuje žiadny vstupný formát"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Nepodarilo sa spracovať snímok."
+
 msgid "Failed to start encoding thread."
 msgstr "Nepodarilo sa spustiť enkódovacie vlákno."
 
@@ -433,6 +439,10 @@ msgstr "Nepodarilo sa nastaviť výstup %d na zariadení %s."
 msgid "Cannot operate without a clock"
 msgstr "Nemôžem fungovať bez hodín"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Dekodér na zariadení %s nemá žiadny podporovaný výstupný formát"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Zmena rozlíšenia za chodu nie je zatiaľ podporovaná."
 
diff --git a/po/sl.po b/po/sl.po
index 9e5e4a6446c90b638b1edfeb272cd907a172c352..30c3c7ac5cc5da8305787394e327e682c6e3b62b 100644
--- a/po/sl.po
+++ b/po/sl.po
@@ -5,14 +5,15 @@
 # Robert Horvat <robi@hipnos.net>, 2010.
 # Matej Urbančič <matej.urban@gmail.com>, 2010 - 2011.
 # Klemen Košir <klemen913@gmail.com>, 2012 - 2014.
+# Martin Srebotnjak  <miles@filmsi.net>, 2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.2.1\n"
+"Project-Id-Version: gst-plugins-good-1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2014-04-09 22:52+0100\n"
-"Last-Translator: Klemen Košir <klemen913@gmail.com>\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-08-05 15:27+0200\n"
+"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
 "Language-Team: Slovenian <translation-team-sl@lists.sourceforge.net>\n"
 "Language: sl\n"
 "MIME-Version: 1.0\n"
@@ -21,6 +22,7 @@ msgstr ""
 "X-Bugs: Report translation errors to the Language-Team address.\n"
 "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
 "n%100==4 ? 3 : 0);\n"
+"X-Generator: Poedit 2.2.1\n"
 
 msgid "Jack server not found"
 msgstr "Strežnika JACK ni mogoče najti"
@@ -28,13 +30,14 @@ msgstr "Strežnika JACK ni mogoče najti"
 msgid "Failed to decode JPEG image"
 msgstr "Napaka med dekodiranjem JPEG slike"
 
-#, fuzzy
 msgid "Failed to read memory"
-msgstr "Napaka med dekodiranjem JPEG slike"
+msgstr "Branje pomnilnika je spodletelo"
 
 msgid ""
 "Failed to configure LAME mp3 audio encoder. Check your encoding parameters."
 msgstr ""
+"Prilagoditev zvočnega kodirnika LAME mp3 ni uspela. Preverite parametre "
+"kodiranja."
 
 #. <php-emulation-mode>three underscores for ___rate is really really really
 #. * private as opposed to one underscore<php-emulation-mode>
@@ -45,6 +48,8 @@ msgid ""
 "The requested bitrate %d kbit/s for property '%s' is not allowed. The "
 "bitrate was changed to %d kbit/s."
 msgstr ""
+"Zahtevana bitna hitrost %d kbit/s za lastnost »%s« ni dovoljena. Bitna "
+"hitrost je bila spremenjena na %d kbit/s."
 
 #. TRANSLATORS: 'song title' by 'artist name'
 #, c-format
@@ -66,7 +71,6 @@ msgstr "Ni mogoče vzpostaviti povezave s strežnikom."
 msgid "Secure connection setup failed."
 msgstr "Nastavitev varne povezave je spodletela."
 
-#, fuzzy
 msgid ""
 "A network error occurred, or the server closed the connection unexpectedly."
 msgstr ""
@@ -80,6 +84,7 @@ msgstr "Strežnik ne podpira iskanja."
 
 msgid "Failed to configure TwoLAME encoder. Check your encoding parameters."
 msgstr ""
+"Prilagoditev kodirnika TwoLAME ni uspela. Preverite parametre kodiranja."
 
 msgid "No or invalid input audio, AVI stream will be corrupt."
 msgstr "Napaka vhodnega zvoka, AVI pretok bo pokvarjen."
@@ -99,6 +104,12 @@ msgstr "Datoteka je pokvarjena, zato je ni mogoče predvajati."
 msgid "Invalid atom size."
 msgstr "Neveljavna velikost atoma."
 
+msgid "Cannot query file size"
+msgstr "Ni mogoče poizvedovati po velikosti datoteke"
+
+msgid "Cannot demux file"
+msgstr "Datoteke ni možno demultipleksirati"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Datoteka ni popolna, zato je ni mogoče predvajati."
 
@@ -167,14 +178,13 @@ msgid "Could not open audio device for recording."
 msgstr "Zvočne naprave ni mogoče odpreti za snemanje."
 
 msgid "CoreAudio device not found"
-msgstr ""
+msgstr "Naprave CoreAudio ni mogoče najti"
 
-#, fuzzy
 msgid "CoreAudio device could not be opened"
-msgstr "Naprava ne more ustvariti zaloge medpomnilnika."
+msgstr "Naprave CoreAudio ni bilo mogoče odpreti"
 
 msgid "Raspberry Pi Camera Module"
-msgstr ""
+msgstr "Modul kamere Raspberry Pi"
 
 #, c-format
 msgid "Error reading %d bytes from device '%s'."
@@ -198,17 +208,16 @@ msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
 msgid "The driver of device '%s' does not support any known IO method."
 msgstr "Gonilnik naprave \"%s\" ne podpira nobenega znanega načina I/O."
 
-#, fuzzy
 msgid "Invalid caps"
-msgstr "Neveljavna velikost atoma."
+msgstr "Neveljavne velike črke"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' has no supported format"
-msgstr "Naprava \"%s\" ne podpira zajemanja videa."
+msgstr "Naprava »%s« nima podprte oblike zapisa"
 
 #, c-format
 msgid "Device '%s' failed during initialization"
-msgstr ""
+msgstr "Naprava »%s« pri inicializacijo ni uspelo"
 
 #, c-format
 msgid "Device '%s' is busy"
@@ -222,17 +231,17 @@ msgstr "Naprava \"%s\" ne podpira zajemanja v ločljivosti %dx%d."
 msgid "Device '%s' cannot capture in the specified format"
 msgstr "Naprava \"%s\" ne podpira zajemanja v izbrani obliki."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does support non-contiguous planes"
-msgstr "Naprava \"%s\" ne podpira zajemanja videa."
+msgstr "Naprava »%s« ne podpira nestične ravnine"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s interlacing"
-msgstr "Naprava \"%s\" ne podpira zajemanja videa."
+msgstr "Naprava »%s« ne podpira prepletanja %s"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s colorimetry"
-msgstr "Naprava \"%s\" ne podpira zajemanja videa."
+msgstr "Naprava »%s« ne podpira kolorimetrije %s"
 
 #, c-format
 msgid "Could not get parameters on device '%s'"
@@ -241,30 +250,26 @@ msgstr "Ni mogoče pridobiti parametrov na napravi '%s'."
 msgid "Video device did not accept new frame rate setting."
 msgstr "Napravi ni mogoče določiti novih nastavitev hitrosti sličic."
 
-#, fuzzy
 msgid "Video device did not provide output format."
-msgstr "Napravi ni mogoče določiti novih nastavitev hitrosti sličic."
+msgstr "Video naprava ni zagotovila izhodne oblike."
 
 msgid "Video device returned invalid dimensions."
-msgstr ""
+msgstr "Videonaprava je vrnila neveljavne mere."
 
-#, fuzzy
 msgid "Video device uses an unsupported interlacing method."
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
+msgstr "Videonaprava uporablja nepodprt način prepletanja."
 
 msgid "Video device uses an unsupported pixel format."
-msgstr ""
+msgstr "Videonaprava uporablja nepodprto obliko zapisa slikovnih točk."
 
-#, fuzzy
 msgid "Failed to configure internal buffer pool."
-msgstr "Naprava ne more ustvariti zaloge medpomnilnika."
+msgstr "Ustvarjanje notranje zaloge medpomnilnika je spodletelo."
 
-#, fuzzy
 msgid "Video device did not suggest any buffer size."
-msgstr "Naprava ne more ustvariti zaloge medpomnilnika."
+msgstr "Video naprava ni predlagala nobene velikosti medpomnilnika."
 
 msgid "No downstream pool to import from."
-msgstr ""
+msgstr "Ni pripadne zaloge za uvoz."
 
 #, c-format
 msgid "Failed to get settings of tuner %d on device '%s'."
@@ -274,15 +279,15 @@ msgstr ""
 
 #, c-format
 msgid "Error getting capabilities for device '%s'."
-msgstr "Med pridobivanjem lastnosti naprave \"%s\" je prišlo do napake."
+msgstr "Med pridobivanjem lastnosti naprave »%s« je prišlo do napake."
 
 #, c-format
 msgid "Device '%s' is not a tuner."
-msgstr "Naprava \"%s\" ni uglaševalnik."
+msgstr "Naprava »%s« ni uglaševalnik."
 
 #, c-format
 msgid "Failed to get radio input on device '%s'. "
-msgstr "Med pridobivanjem vhoda na napravi \"%s\" je prišlo do napake."
+msgstr "Med pridobivanjem vhoda na napravi »%s« je prišlo do napake."
 
 #, c-format
 msgid "Failed to set input %d on device %s."
@@ -291,47 +296,45 @@ msgstr "Napaka med določanjem vhoda %d naprave %s."
 #, c-format
 msgid "Failed to change mute state for device '%s'."
 msgstr ""
-"Med nastavljanjem stanja glasnosti na napravi \"%s\" je prišlo do napake."
+"Med nastavljanjem stanja glasnosti na napravi »%s« je prišlo do napake."
 
 msgid "Failed to allocated required memory."
-msgstr ""
+msgstr "Dodelitev zahtevanega pomnilnika je spodletela."
 
 msgid "Failed to allocate required memory."
-msgstr ""
+msgstr "Zahtevanega pomnilnika ni bilo mogoče dodeliti."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Converter on device %s has no supported input format"
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
+msgstr "Pretvornik v napravi %s nima podprtega vhodnega formata"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Converter on device %s has no supported output format"
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
+msgstr "Pretvornik v napravi %s nima podprte izhodne oblike zapisa"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Decoder on device %s has no supported input format"
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
-
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
+msgstr "Dekodirnik v napravi %s nima podprtega vhodnega formata"
 
-#, fuzzy
 msgid "Failed to start decoding thread."
-msgstr "Napaka med dekodiranjem JPEG slike"
+msgstr "Ni bilo mogoče začeti dekodiranja niti."
 
 msgid "Failed to process frame."
-msgstr ""
+msgstr "Obdelava sličice ni uspela."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Encoder on device %s has no supported output format"
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
+msgstr "Kodirnik v napravi %s nima podprte izhodne oblike zapisa"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Encoder on device %s has no supported input format"
-msgstr "Gonilnik naprave \"%s\" ne podpira nobenega načina I/O %d."
+msgstr "Kodirnik v napravi %s nima podprte vhodne oblike zapisa"
+
+msgid "Failed to force keyframe."
+msgstr "Ni uspelo vsiliti ključne sličice."
 
 msgid "Failed to start encoding thread."
-msgstr ""
+msgstr "Ni bilo mogoče začeti kodiranja niti."
 
 #, c-format
 msgid ""
@@ -377,13 +380,13 @@ msgstr "Naprava '%s' ni naprava za zajemanje."
 msgid "Device '%s' is not a output device."
 msgstr "Naprava '%s' ni izhodna naprava."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' is not a M2M device."
-msgstr "Naprava '%s' ni izhodna naprava."
+msgstr "Naprava »%s« ni naprava M2M."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Could not dup device '%s' for reading and writing."
-msgstr "Ni mogoče odpreti naprave '%s' za branje in pisanje."
+msgstr "Ni mogoče podvojiti naprave »%s« za branje in pisanje."
 
 #, c-format
 msgid "Failed to set norm for device '%s'."
@@ -410,9 +413,9 @@ msgstr "Napaka med pridobivanjem vrednosti %d na napravi '%s'."
 msgid "Failed to set value %d for control %d on device '%s'."
 msgstr "Napaka med določanjem vrednosti %d za %d na napravi '%s'."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set value %s for control %d on device '%s'."
-msgstr "Napaka med določanjem vrednosti %d za %d na napravi '%s'."
+msgstr "Napaka med določanjem vrednosti %s za kontrolnik %d na napravi »%s«."
 
 #, c-format
 msgid "Failed to get current input on device '%s'. May be it is a radio device"
@@ -420,9 +423,9 @@ msgstr ""
 "Napaka med pridobivanjem vhodnega signala naprave '%s'. Morda je radijska "
 "naprava."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set input %u on device %s."
-msgstr "Napaka med določanjem vhoda %d naprave %s."
+msgstr "Napaka pri določanju vhoda %u naprave %s."
 
 #, c-format
 msgid ""
@@ -431,76 +434,9 @@ msgstr ""
 "Napaka med pridobivanjem odvodnega signala naprave '%s'. Morda je radijska "
 "naprava."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set output %u on device %s."
-msgstr "Napaka med določanjem odvoda %d naprave %s."
+msgstr "Napaka pri določanju izhoda %u naprave %s."
 
 msgid "Cannot operate without a clock"
 msgstr "Izvajanje opravil brez ure ni mogoče"
-
-#~ msgid "Internal data stream error."
-#~ msgstr "Notranja napaka pretoka podatkov."
-
-#, c-format
-#~ msgid "This file contains too many streams. Only playing first %d"
-#~ msgstr "Datoteka vsebuje več pretokov. Predvajano bo le začetnih %d"
-
-#~ msgid "Internal data flow error."
-#~ msgstr "Notranja napaka pretoka podatkov."
-
-#~ msgid "Record Source"
-#~ msgstr "Vir snemanja"
-
-#~ msgid "Microphone"
-#~ msgstr "Mikrofon"
-
-#~ msgid "Line In"
-#~ msgstr "Analogni vhod"
-
-#~ msgid "Internal CD"
-#~ msgstr "Notranji CD"
-
-#~ msgid "SPDIF In"
-#~ msgstr "Vhod SPDIF"
-
-#~ msgid "AUX 1 In"
-#~ msgstr "Vhod AUX 1"
-
-#~ msgid "AUX 2 In"
-#~ msgstr "Vhod AUX 2"
-
-#~ msgid "Codec Loopback"
-#~ msgstr "Povratna zanka kodeka"
-
-#~ msgid "SunVTS Loopback"
-#~ msgstr "Povratna zanka SunVTS"
-
-#~ msgid "Volume"
-#~ msgstr "Glasnost"
-
-#~ msgid "Gain"
-#~ msgstr "Ojačitev"
-
-#~ msgid "Monitor"
-#~ msgstr "Zaslon"
-
-#~ msgid "Built-in Speaker"
-#~ msgstr "Vgrajeni zvočnik"
-
-#~ msgid "Headphone"
-#~ msgstr "Slušalke"
-
-#~ msgid "Line Out"
-#~ msgstr "Analogni izhod"
-
-#~ msgid "SPDIF Out"
-#~ msgstr "Izhod SPDIF"
-
-#~ msgid "AUX 1 Out"
-#~ msgstr "Izhod AUX 1"
-
-#~ msgid "AUX 2 Out"
-#~ msgstr "Izhod AUX 2"
-
-#~ msgid "Changing resolution at runtime is not yet supported."
-#~ msgstr "Spreminjanje ločljivosti med delovanjem še ni podprto."
diff --git a/po/sq.po b/po/sq.po
index 8a86ec6b6010ae8e0d0bc944d9eee4a1372f73c1..1b34d1c2d6654387b2bd52ba772fd82b5e552bd6 100644
--- a/po/sq.po
+++ b/po/sq.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins 0.8.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2004-08-07 20:29+0200\n"
 "Last-Translator: Laurent Dhima <laurenti@alblinux.net>\n"
 "Language-Team: Albanian <begraj@hotmail.com>\n"
@@ -93,6 +93,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -285,10 +291,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -303,6 +305,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/sr.po b/po/sr.po
index 8c9b4aa5a984272a7b70705d31b59334954ea5a8..b962414fb3cd557f06579e40987a1f926adbcc4c 100644
--- a/po/sr.po
+++ b/po/sr.po
@@ -2,13 +2,13 @@
 # Copyright © 2020 Free Software Foundation, Inc.
 # This file is distributed under the same license as the gst-plugins-good package.
 # Danilo Segan <dsegan@gmx.net>, 2004.
-# Мирослав Николић <miroslavnikolic@rocketmail.com>, 2011–2021.
+# Мирослав Николић <miroslavnikolic@rocketmail.com>, 2011-2024.
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.19.2\n"
+"Project-Id-Version: gst-plugins-good-1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-10-01 22:00+0200\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-05-07 07:36+0200\n"
 "Last-Translator: Мирослав Николић <miroslavnikolic@rocketmail.com>\n"
 "Language-Team: Serbian <(nothing)>\n"
 "Language: sr\n"
@@ -18,7 +18,6 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Project-Style: gnome\n"
 
 msgid "Jack server not found"
 msgstr "Нисам пронашао сервер утичнице"
@@ -99,6 +98,12 @@ msgstr "Ова датотека је оштећена и не може бити
 msgid "Invalid atom size."
 msgstr "Неисправна величина атома."
 
+msgid "Cannot query file size"
+msgstr "Не могу да пропитам величину датотеке"
+
+msgid "Cannot demux file"
+msgstr "Не могу да демуксујем датотеку"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Ова датотека је непотпуна и не може бити пуштена."
 
@@ -303,10 +308,6 @@ msgstr "Претварач на уређају „%s“ нема подржан
 msgid "Decoder on device %s has no supported input format"
 msgstr "Декодер на уређају „%s“ нема подржани улазни запис"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Декодер на уређају „%s“ нема подржани излазни запис"
-
 msgid "Failed to start decoding thread."
 msgstr "Нисам успео да покренем нит декодирања."
 
@@ -321,6 +322,9 @@ msgstr "Кодер на уређају „%s“ нема подржани изл
 msgid "Encoder on device %s has no supported input format"
 msgstr "Кодер на уређају „%s“ нема подржани улазни запис"
 
+msgid "Failed to force keyframe."
+msgstr "Нисам успео да приморам кадар."
+
 msgid "Failed to start encoding thread."
 msgstr "Нисам успео да покренем нит кодирања."
 
@@ -428,6 +432,10 @@ msgstr "Нисам успео да подесим излаз „%u“ на ур
 msgid "Cannot operate without a clock"
 msgstr "Не могу да радим без сата"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Декодер на уређају „%s“ нема подржани излазни запис"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Промена резолуције приликом извршавања још увек није подржана."
 
diff --git a/po/sv.po b/po/sv.po
index 45ef96fe82fbd6c8f27160ee1108587c3294b065..91c80c38067358337e37b8e47ce02e9a25b11ad8 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,16 +1,16 @@
 # Swedish messages for gst-plugins-good.
-# Copyright © 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2014, 2015, 2016, 2017, 2019, 2022 Free Software Foundation, Inc.
+# Copyright © 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2014, 2015, 2016, 2017, 2019, 2022, 2024 Free Software Foundation, Inc.
 # This file is distributed under the same license as the gst-plugins-good package.
 # Christian Rose <menthos@menthos.com>, 2004, 2005.
 # Daniel Nylander <po@danielnylander.se>, 2007, 2008, 2009, 2010, 2011.
-# Sebastian Rasmussen <sebras@gmail.com>, 2014, 2015, 2016, 2017, 2019, 2022.
+# Sebastian Rasmussen <sebras@gmail.com>, 2014, 2015, 2016, 2017, 2019, 2022, 2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2022-10-26 21:21+0200\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-03-07 18:24+0800\n"
 "Last-Translator: Sebastian Rasmussen <sebras@gmail.com>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\n"
@@ -18,7 +18,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Generator: Poedit 3.1.1\n"
+"X-Generator: Poedit 3.4.2\n"
 
 msgid "Jack server not found"
 msgstr "Jack-server hittades ej"
@@ -100,6 +100,12 @@ msgstr "Den här filen är skadad och kan inte spelas upp."
 msgid "Invalid atom size."
 msgstr "Ogiltig atomstorlek."
 
+msgid "Cannot query file size"
+msgstr "Kan inte begära filstorlek"
+
+msgid "Cannot demux file"
+msgstr "Kan inte avmultiplexera fil"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Den här filen är inte fullständig och kan inte spelas upp."
 
@@ -306,10 +312,6 @@ msgstr "Konvertorn på enheten %s har inget utgångsformat som stöds"
 msgid "Decoder on device %s has no supported input format"
 msgstr "Avkodaren på enhet %s har inget ingångsformat som stöds"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Avkodaren på enhet %s har inget utgångsformat som stöds"
-
 msgid "Failed to start decoding thread."
 msgstr "Misslyckades med att starta avkodningstråd."
 
@@ -324,6 +326,9 @@ msgstr "Kodaren på enheten %s har inget utgångsformat som stöds"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Kodaren på enheten %s har inget ingångsformat som stöds"
 
+msgid "Failed to force keyframe."
+msgstr "Misslyckades att tvinga nyckelram."
+
 msgid "Failed to start encoding thread."
 msgstr "Misslyckades med att starta kodningstråd."
 
@@ -436,6 +441,10 @@ msgstr "Misslyckades med att ställa in utgång %u på enhet %s."
 msgid "Cannot operate without a clock"
 msgstr "Kan inte fungera utan en klocka"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "Avkodaren på enhet %s har inget utgångsformat som stöds"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Ändring av upplösning vid körtid stöds inte än."
 
diff --git a/po/tr.po b/po/tr.po
index 7d3db6f0fd3a5a6b29011c0cf641dd82be5d3f50..5852f487c7f749b2faed9ef02b76674773599f59 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -1,24 +1,23 @@
 # translation of gst-plugins-good-1.12.0.po to Turkish
 # This file is put in the public domain.
 # This file is distributed under the same license as the gst-plugins-good package.
-# Server Acim <serveracim@gmail.com>, 2010.
-# Server Acim <serveracim@gmail.com>, 2011.
-# Server Acim <serveracim@gmail.com>, 2013, 2015.
+# Server Acim <serveracim@gmail.com>, 2010, 2011, 2013, 2015.
 # Mehmet Kececi <mkececi@mehmetkececi.com>, 2017, 2019, 2021.
+# Emin Tufan Çetin <etcetin@gmail.com>, 2023.
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.21.90\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-10-04 14:15+0300\n"
-"Last-Translator: Mehmet Kececi <mkececi@mehmetkececi.com>\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2023-05-11 21:46+0300\n"
+"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
 "Language-Team: Turkish <gnome-turk@gnome.org>\n"
 "Language: tr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 3.0\n"
+"X-Generator: Poedit 3.2.2\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
 "X-Project-Style: gnome\n"
 
@@ -46,13 +45,13 @@ msgid ""
 "The requested bitrate %d kbit/s for property '%s' is not allowed. The "
 "bitrate was changed to %d kbit/s."
 msgstr ""
-"The requested bitrate %d kbit/s for property '%s' is not allowed. The "
-"bitrate was changed to %d kbit/s."
+"'%2$s' özelliği için istenen %1$d kbit/s bit oranına izin verilmiyor. Bit "
+"oranı %3$d kbit/s olarak değiştirildi."
 
 #. TRANSLATORS: 'song title' by 'artist name'
 #, c-format
 msgid "'%s' by '%s'"
-msgstr "'%s' tarafından '%s'"
+msgstr "'%2$s' - '%1$s'"
 
 msgid "Could not connect to server"
 msgstr "Sunucuya bağlanamıyor"
@@ -71,8 +70,7 @@ msgstr "Güvenli bağlantı ayarı yapılamadı."
 
 msgid ""
 "A network error occurred, or the server closed the connection unexpectedly."
-msgstr ""
-"Bir ağ hatası oluştu veya sunucu beklenmedik şekilde bağlantıyı kapattı."
+msgstr "Ağ hatası oluştu ya da sunucu beklenmedik biçimde bağlantıyı kapattı."
 
 msgid "Server sent bad data."
 msgstr "Sunucu yetersiz veri gönderdi."
@@ -82,7 +80,7 @@ msgstr "Sunucu aramayı desteklemiyor."
 
 msgid "Failed to configure TwoLAME encoder. Check your encoding parameters."
 msgstr ""
-"TwoLAME kodlayıcı yapılandırılamadı. Kodlama parametrelerini kontrol edin."
+"TwoLAME kodlayıcı yapılandırılamadı. Kodlama parametrelerini gözden geçirin."
 
 msgid "No or invalid input audio, AVI stream will be corrupt."
 msgstr "Geçerli bir ses girişi bulunamadı. AVI akışı kesilecek."
@@ -102,33 +100,39 @@ msgstr "Bu dosya bozuk ve oynatılamaz."
 msgid "Invalid atom size."
 msgstr "Geçersiz atom boyutu."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Dosya eksik ve oynatılamaz."
 
 msgid "The video in this file might not play correctly."
-msgstr "Bu dosyadaki vidyo doğru oynatılamıyabilir."
+msgstr "Bu dosyadaki video doğru oynatılamıyabilir."
 
 msgid ""
 "No supported stream was found. You might need to install a GStreamer RTSP "
 "extension plugin for Real media streams."
 msgstr ""
-"Desteklenen bir akış bulunamadı. Real Media akışları için bir GStreamer RTSP "
-"uzantı eklentisi kurmalısınız."
+"Desteklenen akış bulunamadı. Real Media akışları için GStreamer RTSP uzantı "
+"eklentisi kurmalısınız."
 
 msgid ""
 "No supported stream was found. You might need to allow more transport "
 "protocols or may otherwise be missing the right GStreamer RTSP extension "
 "plugin."
 msgstr ""
-"Desteklenen akış bulunamadı. Daha fazla aktarım protokolüne izin vermeniz "
-"veya doğru GStreamer RTSP uzantı eklentisine onay vermeniz gerekebilir."
+"Desteklenen akış bulunamadı. Daha çok aktarım iletişim kuralına izin "
+"vermeniz ya da doğru GStreamer RTSP uzantı eklentisine onay vermeniz "
+"gerekebilir."
 
 msgid ""
 "Could not open audio device for playback. Device is being used by another "
 "application."
 msgstr ""
-"Çalmak için ses aygıtı açılamıyor. Aygıt başka bir uygulama tarafından "
-"kullanılıyor."
+"Çalmak için ses aygıtı açılamıyor. Aygıt başka uygulamaca kullanılıyor."
 
 msgid ""
 "Could not open audio device for playback. You don't have permission to open "
@@ -142,7 +146,7 @@ msgid ""
 "Could not open audio device for playback. This version of the Open Sound "
 "System is not supported by this element."
 msgstr ""
-"Çalma için ses aygıtı açılamıyor. Açık Ses Sistemi'nin bu sürümü, bu öğeyi "
+"Çalma için ses aygıtı açılamıyor. Açık Ses Sistemi'nin bu sürümü, bu ögeyi "
 "desteklemiyor."
 
 msgid "Playback is not supported by this audio device."
@@ -180,7 +184,7 @@ msgstr "%d bayt bilgili '%s' aygıtından okumada hata."
 
 #, c-format
 msgid "Failed to enumerate possible video formats device '%s' can work with"
-msgstr "Aygıtın '%s' birlikte çalışabileceği vidyo kiplerini sıralamada hata"
+msgstr "Aygıtın '%s' birlikte çalışabileceği video kiplerini sıralamada hata"
 
 #, c-format
 msgid "Could not map buffers from device '%s'"
@@ -213,7 +217,7 @@ msgstr "Aygıt '%s' meşgul"
 
 #, c-format
 msgid "Device '%s' cannot capture at %dx%d"
-msgstr "Aygıt '%s' görüntü yakalayamadı%dx%d"
+msgstr "Aygıt '%s' görüntü yakalayamadı %dx%d"
 
 #, c-format
 msgid "Device '%s' cannot capture in the specified format"
@@ -236,7 +240,7 @@ msgid "Could not get parameters on device '%s'"
 msgstr "Değiştirgeler aygıttan '%s' alınamıyor"
 
 msgid "Video device did not accept new frame rate setting."
-msgstr "Vidyo aygıtı yeni çerçeve oranı ayarlarını kabul etmedi."
+msgstr "Video aygıtı yeni çerçeve oranı ayarlarını kabul etmedi."
 
 msgid "Video device did not provide output format."
 msgstr "Video aygıtı çıktı biçimini sağlamadı."
@@ -301,10 +305,6 @@ msgstr "Aygıt %s üzerindeki dönüştürücü çıktı biçimini desteklemiyor
 msgid "Decoder on device %s has no supported input format"
 msgstr "%s aygıtındaki kod çözücü, desteklenen bir giriş biçimine sahip değil"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "%s aygıtındaki kod çözücü, desteklenen bir çıkış biçimine sahip değil"
-
 msgid "Failed to start decoding thread."
 msgstr "Çözme işi başlaması başarısız."
 
@@ -319,6 +319,10 @@ msgstr "Aygıt %s üzerindeki kodlayıcı çıktı biçimini deskteklemiyor"
 msgid "Encoder on device %s has no supported input format"
 msgstr "Aygıt %s üzerindeki kodlayıcı girdi biçimini deskteklemiyor"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "İşlem çerçevesi başarısız."
+
 msgid "Failed to start encoding thread."
 msgstr "Kod çözme iş parçacığı başlatılamadı."
 
@@ -422,6 +426,11 @@ msgstr "Failed to set output %u on device %s."
 msgid "Cannot operate without a clock"
 msgstr "Saat olmadan çalışamaz"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "%s aygıtındaki kod çözücü, desteklenen bir çıkış biçimine sahip değil"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Çalışırken çözünürlüğü değiştirmek henüz desteklenmiyor."
 
diff --git a/po/uk.po b/po/uk.po
index 9ced11a1986cdb4ef1627fb51abeff048921f053..07b12f957c4251f9a7c6af96f619f50646a7f8c8 100644
--- a/po/uk.po
+++ b/po/uk.po
@@ -3,13 +3,13 @@
 # This file is distributed under the same license as the gst-plugins-good package.
 #
 # Maxim V. Dziumanenko <dziumanenko@gmail.com>, 2004-2007.
-# Yuri Chornoivan <yurchor@ukr.net>, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021.
+# Yuri Chornoivan <yurchor@ukr.net>, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021, 2024.
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good 1.19.2\n"
+"Project-Id-Version: gst-plugins-good 1.24.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-09-24 16:49+0300\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2024-07-21 19:45+0300\n"
 "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
 "Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
 "Language: uk\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Generator: Lokalize 20.12.0\n"
+"X-Generator: Lokalize 23.04.3\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
 msgid "Jack server not found"
@@ -106,6 +106,12 @@ msgstr "Файл пошкоджено, його не можна відтвори
 msgid "Invalid atom size."
 msgstr "Некоректний розмір елементарного фрагмента."
 
+msgid "Cannot query file size"
+msgstr "Не вдалося визначити розмір файла"
+
+msgid "Cannot demux file"
+msgstr "Не вдалося демультиплексувати файл"
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Файл неповний, його не можна відтворити."
 
@@ -322,12 +328,6 @@ msgstr ""
 "Засіб декодування на пристрої %s не може отримувати дані у підтримуваному "
 "форматі"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-"Засіб декодування на пристрої %s не може виводити дані у підтримуваному "
-"форматі"
-
 msgid "Failed to start decoding thread."
 msgstr "Не вдалося започаткувати потік декодування."
 
@@ -345,6 +345,9 @@ msgstr ""
 "Засіб кодування на пристрої %s не може отримувати дані у підтримуваному "
 "форматі"
 
+msgid "Failed to force keyframe."
+msgstr "Не вдалося примусово встановити ключовий кадр."
+
 msgid "Failed to start encoding thread."
 msgstr "Не вдалося започаткувати потік кодування."
 
@@ -454,5 +457,11 @@ msgstr "Не вдалося встановити вихід %u пристрою
 msgid "Cannot operate without a clock"
 msgstr "Робота без годинника неможлива"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr ""
+#~ "Засіб декодування на пристрої %s не може виводити дані у підтримуваному "
+#~ "форматі"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "Зміна роздільної здатності при відтворенні ще не підтримується."
diff --git a/po/vi.po b/po/vi.po
index a31c7d05439ff9ea55b319629f847d923ab7c712..5eaa961916b7d69b649779641d6ae1053245924a 100644
--- a/po/vi.po
+++ b/po/vi.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.12.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2017-05-05 06:52+0700\n"
 "Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n"
 "Language-Team: Vietnamese <translation-team-vi@lists.sourceforge.net>\n"
@@ -99,6 +99,12 @@ msgstr "Tập tin này bị hỏng nên không thể phát."
 msgid "Invalid atom size."
 msgstr "Kích thước nguyên tử (atom) không hợp lệ."
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "Tập tin này chưa hoàn thành nên không thể được phát."
 
@@ -305,10 +311,6 @@ msgstr "Bộ chuyển đổi trên thiết bị %s không có định dạng xu
 msgid "Decoder on device %s has no supported input format"
 msgstr "Bộ mã hóa trên thiết bị “%s” không hỗ trợ định dạng đầu vào"
 
-#, fuzzy, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "Bộ giải mã trên thiết bị “%s” không hỗ trợ định dạng đầu ra"
-
 msgid "Failed to start decoding thread."
 msgstr "Gặp lỗi khi bắt đầu tiến trình giải mã."
 
@@ -323,6 +325,10 @@ msgstr "Bộ giải mã trên thiết bị “%s” không hỗ trợ định d
 msgid "Encoder on device %s has no supported input format"
 msgstr "Bộ mã hóa trên thiết bị “%s” không hỗ trợ định dạng đầu vào"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "Gặp lỗi khi xử lý khung."
+
 #, fuzzy
 msgid "Failed to start encoding thread."
 msgstr "Gặp lỗi khi bắt đầu tiến trình giải mã."
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 8e8d2955b18747e06eff2a711e13249913c8d31e..47649b3d1c0e1a8d9ec0cb4eac8f49a83b57a1e3 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good 1.21.90\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-23 16:27+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2023-01-21 13:45+0800\n"
 "Last-Translator: Tianze Wang <zwpwjwtz@126.com>\n"
 "Language-Team: Chinese (simplified) <i18n-zh@googlegroups.com>\n"
@@ -94,6 +94,12 @@ msgstr "此文件已损坏,无法播放。"
 msgid "Invalid atom size."
 msgstr "无效的原子尺寸。"
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "此文件不完整且无法播放。"
 
@@ -287,10 +293,6 @@ msgstr "设备 %s 上的转换器没有支持的输出格式"
 msgid "Decoder on device %s has no supported input format"
 msgstr "设备 %s 上的解码器没有支持的输入格式"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "设备 %s 上的解码器没有支持的输出格式"
-
 msgid "Failed to start decoding thread."
 msgstr "启动解码线程失败。"
 
@@ -305,6 +307,10 @@ msgstr "设备 %s 上的编码器没有支持的输出格式"
 msgid "Encoder on device %s has no supported input format"
 msgstr "设备 %s 上的编码器没有支持的输入格式"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "处理帧失败。"
+
 msgid "Failed to start encoding thread."
 msgstr "启动编码线程失败。"
 
@@ -406,6 +412,10 @@ msgstr "将输出 %u 设置到设备 %s 上失败。"
 msgid "Cannot operate without a clock"
 msgstr "没有时钟的话无法操作"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "设备 %s 上的解码器没有支持的输出格式"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "尚不支持在运行时更改分辨率。"
 
diff --git a/po/zh_HK.po b/po/zh_HK.po
index b56a5dd17aaaa4cc8dac573861e4259dd76bce4c..e13cc08066d3ab76148adf2ec6ab6d6bfc1ebad4 100644
--- a/po/zh_HK.po
+++ b/po/zh_HK.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gst-plugins-good-0.10.2 0.10.2\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
 "PO-Revision-Date: 2006-08-29 01:08+0800\n"
 "Last-Translator: Abel Cheung <abelcheung@gmail.com>\n"
 "Language-Team: Chinese (Hong Kong) <community@linuxhall.org>\n"
@@ -90,6 +90,12 @@ msgstr ""
 msgid "Invalid atom size."
 msgstr ""
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr ""
 
@@ -279,10 +285,6 @@ msgstr ""
 msgid "Decoder on device %s has no supported input format"
 msgstr ""
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr ""
-
 msgid "Failed to start decoding thread."
 msgstr ""
 
@@ -297,6 +299,9 @@ msgstr ""
 msgid "Encoder on device %s has no supported input format"
 msgstr ""
 
+msgid "Failed to force keyframe."
+msgstr ""
+
 msgid "Failed to start encoding thread."
 msgstr ""
 
diff --git a/po/zh_TW.po b/po/zh_TW.po
index 964003d8ab79a6f1d875a7c1f76a0c3776cac7f3..a25fca9954a70d02995a00018c3bdb9398e67d5b 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -5,10 +5,10 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: gst-plugins-good-1.15.1\n"
+"Project-Id-Version: gst-plugins-good-1.21.90\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-01-11 15:41+0000\n"
-"PO-Revision-Date: 2021-05-09 21:14+0800\n"
+"POT-Creation-Date: 2024-11-03 17:36+0000\n"
+"PO-Revision-Date: 2023-02-03 01:03+0800\n"
 "Last-Translator: Yi-Jyun Pan <pan93412@gmail.com>\n"
 "Language-Team: Chinese (traditional) <zh-l10n@lists.linux.org.tw>\n"
 "Language: zh_TW\n"
@@ -16,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Bugs: Report translation errors to the Language-Team address.\n"
-"X-Generator: Poedit 2.4.3\n"
+"X-Generator: Poedit 3.2.2\n"
 
 msgid "Jack server not found"
 msgstr "找不到 Jack 伺服器"
@@ -24,9 +24,8 @@ msgstr "找不到 Jack 伺服器"
 msgid "Failed to decode JPEG image"
 msgstr "無法解碼 JPEG 影像"
 
-#, fuzzy
 msgid "Failed to read memory"
-msgstr "無法配置所需的記憶體。"
+msgstr "無法讀取記憶體"
 
 msgid ""
 "Failed to configure LAME mp3 audio encoder. Check your encoding parameters."
@@ -95,6 +94,12 @@ msgstr "此檔案損壞,無法播放。"
 msgid "Invalid atom size."
 msgstr "atom 大小無效。"
 
+msgid "Cannot query file size"
+msgstr ""
+
+msgid "Cannot demux file"
+msgstr ""
+
 msgid "This file is incomplete and cannot be played."
 msgstr "此檔案不完整,無法播放。"
 
@@ -161,7 +166,7 @@ msgid "CoreAudio device could not be opened"
 msgstr "無法開啟 CoreAudio 裝置"
 
 msgid "Raspberry Pi Camera Module"
-msgstr ""
+msgstr "Raspberry Pi 相機模組"
 
 #, c-format
 msgid "Error reading %d bytes from device '%s'."
@@ -183,9 +188,8 @@ msgstr "'%s' 裝置的驅動程式不支援 IO 方法 %d"
 msgid "The driver of device '%s' does not support any known IO method."
 msgstr "'%s' 裝置的驅動程式不支援任何已知的 IO 方法。"
 
-#, fuzzy
 msgid "Invalid caps"
-msgstr "atom 大小無效。"
+msgstr "容量 (caps) 無效"
 
 #, c-format
 msgid "Device '%s' has no supported format"
@@ -209,15 +213,15 @@ msgstr "'%s' 裝置無法擷取為指定格式"
 
 #, c-format
 msgid "Device '%s' does support non-contiguous planes"
-msgstr ""
+msgstr "'%s' 裝置不支援非連續 plane"
 
 #, c-format
 msgid "Device '%s' does not support %s interlacing"
-msgstr "「%s」裝置不支援 %s 交錯方式"
+msgstr "'%s' 裝置不支援 %s 交錯方式"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Device '%s' does not support %s colorimetry"
-msgstr "「%s」裝置不支援 %s 色彩模式"
+msgstr "'%s' 裝置不支援 %s 比色法"
 
 #, c-format
 msgid "Could not get parameters on device '%s'"
@@ -289,10 +293,6 @@ msgstr "%s 裝置上的轉換工具沒有支援的輸出格式"
 msgid "Decoder on device %s has no supported input format"
 msgstr "%s 裝置上的解碼工具沒有支援的輸入格式"
 
-#, c-format
-msgid "Decoder on device %s has no supported output format"
-msgstr "%s 裝置上的解碼工具沒有支援的輸出格式"
-
 msgid "Failed to start decoding thread."
 msgstr "無法啟動解碼處理序。"
 
@@ -307,6 +307,10 @@ msgstr "%s 裝置上的編碼工具沒有支援的輸出格式"
 msgid "Encoder on device %s has no supported input format"
 msgstr "%s 裝置上的編碼工具沒有支援的輸入格式"
 
+#, fuzzy
+msgid "Failed to force keyframe."
+msgstr "無法處理影格。"
+
 msgid "Failed to start encoding thread."
 msgstr "無法啟動編碼處理序。"
 
@@ -386,30 +390,34 @@ msgstr "無法取得 '%2$s' 裝置上控制 %1$d 的值。"
 msgid "Failed to set value %d for control %d on device '%s'."
 msgstr "無法設定 '%3$s' 裝置上控制 %2$d 的值 %1$d。"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set value %s for control %d on device '%s'."
-msgstr "無法設定 '%3$s' 裝置上控制 %2$d 的值 %1$d。"
+msgstr "無法設定 '%3$s' 裝置上控制 %2$d 的值 %1$s。"
 
 #, c-format
 msgid "Failed to get current input on device '%s'. May be it is a radio device"
 msgstr "無法取得 '%s' 裝置上的目前輸入。或許此為廣播裝置"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set input %u on device %s."
-msgstr "無法設定 %2$s 裝置上的輸入 %1$d。"
+msgstr "無法設定 %2$s 裝置上的輸入 %1$u。"
 
 #, c-format
 msgid ""
 "Failed to get current output on device '%s'. May be it is a radio device"
 msgstr "無法取得 '%s' 裝置上的目前輸出。或許此為廣播裝置"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Failed to set output %u on device %s."
-msgstr "無法設定 %2$s 裝置的輸出 %1$d。"
+msgstr "無法設定 %2$s 裝置的輸出 %1$u。"
 
 msgid "Cannot operate without a clock"
 msgstr "無法在沒有時鐘 (clock) 的情況下動作"
 
+#, c-format
+#~ msgid "Decoder on device %s has no supported output format"
+#~ msgstr "%s 裝置上的解碼工具沒有支援的輸出格式"
+
 #~ msgid "Changing resolution at runtime is not yet supported."
 #~ msgstr "尚未支援在執行時變更解析度。"
 
diff --git a/scripts/gen-changelog.py b/scripts/gen-changelog.py
index 3924e6ea326892c78c56449274e6d19b2c4cb43b..44739f18cc069d1c14c85644deb131a068db13cc 100755
--- a/scripts/gen-changelog.py
+++ b/scripts/gen-changelog.py
@@ -29,7 +29,6 @@ changelog_starts = {
     'gst-editing-services': 'ee8bf88ebf131cf7c7161356540efc20bf411e14',
     'gst-python': 'b3e564eff577e2f577d795051bbcca85d47c89dc',
     'gstreamer-vaapi': 'c89e9afc5d43837c498a55f8f13ddf235442b83b',
-    'gst-omx': 'd2463b017f222e678978582544a9c9a80edfd330',
     'gst-devtools': 'da962d096af9460502843e41b7d25fdece7ff1c2',
     'gstreamer-sharp': 'b94528f8e7979df49fedf137dfa228d8fe475e1b',
 }
diff --git a/sys/oss/gstossaudio.c b/sys/oss/gstossaudio.c
index 2edf0c4ef9bce12a6c152aead7749741fc94f1dd..c6d2e163157d1ce6f3de82e27a47fd49abd79370 100644
--- a/sys/oss/gstossaudio.c
+++ b/sys/oss/gstossaudio.c
@@ -25,6 +25,7 @@
 
 #include "common.h"
 #include "gstossaudioelements.h"
+#include "gstossdeviceprovider.h"
 #include "gstosssink.h"
 #include "gstosssrc.h"
 
@@ -32,10 +33,10 @@
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  gboolean ret = FALSE;
+  GST_DEVICE_PROVIDER_REGISTER (ossdeviceprovider, plugin);
 
-  ret |= GST_ELEMENT_REGISTER (osssrc, plugin);
-  ret |= GST_ELEMENT_REGISTER (osssink, plugin);
+  GST_ELEMENT_REGISTER (osssrc, plugin);
+  GST_ELEMENT_REGISTER (osssink, plugin);
 
   return TRUE;
 }
diff --git a/sys/oss/gstossaudioelement.c b/sys/oss/gstossaudioelement.c
index 50e06a9ed03a8893fe6538a9cbfa6156fe93d68c..4cf5a63bbe3471e7fa9c3c3d9aaef659766d8da3 100644
--- a/sys/oss/gstossaudioelement.c
+++ b/sys/oss/gstossaudioelement.c
@@ -25,10 +25,14 @@
 
 #include "common.h"
 #include "gstossaudioelements.h"
+#include "gstossdeviceprovider.h"
 
 GST_DEBUG_CATEGORY (oss_debug);
 #define GST_CAT_DEFAULT oss_debug
 
+GST_DEVICE_PROVIDER_REGISTER_DEFINE (ossdeviceprovider, "ossdeviceprovider",
+    GST_RANK_SECONDARY, GST_TYPE_OSS_DEVICE_PROVIDER);
+
 void
 oss_element_init (GstPlugin * plugin)
 {
diff --git a/sys/oss/gstossaudioelements.h b/sys/oss/gstossaudioelements.h
index 4770a76c6e5051cf66891eab14c0a73b26e84548..67430b612baf3f7abec45c8c65378d021c86e821 100644
--- a/sys/oss/gstossaudioelements.h
+++ b/sys/oss/gstossaudioelements.h
@@ -34,6 +34,8 @@ G_GNUC_INTERNAL void oss_element_init (GstPlugin * plugin);
 GST_ELEMENT_REGISTER_DECLARE (osssink);
 GST_ELEMENT_REGISTER_DECLARE (osssrc);
 
+GST_DEVICE_PROVIDER_REGISTER_DECLARE(ossdeviceprovider);
+
 G_END_DECLS
 
 #endif /* __GST_OSS_ELEMENTS_H__ */
diff --git a/sys/oss/gstossdeviceprovider.c b/sys/oss/gstossdeviceprovider.c
new file mode 100644
index 0000000000000000000000000000000000000000..b8984d0e822e3a30989e943716fe11680047df6d
--- /dev/null
+++ b/sys/oss/gstossdeviceprovider.c
@@ -0,0 +1,303 @@
+/* GStreamer
+ * Copyright (C) 2023 Matthieu Volat <matthieu.volat@ensimag.fr>
+ *
+ * ossdeviceprovider.c: OSS device probing and monitoring
+ *
+ * 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 "gstossdeviceprovider.h"
+#include "gstosshelper.h"
+#include <glib/gstdio.h>
+#include <gst/gst.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+static GstDevice *gst_oss_device_new (const gchar * device_name, GstCaps * caps,
+    const gchar * device, GstOssDeviceType type);
+
+G_DEFINE_TYPE (GstOssDeviceProvider, gst_oss_device_provider,
+    GST_TYPE_DEVICE_PROVIDER);
+
+
+static GstDevice *
+add_device (GstDeviceProvider * provider, GstOssDeviceType type, gint devno)
+{
+  gchar devpath[64];
+  gchar mixpath[64];
+  gint fd;
+  GstCaps *caps;
+  gchar *name;
+  GstDevice *device;
+
+  snprintf (devpath, sizeof (devpath), "/dev/dsp%u", devno);
+  snprintf (mixpath, sizeof (mixpath), "/dev/mixer%u", devno);
+
+  switch (type) {
+    case GST_OSS_DEVICE_TYPE_SOURCE:
+      fd = open (devpath, O_RDONLY);
+      break;
+    case GST_OSS_DEVICE_TYPE_SINK:
+      fd = open (devpath, O_WRONLY);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+  if (fd == -1) {
+    GST_WARNING_OBJECT (provider, "Could open device %s for introspection",
+        devpath);
+    return NULL;
+  }
+  caps = gst_oss_helper_probe_caps (fd);
+  close (fd);
+  name = gst_oss_helper_get_card_name (mixpath);
+
+  device = gst_oss_device_new (name, caps, devpath, type);
+  g_free (name);
+  return device;
+}
+
+static GList *
+gst_oss_device_provider_probe (GstDeviceProvider * provider)
+{
+  FILE *sndstat_handle;
+  gchar *sndstat_line = NULL;
+  size_t sndstat_line_len = 0;
+  gint sndstat_device_section = 0;
+  gint ossdevno;
+  gboolean play, rec;
+  GstDevice *device;
+  GList *list = NULL;
+
+  GST_INFO_OBJECT (provider, "Probing OSS devices");
+  if (((sndstat_handle = fopen ("/dev/sndstat", "r")) == NULL)
+      && ((sndstat_handle = fopen ("/proc/sndstat", "r")) == NULL)
+      && ((sndstat_handle = fopen ("/proc/asound/sndstat", "r")) == NULL)) {
+    /* Cannot evaluate OSS devices without this file */
+    GST_WARNING_OBJECT (provider, "No sndstat file found");
+    goto beach;
+  }
+
+  while (!feof (sndstat_handle)) {
+    if (getline (&sndstat_line, &sndstat_line_len, sndstat_handle) == -1) {
+      break;
+    }
+    g_strstrip (sndstat_line);
+
+    if (!sndstat_device_section) {
+      sndstat_device_section = g_str_equal (sndstat_line, "Audio devices:")
+          || g_str_equal (sndstat_line, "Installed devices:")
+          || g_str_equal (sndstat_line, "Installed devices from userspace:");
+      continue;
+    }
+
+    if ((sscanf (sndstat_line, "pcm%u:", &ossdevno) == 1)
+        || (sscanf (sndstat_line, "%u:", &ossdevno) == 1)) {
+      /* At least on FreeBSD, these keywords can be different if hw.snd.verbose is not 0 */
+      if (strstr (sndstat_line, "(play/rec)") != NULL) {
+        play = rec = TRUE;
+      } else if (strstr (sndstat_line, "(play)") != NULL) {
+        play = TRUE, rec = FALSE;
+      } else if (strstr (sndstat_line, "(rec)") != NULL) {
+        play = FALSE, rec = TRUE;
+      } else {
+        play = rec = FALSE;     /* Or should we assume play/rec? */
+      }
+
+      if (play) {
+        device = add_device (provider, GST_OSS_DEVICE_TYPE_SINK, ossdevno);
+        if (device != NULL) {
+          list = g_list_append (list, device);
+        }
+      }
+      if (rec) {
+        device = add_device (provider, GST_OSS_DEVICE_TYPE_SOURCE, ossdevno);
+        if (device != NULL) {
+          list = g_list_append (list, device);
+        }
+      }
+    }
+  }
+
+  free (sndstat_line);
+  fclose (sndstat_handle);
+
+beach:
+  return list;
+}
+
+
+static void
+gst_oss_device_provider_class_init (GstOssDeviceProviderClass * klass)
+{
+  GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
+
+  dm_class->probe = gst_oss_device_provider_probe;
+
+  gst_device_provider_class_set_static_metadata (dm_class,
+      "OSS Device Provider", "Sink/Source/Audio",
+      "List and provides OSS source and sink devices",
+      "Matthieu Volat <matthieu.volat@ensimag.fr>");
+}
+
+static void
+gst_oss_device_provider_init (GstOssDeviceProvider * self)
+{
+}
+
+/*** GstOssDevice implementation ******/
+enum
+{
+  PROP_DEVICE_PATH = 1,
+};
+
+
+G_DEFINE_TYPE (GstOssDevice, gst_oss_device, GST_TYPE_DEVICE);
+
+static GstElement *
+gst_oss_device_create_element (GstDevice * device, const gchar * name)
+{
+  GstOssDevice *oss_dev = GST_OSS_DEVICE (device);
+  GstElement *elem;
+
+  elem = gst_element_factory_make (oss_dev->element, name);
+  g_object_set (elem, "device", oss_dev->device_path, NULL);
+
+  return elem;
+}
+
+static gboolean
+gst_oss_device_reconfigure_element (GstDevice * device, GstElement * element)
+{
+  GstOssDevice *oss_dev = GST_OSS_DEVICE (device);
+
+  g_object_set (element, "device", oss_dev->device_path, NULL);
+
+  return TRUE;
+}
+
+static void
+gst_oss_device_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstOssDevice *device;
+
+  device = GST_OSS_DEVICE_CAST (object);
+
+  switch (prop_id) {
+    case PROP_DEVICE_PATH:
+      g_value_set_string (value, device->device_path);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_oss_device_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstOssDevice *device;
+
+  device = GST_OSS_DEVICE_CAST (object);
+
+  switch (prop_id) {
+    case PROP_DEVICE_PATH:
+      device->device_path = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_oss_device_finalize (GObject * object)
+{
+  GstOssDevice *device = GST_OSS_DEVICE (object);
+
+  g_free (device->device_path);
+
+  G_OBJECT_CLASS (gst_oss_device_parent_class)->finalize (object);
+}
+
+static void
+gst_oss_device_class_init (GstOssDeviceClass * klass)
+{
+  GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  dev_class->create_element = gst_oss_device_create_element;
+  dev_class->reconfigure_element = gst_oss_device_reconfigure_element;
+
+  object_class->get_property = gst_oss_device_get_property;
+  object_class->set_property = gst_oss_device_set_property;
+  object_class->finalize = gst_oss_device_finalize;
+
+  g_object_class_install_property (object_class, PROP_DEVICE_PATH,
+      g_param_spec_string ("device-path", "OSS device path",
+          "The path of the OSS device", "",
+          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gst_oss_device_init (GstOssDevice * device)
+{
+}
+
+static GstDevice *
+gst_oss_device_new (const gchar * device_name,
+    GstCaps * caps, const gchar * device_path, GstOssDeviceType type)
+{
+  GstOssDevice *gstdev;
+  const gchar *element = NULL;
+  const gchar *klass = NULL;
+
+  g_return_val_if_fail (device_name, NULL);
+  g_return_val_if_fail (device_path, NULL);
+  g_return_val_if_fail (caps, NULL);
+
+  switch (type) {
+    case GST_OSS_DEVICE_TYPE_SOURCE:
+      element = "osssrc";
+      klass = "Audio/Source";
+      break;
+    case GST_OSS_DEVICE_TYPE_SINK:
+      element = "osssink";
+      klass = "Audio/Sink";
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  gstdev = g_object_new (GST_TYPE_OSS_DEVICE,
+      "display-name", device_name, "caps", caps, "device-class", klass,
+      "device-path", device_path, NULL);
+
+  gstdev->element = element;
+
+  gst_caps_unref (caps);
+
+  return GST_DEVICE (gstdev);
+}
diff --git a/sys/oss/gstossdeviceprovider.h b/sys/oss/gstossdeviceprovider.h
new file mode 100644
index 0000000000000000000000000000000000000000..59ef0f26df3f9392891c78d7043b501b87e85d9f
--- /dev/null
+++ b/sys/oss/gstossdeviceprovider.h
@@ -0,0 +1,88 @@
+/* GStreamer
+ * Copyright (C) 2023 Matthieu Volat <mathieu.volat@ensimag.fr>
+ *
+ * ossdeviceprovider.c: OSS device probing and monitoring
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_OSS_DEVICE_PROVIDER_H__
+#define __GST_OSS_DEVICE_PROVIDER_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstOssDeviceProvider GstOssDeviceProvider;
+typedef struct _GstOssDeviceProviderClass GstOssDeviceProviderClass;
+
+#define GST_TYPE_OSS_DEVICE_PROVIDER                 (gst_oss_device_provider_get_type())
+#define GST_IS_OSS_DEVICE_PROVIDER(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OSS_DEVICE_PROVIDER))
+#define GST_IS_OSS_DEVICE_PROVIDER_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_OSS_DEVICE_PROVIDER))
+#define GST_OSS_DEVICE_PROVIDER_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OSS_DEVICE_PROVIDER, GstOssDeviceProviderClass))
+#define GST_OSS_DEVICE_PROVIDER(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OSS_DEVICE_PROVIDER, GstOssDeviceProvider))
+#define GST_OSS_DEVICE_PROVIDER_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstOssDeviceProviderClass))
+#define GST_OSS_DEVICE_PROVIDER_CAST(obj)            ((GstOssDeviceProvider *)(obj))
+
+struct _GstOssDeviceProvider {
+  GstDeviceProvider         parent;
+};
+
+struct _GstOssDeviceProviderClass {
+  GstDeviceProviderClass    parent_class;
+};
+
+GType        gst_oss_device_provider_get_type (void);
+
+
+typedef struct _GstOssDevice GstOssDevice;
+typedef struct _GstOssDeviceClass GstOssDeviceClass;
+
+#define GST_TYPE_OSS_DEVICE                 (gst_oss_device_get_type())
+#define GST_IS_OSS_DEVICE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OSS_DEVICE))
+#define GST_IS_OSS_DEVICE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_OSS_DEVICE))
+#define GST_OSS_DEVICE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OSS_DEVICE, GstOssDeviceClass))
+#define GST_OSS_DEVICE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OSS_DEVICE, GstOssDevice))
+#define GST_OSS_DEVICE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstOssDeviceClass))
+#define GST_OSS_DEVICE_CAST(obj)            ((GstOssDevice *)(obj))
+
+typedef enum
+{
+  GST_OSS_DEVICE_TYPE_INVALID = 0,
+  GST_OSS_DEVICE_TYPE_SOURCE,
+  GST_OSS_DEVICE_TYPE_SINK
+} GstOssDeviceType;
+
+struct _GstOssDevice {
+  GstDevice         parent;
+
+  gchar            *device_path;
+  const gchar      *element;
+};
+
+struct _GstOssDeviceClass {
+  GstDeviceClass    parent_class;
+};
+
+GType        gst_oss_device_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_OSS_DEVICE_PROVIDER_H__ */
diff --git a/sys/oss/gstosshelper.c b/sys/oss/gstosshelper.c
index 8290441e7190651825fa832a535dd8ca9589cff5..ff2da7578bcb32b96245d22ce8eb5516b7f3629a 100644
--- a/sys/oss/gstosshelper.c
+++ b/sys/oss/gstosshelper.c
@@ -202,7 +202,6 @@ gst_oss_helper_rate_probe_check (GstOssProbe * probe)
   GQueue *ranges;
   int exact_rates = 0;
   gboolean checking_exact_rates = TRUE;
-  int n_checks = 0;
   gboolean result = TRUE;
 
   ranges = g_queue_new ();
@@ -210,7 +209,6 @@ gst_oss_helper_rate_probe_check (GstOssProbe * probe)
   probe->rates = g_array_new (FALSE, FALSE, sizeof (int));
 
   probe->min = gst_oss_helper_rate_check_rate (probe, 1000);
-  n_checks++;
   probe->max = gst_oss_helper_rate_check_rate (probe, 100000);
   /* a little bug workaround */
   {
@@ -223,7 +221,6 @@ gst_oss_helper_rate_probe_check (GstOssProbe * probe)
       probe->max = max;
     }
   }
-  n_checks++;
   if (probe->min == -1 || probe->max == -1) {
     /* This is a workaround for drivers that return -EINVAL (or another
      * error) for rates outside of [8000,48000].  If this fails, the
@@ -252,7 +249,6 @@ gst_oss_helper_rate_probe_check (GstOssProbe * probe)
       /* FIXME ioctl returned an error.  do something */
       GST_DEBUG ("unexpected check_rate error");
     }
-    n_checks++;
 
     if (mid == mid_ret && checking_exact_rates) {
       int max_exact_matches = 20;
diff --git a/sys/oss/meson.build b/sys/oss/meson.build
index 51cc4d34c684ce451285a705dcb702db665a91ab..b91b5c02b2668fa1baaa9985ab7510084a5fde8b 100644
--- a/sys/oss/meson.build
+++ b/sys/oss/meson.build
@@ -26,7 +26,7 @@ endif
 
 if have_oss
   plugins += [library('gstossaudio',
-    'gstossaudio.c', 'gstossaudioelement.c', 'gstosshelper.c', 'gstosssink.c', 'gstosssrc.c',
+    'gstossaudio.c', 'gstossaudioelement.c', 'gstossdeviceprovider.c', 'gstosshelper.c', 'gstosssink.c', 'gstosssrc.c',
     c_args : gst_plugins_good_args,
     include_directories : [configinc, libsinc],
     dependencies : [gstaudio_dep, gstbase_dep],
diff --git a/sys/oss4/oss4-sink.c b/sys/oss4/oss4-sink.c
index 506bcf17706780615da459e7f5184165692f0bd0..ab0c67269a6bc644a361384402a84c1b3989c650 100644
--- a/sys/oss4/oss4-sink.c
+++ b/sys/oss4/oss4-sink.c
@@ -477,7 +477,7 @@ gst_oss4_sink_open (GstAudioSink * asink, gboolean silent_errors)
 
     if (ioctl (oss->fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &routings) != -1) {
       GST_LOG_OBJECT (oss, "%u output routings (static list: %d)",
-          routings.nvalues, ! !(routings.version == 0));
+          routings.nvalues, !!(routings.version == 0));
       for (i = 0; i < routings.nvalues; ++i) {
         GST_LOG_OBJECT (oss, "  output routing %d: %s", i,
             &routings.strings[routings.strindex[i]]);
diff --git a/sys/osxaudio/gstosxaudio.c b/sys/osxaudio/gstosxaudio.c
index 99dfdbf8ddc9e96d91499a786978410f4d0b991c..dc3758e4553eba274ee54893505f6ac5e319d268 100644
--- a/sys/osxaudio/gstosxaudio.c
+++ b/sys/osxaudio/gstosxaudio.c
@@ -37,21 +37,15 @@
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  if (!gst_element_register (plugin, "osxaudiosink", GST_RANK_PRIMARY,
-          GST_TYPE_OSX_AUDIO_SINK)) {
-    return FALSE;
-  }
-  if (!gst_element_register (plugin, "osxaudiosrc", GST_RANK_PRIMARY,
-          GST_TYPE_OSX_AUDIO_SRC)) {
-    return FALSE;
-  }
+  gboolean ret = FALSE;
+
+  ret |= GST_ELEMENT_REGISTER (osxaudiosrc, plugin);
+  ret |= GST_ELEMENT_REGISTER (osxaudiosink, plugin);
 #ifndef HAVE_IOS
-  if (!gst_device_provider_register (plugin, "osxaudiodeviceprovider",
-          GST_RANK_PRIMARY, GST_TYPE_OSX_AUDIO_DEVICE_PROVIDER))
-    return FALSE;
+  ret |= GST_DEVICE_PROVIDER_REGISTER (osxaudiodeviceprovider, plugin);
 #endif
 
-  return TRUE;
+  return ret;
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
diff --git a/sys/osxaudio/gstosxaudiodeviceprovider.c b/sys/osxaudio/gstosxaudiodeviceprovider.c
index 4870fb1a07c37664648f2011b555955fbe7a247f..838aa974a23771eb9a1043139966e6da87aa0d4d 100644
--- a/sys/osxaudio/gstosxaudiodeviceprovider.c
+++ b/sys/osxaudio/gstosxaudiodeviceprovider.c
@@ -29,6 +29,9 @@
 #include "gstosxaudiosink.h"
 #include "gstosxaudiodeviceprovider.h"
 
+GST_DEBUG_CATEGORY_STATIC (gst_osxaudio_device_debug);
+#define GST_CAT_DEFAULT gst_osxaudio_device_debug
+
 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < 120000
 #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
 #endif
@@ -51,9 +54,17 @@ static GstOsxAudioDevice *gst_osx_audio_device_new (AudioDeviceID device_id,
 
 G_DEFINE_TYPE (GstOsxAudioDeviceProvider, gst_osx_audio_device_provider,
     GST_TYPE_DEVICE_PROVIDER);
+GST_DEVICE_PROVIDER_REGISTER_DEFINE (osxaudiodeviceprovider,
+    "osxaudiodeviceprovider", GST_RANK_PRIMARY,
+    GST_TYPE_OSX_AUDIO_DEVICE_PROVIDER);
 
+static gboolean gst_osx_audio_device_provider_start (GstDeviceProvider *
+    provider);
+static void gst_osx_audio_device_provider_stop (GstDeviceProvider * provider);
 static GList *gst_osx_audio_device_provider_probe (GstDeviceProvider *
     provider);
+static void
+gst_osx_audio_device_provider_update_devices (GstOsxAudioDeviceProvider * self);
 
 static void
 gst_osx_audio_device_provider_class_init (GstOsxAudioDeviceProviderClass *
@@ -61,12 +72,17 @@ gst_osx_audio_device_provider_class_init (GstOsxAudioDeviceProviderClass *
 {
   GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
 
+  dm_class->start = gst_osx_audio_device_provider_start;
+  dm_class->stop = gst_osx_audio_device_provider_stop;
   dm_class->probe = gst_osx_audio_device_provider_probe;
 
   gst_device_provider_class_set_static_metadata (dm_class,
       "OSX Audio Device Provider", "Source/Sink/Audio",
       "List and monitor OSX audio source and sink devices",
       "Hyunjun Ko <zzoon@igalia.com>");
+
+  GST_DEBUG_CATEGORY_INIT (gst_osxaudio_device_debug, "osxaudio_device", 0,
+      "Audio device provider for macOS");
 }
 
 static void
@@ -231,12 +247,69 @@ _audio_system_get_devices (gint * ndevices)
   return devices;
 }
 
+static OSStatus
+_audio_devices_changed_cb (AudioObjectID inObjectID, UInt32 inNumberAddresses,
+    const AudioObjectPropertyAddress * inAddresses,
+    void *__nullable inClientData)
+{
+  GstOsxAudioDeviceProvider *self =
+      GST_OSX_AUDIO_DEVICE_PROVIDER (inClientData);
+
+  GST_DEBUG ("Audio devices changed");
+  gst_osx_audio_device_provider_update_devices (self);
+
+  return noErr;
+}
+
+static AudioObjectPropertyAddress
+_get_devices_list_address ()
+{
+  AudioObjectPropertyAddress address = {
+    .mSelector = kAudioHardwarePropertyDevices,
+    .mScope = kAudioObjectPropertyScopeGlobal,
+    .mElement = kAudioObjectPropertyElementMain
+  };
+
+  return address;
+}
+
+static gboolean
+_start_audio_device_watcher (GstOsxAudioDeviceProvider * self)
+{
+  AudioObjectPropertyAddress propertyAddress = _get_devices_list_address ();
+  OSStatus result = AudioObjectAddPropertyListener (kAudioObjectSystemObject,
+      &propertyAddress, _audio_devices_changed_cb, self);
+
+  if (result != noErr) {
+    GST_WARNING ("Failed to add device list change listener: %d", result);
+    return FALSE;
+  }
+
+  GST_DEBUG ("Audio device watcher started");
+  return TRUE;
+}
+
+static gboolean
+_stop_audio_device_watcher (GstOsxAudioDeviceProvider * self)
+{
+  AudioObjectPropertyAddress propertyAddress = _get_devices_list_address ();
+  OSStatus result = AudioObjectRemovePropertyListener (kAudioObjectSystemObject,
+      &propertyAddress, _audio_devices_changed_cb, self);
+
+  if (result != noErr) {
+    GST_WARNING ("Failed to remove device list change listener: %d", result);
+    return FALSE;
+  }
+
+  GST_DEBUG ("Audio device watcher stopped");
+  return TRUE;
+}
+
 static void
 gst_osx_audio_device_provider_probe_internal (GstOsxAudioDeviceProvider * self,
     gboolean is_src, AudioDeviceID * osx_devices, gint ndevices,
     GList ** devices)
 {
-
   gint i = 0;
   GstOsxAudioDeviceType type = GST_OSX_AUDIO_DEVICE_TYPE_INVALID;
   GstOsxAudioDevice *device = NULL;
@@ -309,6 +382,122 @@ done:
   return devices;
 }
 
+static gboolean
+gst_osx_audio_device_provider_start (GstDeviceProvider * provider)
+{
+  GstOsxAudioDeviceProvider *self = GST_OSX_AUDIO_DEVICE_PROVIDER (provider);
+  GList *devices = NULL;
+  GList *iter;
+
+  devices = gst_osx_audio_device_provider_probe (provider);
+  if (devices) {
+    for (iter = devices; iter; iter = g_list_next (iter)) {
+      gst_device_provider_device_add (provider, GST_DEVICE (iter->data));
+    }
+
+    g_list_free (devices);
+  }
+
+  return _start_audio_device_watcher (self);
+}
+
+static void
+gst_osx_audio_device_provider_stop (GstDeviceProvider * provider)
+{
+  GstOsxAudioDeviceProvider *self = GST_OSX_AUDIO_DEVICE_PROVIDER (provider);
+  _stop_audio_device_watcher (self);
+}
+
+static gboolean
+gst_osx_audio_device_is_in_list (GList * list, GstDevice * gst_device)
+{
+  GstOsxAudioDevice *osx_device = GST_OSX_AUDIO_DEVICE (gst_device);
+  gchar *name = gst_device_get_display_name (gst_device);
+  GList *iter;
+  gboolean found = FALSE;
+
+  for (iter = list; iter; iter = g_list_next (iter)) {
+    GstOsxAudioDevice *other_osx = GST_OSX_AUDIO_DEVICE (iter->data);
+    gchar *other_name = gst_device_get_display_name (GST_DEVICE (iter->data));
+
+    /* Only checking ID + class for now.
+     * Should be enough to pick up changes when an existing output device
+     * adds an input or vice versa */
+    if (g_ascii_strcasecmp (name, other_name) == 0
+        && osx_device->device_id == other_osx->device_id) {
+      found = TRUE;
+    }
+
+    g_free (other_name);
+
+    if (found)
+      break;
+  }
+
+  g_free (name);
+  return found;
+}
+
+static void
+gst_osx_audio_device_provider_update_devices (GstOsxAudioDeviceProvider * self)
+{
+  GstDeviceProvider *provider = GST_DEVICE_PROVIDER_CAST (self);
+  GList *prev_devices = NULL;
+  GList *new_devices = NULL;
+  GList *to_add = NULL;
+  GList *to_remove = NULL;
+  GList *iter;
+
+  GST_OBJECT_LOCK (self);
+  prev_devices = g_list_copy_deep (provider->devices,
+      (GCopyFunc) gst_object_ref, NULL);
+  GST_OBJECT_UNLOCK (self);
+
+  new_devices = gst_osx_audio_device_provider_probe (provider);
+  if (!new_devices)
+    goto done;
+
+  /* Ownership of GstDevice for gst_device_provider_device_add()
+   * and gst_device_provider_device_remove() is a bit complicated.
+   * Remove floating reference here for things to be clear */
+  for (iter = new_devices; iter; iter = g_list_next (iter))
+    gst_object_ref_sink (iter->data);
+
+  /* Check added devices */
+  for (iter = new_devices; iter; iter = g_list_next (iter)) {
+    if (!gst_osx_audio_device_is_in_list
+        (prev_devices, GST_DEVICE (iter->data))) {
+      to_add = g_list_prepend (to_add, gst_object_ref (iter->data));
+    }
+  }
+
+  /* Check removed devices */
+  for (iter = prev_devices; iter; iter = g_list_next (iter)) {
+    if (!gst_osx_audio_device_is_in_list (new_devices, GST_DEVICE (iter->data))) {
+      to_remove = g_list_prepend (to_remove, gst_object_ref (iter->data));
+    }
+  }
+
+  for (iter = to_remove; iter; iter = g_list_next (iter))
+    gst_device_provider_device_remove (provider, GST_DEVICE (iter->data));
+
+  for (iter = to_add; iter; iter = g_list_next (iter))
+    gst_device_provider_device_add (provider, GST_DEVICE (iter->data));
+
+done:
+  if (prev_devices)
+    g_list_free_full (prev_devices, (GDestroyNotify) gst_object_unref);
+
+  if (to_add)
+    g_list_free_full (to_add, (GDestroyNotify) gst_object_unref);
+
+  if (to_remove)
+    g_list_free_full (to_remove, (GDestroyNotify) gst_object_unref);
+
+  if (new_devices)
+    g_list_free (new_devices);
+}
+
 enum
 {
   PROP_DEVICE_ID = 1,
diff --git a/sys/osxaudio/gstosxaudiodeviceprovider.h b/sys/osxaudio/gstosxaudiodeviceprovider.h
index bd54178ee8a7f9438a9e32b61d02714f153255b4..1f6c2bd311cc829aff61599e80629177e71867bf 100644
--- a/sys/osxaudio/gstosxaudiodeviceprovider.h
+++ b/sys/osxaudio/gstosxaudiodeviceprovider.h
@@ -87,5 +87,7 @@ struct _GstOsxAudioDeviceClass
 
 GType gst_osx_audio_device_get_type (void);
 
+GST_DEVICE_PROVIDER_REGISTER_DECLARE(osxaudiodeviceprovider);
+
 G_END_DECLS
 #endif /* __GST_OSX_AUDIO_DEIVCE_PROVIDER_H__ */
diff --git a/sys/osxaudio/gstosxaudioringbuffer.c b/sys/osxaudio/gstosxaudioringbuffer.c
index 32eb120fd5280f69552e91b2859a987dc3ab970b..5a8e58214dc48cd9b713b77627bfb6575f8e86cb 100644
--- a/sys/osxaudio/gstosxaudioringbuffer.c
+++ b/sys/osxaudio/gstosxaudioringbuffer.c
@@ -174,7 +174,8 @@ gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
 {
   gboolean ret = FALSE, is_passthrough = FALSE;
   GstOsxAudioRingBuffer *osxbuf;
-  AudioStreamBasicDescription format;
+  AudioStreamBasicDescription format = { 0 };
+  guint32 frames_per_packet = 0;
 
   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
 
@@ -225,6 +226,8 @@ gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
         (spec->latency_time * GST_AUDIO_INFO_RATE (&spec->info) /
         G_USEC_PER_SEC) * GST_AUDIO_INFO_BPF (&spec->info);
     spec->segtotal = spec->buffer_time / spec->latency_time;
+    frames_per_packet = spec->segsize / GST_AUDIO_INFO_BPF (&spec->info);
+
     is_passthrough = FALSE;
   }
 
@@ -239,7 +242,7 @@ gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
   buf->memory = g_malloc0 (buf->size);
 
   ret = gst_core_audio_initialize (osxbuf->core_audio, format, spec->caps,
-      is_passthrough);
+      frames_per_packet, is_passthrough);
 
   if (!ret) {
     g_free (buf->memory);
diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c
index e4211edc9c0da9015e88e02a5d15c5adc95fdea5..ac9183521c1799d21f6875fbd7f667d77459ffb1 100644
--- a/sys/osxaudio/gstosxaudiosink.c
+++ b/sys/osxaudio/gstosxaudiosink.c
@@ -148,6 +148,8 @@ gst_osx_audio_sink_do_init (GType type)
 #define gst_osx_audio_sink_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstOsxAudioSink, gst_osx_audio_sink,
     GST_TYPE_AUDIO_BASE_SINK, gst_osx_audio_sink_do_init (g_define_type_id));
+GST_ELEMENT_REGISTER_DEFINE (osxaudiosink, "osxaudiosink", GST_RANK_PRIMARY,
+    GST_TYPE_OSX_AUDIO_SINK);
 
 static void
 gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
@@ -237,6 +239,11 @@ gst_osx_audio_sink_change_state (GstElement * element,
   GstStateChangeReturn ret;
 
   switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      GST_OBJECT_LOCK (osxsink);
+      osxsink->device_id = kAudioDeviceUnknown;
+      GST_OBJECT_UNLOCK (osxsink);
+      break;
     default:
       break;
   }
@@ -254,7 +261,9 @@ gst_osx_audio_sink_change_state (GstElement * element,
       ringbuffer =
           GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SINK (osxsink)->ringbuffer);
       if (ringbuffer->core_audio->device_id != osxsink->device_id) {
+        GST_OBJECT_LOCK (osxsink);
         osxsink->device_id = ringbuffer->core_audio->device_id;
+        GST_OBJECT_UNLOCK (osxsink);
         g_object_notify (G_OBJECT (osxsink), "device");
       }
       break;
@@ -544,7 +553,13 @@ gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf,
       gst_audio_ring_buffer_clear (GST_AUDIO_RING_BUFFER (buf), readseg);
 
       /* we wrote one segment */
+      CORE_AUDIO_TIMING_LOCK (buf->core_audio);
       gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);
+      /* FIXME: Update the timestamp and reported frames in smaller increments
+       * when the segment size is larger than the total inNumberFrames */
+      gst_core_audio_update_timing (buf->core_audio, inTimeStamp,
+          inNumberFrames);
+      CORE_AUDIO_TIMING_UNLOCK (buf->core_audio);
 
       buf->segoffset = 0;
     }
diff --git a/sys/osxaudio/gstosxaudiosink.h b/sys/osxaudio/gstosxaudiosink.h
index 2f55c4d2e6e81006b1c596fb2103833f280b9b8d..a15b0ec3c307d52bd8f534fe6d239949733f4a2c 100644
--- a/sys/osxaudio/gstosxaudiosink.h
+++ b/sys/osxaudio/gstosxaudiosink.h
@@ -96,6 +96,8 @@ struct _GstOsxAudioSinkClass
 
 GType gst_osx_audio_sink_get_type (void);
 
+GST_ELEMENT_REGISTER_DECLARE (osxaudiosink);
+
 G_END_DECLS
 
 #endif /* __GST_OSXAUDIOSINK_H__ */
diff --git a/sys/osxaudio/gstosxaudiosrc.c b/sys/osxaudio/gstosxaudiosrc.c
index 0ae5e9ce62b7c4511cd7a4c34e0ea493e3a7fb7c..adfc721a9551c3dbaa44078ce5fd3ea523b41b1d 100644
--- a/sys/osxaudio/gstosxaudiosrc.c
+++ b/sys/osxaudio/gstosxaudiosrc.c
@@ -123,6 +123,8 @@ gst_osx_audio_src_do_init (GType type)
 #define gst_osx_audio_src_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstOsxAudioSrc, gst_osx_audio_src,
     GST_TYPE_AUDIO_BASE_SRC, gst_osx_audio_src_do_init (g_define_type_id));
+GST_ELEMENT_REGISTER_DEFINE (osxaudiosrc, "osxaudiosrc", GST_RANK_PRIMARY,
+    GST_TYPE_OSX_AUDIO_SRC);
 
 static void
 gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass)
@@ -208,6 +210,19 @@ gst_osx_audio_src_change_state (GstElement * element, GstStateChange transition)
   GstStateChangeReturn ret;
 
   switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_NULL:{
+      GST_OBJECT_LOCK (osxsrc);
+      osxsrc->device_id = kAudioDeviceUnknown;
+      GST_OBJECT_UNLOCK (osxsrc);
+      break;
+    }
+#ifdef HAVE_IOS
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      ringbuffer =
+          GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SRC (osxsrc)->ringbuffer);
+      ringbuffer->core_audio->first_sample_time = -1;
+      break;
+#endif
     default:
       break;
   }
@@ -222,7 +237,10 @@ gst_osx_audio_src_change_state (GstElement * element, GstStateChange transition)
       ringbuffer =
           GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SRC (osxsrc)->ringbuffer);
       if (ringbuffer->core_audio->device_id != osxsrc->device_id) {
+        GST_OBJECT_LOCK (osxsrc);
         osxsrc->device_id = ringbuffer->core_audio->device_id;
+        GST_OBJECT_UNLOCK (osxsrc);
+
         g_object_notify (G_OBJECT (osxsrc), "device");
       }
       break;
@@ -339,6 +357,12 @@ gst_osx_audio_src_io_proc (GstOsxAudioRingBuffer * buf,
   gint remaining;
   UInt32 n;
   gint offset = 0;
+  guint64 sample_position;
+  GstAudioRingBufferSpec *spec = &GST_AUDIO_RING_BUFFER (buf)->spec;
+  guint bpf = GST_AUDIO_INFO_BPF (&spec->info);
+
+  GST_LOG_OBJECT (buf, "in sample position %f frames %u",
+      inTimeStamp->mSampleTime, inNumberFrames);
 
   /* Previous invoke of AudioUnitRender changed mDataByteSize into
    * number of bytes actually read. Reset the members. */
@@ -359,6 +383,18 @@ gst_osx_audio_src_io_proc (GstOsxAudioRingBuffer * buf,
    *       not just the first one. */
 
   remaining = buf->core_audio->recBufferList->mBuffers[0].mDataByteSize;
+  sample_position = inTimeStamp->mSampleTime;
+
+#ifdef HAVE_IOS
+  /* Timestamps don't always start from 0 on iOS, have to offset */
+  if (buf->core_audio->first_sample_time == -1) {
+    GST_ERROR ("Setting first CoreAudio timestamp to %f",
+        inTimeStamp->mSampleTime);
+    buf->core_audio->first_sample_time = inTimeStamp->mSampleTime;
+  }
+
+  sample_position -= buf->core_audio->first_sample_time;
+#endif
 
   while (remaining) {
     if (!gst_audio_ring_buffer_prepare_read (GST_AUDIO_RING_BUFFER (buf),
@@ -377,10 +413,24 @@ gst_osx_audio_src_io_proc (GstOsxAudioRingBuffer * buf,
     buf->segoffset += len;
     offset += len;
     remaining -= len;
+    sample_position += len / bpf;
 
     if ((gint) buf->segoffset == GST_AUDIO_RING_BUFFER (buf)->spec.segsize) {
+      /* Calculate the timestamp corresponding to the first sample in the segment */
+      guint64 seg_sample_pos = sample_position - (spec->segsize / bpf);
+      GstClockTime ts = gst_util_uint64_scale_int (seg_sample_pos, GST_SECOND,
+          GST_AUDIO_INFO_RATE (&spec->info));
+      gst_audio_ring_buffer_set_timestamp (GST_AUDIO_RING_BUFFER (buf),
+          writeseg, ts);
+
       /* we wrote one segment */
+      CORE_AUDIO_TIMING_LOCK (buf->core_audio);
       gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);
+      /* FIXME: Update the timestamp and reported frames in smaller increments
+       * when the segment size is larger than the total inNumberFrames */
+      gst_core_audio_update_timing (buf->core_audio, inTimeStamp,
+          inNumberFrames);
+      CORE_AUDIO_TIMING_UNLOCK (buf->core_audio);
 
       buf->segoffset = 0;
     }
diff --git a/sys/osxaudio/gstosxaudiosrc.h b/sys/osxaudio/gstosxaudiosrc.h
index 9d825b028621ceac397b7fac55e7a48749fe1d9e..980cf91722788ec308393696552b40f7e1d9e091 100644
--- a/sys/osxaudio/gstosxaudiosrc.h
+++ b/sys/osxaudio/gstosxaudiosrc.h
@@ -82,6 +82,8 @@ struct _GstOsxAudioSrcClass
 
 GType gst_osx_audio_src_get_type (void);
 
+GST_ELEMENT_REGISTER_DECLARE (osxaudiosrc);
+
 G_END_DECLS
 
 #endif /* __GST_OSXAUDIOSRC_H__ */
diff --git a/sys/osxaudio/gstosxcoreaudio.c b/sys/osxaudio/gstosxcoreaudio.c
index 08a52e4f33956682f73e5f94d5df31d2466fb466..3d6a88a63be37f0bfefe1752df80d031250abb47 100644
--- a/sys/osxaudio/gstosxcoreaudio.c
+++ b/sys/osxaudio/gstosxcoreaudio.c
@@ -24,8 +24,8 @@
 #include "gstosxcoreaudio.h"
 #include "gstosxcoreaudiocommon.h"
 
-GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
-#define GST_CAT_DEFAULT osx_audio_debug
+GST_DEBUG_CATEGORY (osx_coreaudio_debug);
+#define GST_CAT_DEFAULT osx_coreaudio_debug
 
 G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
 
@@ -35,10 +35,20 @@ G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
 #include "gstosxcoreaudiohal.c"
 #endif
 
+static void
+gst_core_audio_finalize (GObject * object)
+{
+  GstCoreAudio *core_audio = GST_CORE_AUDIO (object);
+  g_mutex_clear (&core_audio->timing_lock);
+
+  G_OBJECT_CLASS (gst_core_audio_parent_class)->finalize (object);
+}
 
 static void
 gst_core_audio_class_init (GstCoreAudioClass * klass)
 {
+  GObjectClass *object_klass = G_OBJECT_CLASS (klass);
+  object_klass->finalize = gst_core_audio_finalize;
 }
 
 static void
@@ -54,6 +64,9 @@ gst_core_audio_init (GstCoreAudio * core_audio)
   core_audio->hog_pid = -1;
   core_audio->disabled_mixing = FALSE;
 #endif
+
+  mach_timebase_info (&core_audio->timebase);
+  g_mutex_init (&core_audio->timing_lock);
 }
 
 static gboolean
@@ -96,6 +109,21 @@ _audio_unit_property_listener (void *inRefCon, AudioUnit inUnit,
   }
 }
 
+static GstClockTime
+_current_time_ns (GstCoreAudio * core_audio)
+{
+  guint64 mach_t = mach_absolute_time ();
+  return gst_util_uint64_scale (mach_t, core_audio->timebase.numer,
+      core_audio->timebase.denom);
+}
+
+static GstClockTime
+_host_time_to_ns (GstCoreAudio * core_audio, uint64_t host_time)
+{
+  return gst_util_uint64_scale (host_time, core_audio->timebase.numer,
+      core_audio->timebase.denom);
+}
+
 /**************************
  *       Public API       *
  *************************/
@@ -202,28 +230,118 @@ gboolean
 gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
     gdouble rate, guint * samples, gdouble * latency)
 {
-  return gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
+  uint64_t now_ns = _current_time_ns (core_audio);
+  gboolean ret = gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
       samples, latency);
+
+  if (!ret)
+    return FALSE;
+
+  CORE_AUDIO_TIMING_LOCK (core_audio);
+
+  uint32_t samples_remain = 0;
+  uint64_t anchor_ns = core_audio->anchor_hosttime_ns;
+
+  if (core_audio->is_src) {
+    int64_t captured_ns =
+        core_audio->rate_scalar * (int64_t) (now_ns - anchor_ns);
+
+    /* src, the anchor time is the timestamp of the first sample in the last
+     * packet received, and we increment up from there, unless the device gets stopped. */
+    if (captured_ns > 0) {
+      if (core_audio->io_proc_active) {
+        samples_remain = (uint32_t) (captured_ns * rate / GST_SECOND);
+      } else {
+        samples_remain = core_audio->anchor_pend_samples;
+      }
+    } else {
+      /* Time went backward. This shouldn't happen for sources, but report something anyway */
+      samples_remain =
+          (uint32_t) (-captured_ns * rate / GST_SECOND) +
+          core_audio->anchor_pend_samples;
+    }
+
+    GST_DEBUG_OBJECT (core_audio,
+        "now_ns %" G_GUINT64_FORMAT " anchor %" G_GUINT64_FORMAT " elapsed ns %"
+        G_GINT64_FORMAT " rate %f captured_ns %" G_GINT64_FORMAT
+        " anchor_pend_samples %u samples_remain %u", now_ns, anchor_ns,
+        now_ns - anchor_ns, rate, captured_ns, core_audio->anchor_pend_samples,
+        samples_remain);
+  } else {
+    /* Sink, the anchor time is the time the most recent buffer will commence play out,
+     * and we count down to 0 for unplayed samples beyond that */
+    int64_t unplayed_ns =
+        core_audio->rate_scalar * (int64_t) (anchor_ns - now_ns);
+    if (unplayed_ns > 0) {
+      samples_remain =
+          (uint32_t) (unplayed_ns * rate / GST_SECOND) +
+          core_audio->anchor_pend_samples;
+    } else {
+      uint32_t samples_played = (uint32_t) (-unplayed_ns * rate / GST_SECOND);
+      if (samples_played < core_audio->anchor_pend_samples) {
+        samples_remain = core_audio->anchor_pend_samples - samples_played;
+      }
+    }
+
+    GST_DEBUG_OBJECT (core_audio,
+        "now_ns %" G_GUINT64_FORMAT " anchor %" G_GUINT64_FORMAT " elapsed ns %"
+        G_GINT64_FORMAT " rate %f unplayed_ns %" G_GINT64_FORMAT
+        " anchor_pend_samples %u", now_ns, anchor_ns, now_ns - anchor_ns, rate,
+        unplayed_ns, core_audio->anchor_pend_samples);
+  }
+
+  CORE_AUDIO_TIMING_UNLOCK (core_audio);
+
+  GST_DEBUG_OBJECT (core_audio, "samples = %u latency %f", samples_remain,
+      *latency);
+
+  *samples = samples_remain;
+  return TRUE;
+}
+
+void
+gst_core_audio_update_timing (GstCoreAudio * core_audio,
+    const AudioTimeStamp * inTimeStamp, unsigned int inNumberFrames)
+{
+  AudioTimeStampFlags target_flags =
+      kAudioTimeStampSampleHostTimeValid | kAudioTimeStampRateScalarValid;
+
+  if ((inTimeStamp->mFlags & target_flags) == target_flags) {
+    core_audio->anchor_hosttime_ns =
+        _host_time_to_ns (core_audio, inTimeStamp->mHostTime);
+    core_audio->anchor_pend_samples = inNumberFrames;
+    core_audio->rate_scalar = inTimeStamp->mRateScalar;
+
+    GST_DEBUG_OBJECT (core_audio,
+        "anchor hosttime_ns %" G_GUINT64_FORMAT
+        " scalar_rate %f anchor_pend_samples %u",
+        core_audio->anchor_hosttime_ns,
+        core_audio->rate_scalar, core_audio->anchor_pend_samples);
+  }
 }
 
 gboolean
 gst_core_audio_initialize (GstCoreAudio * core_audio,
-    AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough)
+    AudioStreamBasicDescription format, GstCaps * caps,
+    guint32 frames_per_packet, gboolean is_passthrough)
 {
-  guint32 frame_size;
-
   GST_DEBUG_OBJECT (core_audio,
       "Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough,
       caps);
 
   if (!gst_core_audio_initialize_impl (core_audio, format, caps,
-          is_passthrough, &frame_size)) {
+          is_passthrough, &frames_per_packet)) {
     return FALSE;
   }
 
   if (core_audio->is_src) {
     /* create AudioBufferList needed for recording */
-    core_audio->recBufferSize = frame_size * format.mBytesPerFrame;
+    core_audio->recBufferSize = frames_per_packet * format.mBytesPerFrame;
+
+    GST_DEBUG_OBJECT (core_audio,
+        "Allocating record buffers %u bytes %u frames",
+        core_audio->recBufferSize, frames_per_packet);
+
     core_audio->recBufferList =
         buffer_list_alloc (format.mChannelsPerFrame, core_audio->recBufferSize,
         /* Currently always TRUE (i.e. interleaved) */
@@ -256,7 +374,7 @@ gst_core_audio_select_device (GstCoreAudio * core_audio)
 void
 gst_core_audio_init_debug (void)
 {
-  GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
+  GST_DEBUG_CATEGORY_INIT (osx_coreaudio_debug, "osxaudio", 0,
       "OSX Audio Elements");
 }
 
diff --git a/sys/osxaudio/gstosxcoreaudio.h b/sys/osxaudio/gstosxcoreaudio.h
index 4319434d49bf802b03e2c1c0fc506288050df143..f1220255487e12de1527bd89a339931ce14c9783 100644
--- a/sys/osxaudio/gstosxcoreaudio.h
+++ b/sys/osxaudio/gstosxcoreaudio.h
@@ -45,6 +45,7 @@
   #endif
 #endif
 #include <AudioUnit/AudioUnit.h>
+#include <mach/mach_time.h>
 #include "gstosxaudioelement.h"
 
 
@@ -77,6 +78,9 @@ G_BEGIN_DECLS
 typedef struct _GstCoreAudio GstCoreAudio;
 typedef struct _GstCoreAudioClass GstCoreAudioClass;
 
+#define CORE_AUDIO_TIMING_LOCK(core_audio) (g_mutex_lock(&(core_audio->timing_lock)))
+#define CORE_AUDIO_TIMING_UNLOCK(core_audio) (g_mutex_unlock(&(core_audio->timing_lock)))
+
 struct _GstCoreAudio
 {
   GObject object;
@@ -107,6 +111,16 @@ struct _GstCoreAudio
   AudioStreamBasicDescription original_format, stream_format;
   AudioDeviceIOProcID procID;
 #endif
+
+  mach_timebase_info_data_t timebase;
+  GMutex timing_lock;
+  uint64_t anchor_hosttime_ns;
+  uint32_t anchor_pend_samples;
+  float rate_scalar;
+
+#ifdef HAVE_IOS
+  gdouble first_sample_time;
+#endif
 };
 
 struct _GstCoreAudioClass
@@ -127,6 +141,7 @@ gboolean gst_core_audio_close                                (GstCoreAudio *core
 gboolean gst_core_audio_initialize                           (GstCoreAudio *core_audio,
                                                               AudioStreamBasicDescription format,
                                                               GstCaps *caps,
+                                                              guint32 frames_per_packet,
                                                               gboolean is_passthrough);
 
 void gst_core_audio_uninitialize                             (GstCoreAudio *core_audio);
@@ -142,6 +157,10 @@ gboolean gst_core_audio_get_samples_and_latency              (GstCoreAudio * cor
                                                               guint *samples,
                                                               gdouble *latency);
 
+void gst_core_audio_update_timing                            (GstCoreAudio * core_audio,
+                                                              const AudioTimeStamp * inTimeStamp,
+                                                              unsigned int inNumberFrames);
+
 void  gst_core_audio_set_volume                              (GstCoreAudio *core_audio,
                                                               gfloat volume);
 
diff --git a/sys/osxaudio/gstosxcoreaudiocommon.c b/sys/osxaudio/gstosxcoreaudiocommon.c
index 39d03ac5b422d686e9ab8f0428f4c800a119bdbf..d512b32c43b8a4f1a9f829a3032293daf88bb2de 100644
--- a/sys/osxaudio/gstosxcoreaudiocommon.c
+++ b/sys/osxaudio/gstosxcoreaudiocommon.c
@@ -23,19 +23,26 @@
 
 #include "gstosxcoreaudiocommon.h"
 
+GST_DEBUG_CATEGORY_EXTERN (osx_coreaudio_debug);
+#define GST_CAT_DEFAULT osx_coreaudio_debug
+
 void
 gst_core_audio_remove_render_callback (GstCoreAudio * core_audio)
 {
   AURenderCallbackStruct input;
   OSStatus status;
+  AudioUnitPropertyID callback_type;
 
   /* Deactivate the render callback by calling SetRenderCallback
    * with a NULL inputProc.
    */
   input.inputProc = NULL;
   input.inputProcRefCon = NULL;
+  callback_type = core_audio->is_src ?
+      kAudioOutputUnitProperty_SetInputCallback :
+      kAudioUnitProperty_SetRenderCallback;
 
-  status = AudioUnitSetProperty (core_audio->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0,        /* N/A for global */
+  status = AudioUnitSetProperty (core_audio->audiounit, callback_type, kAudioUnitScope_Global, 0,       /* N/A for global */
       &input, sizeof (input));
 
   if (status) {
@@ -119,7 +126,11 @@ gst_core_audio_io_proc_start (GstCoreAudio * core_audio)
 
   core_audio->io_proc_needs_deactivation = FALSE;
 
+  // AudioOutputUnitStart on iOS can wait for the render callback to finish,
+  // where in our case we set the ringbuffer timestamp, which also needs the ringbuf lock.
+  GST_OBJECT_UNLOCK (core_audio->osxbuf);
   status = AudioOutputUnitStart (core_audio->audiounit);
+  GST_OBJECT_LOCK (core_audio->osxbuf);
   if (status) {
     GST_ERROR_OBJECT (core_audio->osxbuf, "AudioOutputUnitStart failed: %d",
         (int) status);
@@ -385,9 +396,9 @@ gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
     case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
       return kAudioChannelLabel_CenterSurround;
     case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
-      return kAudioChannelLabel_LeftSurround;
+      return kAudioChannelLabel_RearSurroundLeft;
     case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
-      return kAudioChannelLabel_RightSurround;
+      return kAudioChannelLabel_RearSurroundRight;
     case GST_AUDIO_CHANNEL_POSITION_LFE1:
       return kAudioChannelLabel_LFEScreen;
     case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
@@ -418,6 +429,12 @@ gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
       return kAudioChannelLabel_VerticalHeightRight;
     case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER:
       return kAudioChannelLabel_VerticalHeightCenter;
+    case GST_AUDIO_CHANNEL_POSITION_TOP_CENTER:
+      return kAudioChannelLabel_TopCenterSurround;
+    case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT:
+      return kAudioChannelLabel_LeftSurround;
+    case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT:
+      return kAudioChannelLabel_RightSurround;
 
       /* Special position values */
     case GST_AUDIO_CHANNEL_POSITION_NONE:
@@ -427,14 +444,11 @@ gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
 
       /* Following positions are unmapped --
        * i.e. mapped to kAudioChannelLabel_Unknown: */
-    case GST_AUDIO_CHANNEL_POSITION_TOP_CENTER:
     case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT:
     case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT:
     case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER:
     case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT:
     case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT:
-    case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT:
-    case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT:
     default:
       return kAudioChannelLabel_Unknown;
   }
@@ -455,9 +469,9 @@ gst_core_audio_channel_label_to_gst (AudioChannelLabel label,
     case kAudioChannelLabel_LFEScreen:
       return GST_AUDIO_CHANNEL_POSITION_LFE1;
     case kAudioChannelLabel_LeftSurround:
-      return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+      return GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT;
     case kAudioChannelLabel_RightSurround:
-      return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+      return GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT;
     case kAudioChannelLabel_LeftSurroundDirect:
       return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
     case kAudioChannelLabel_RightSurroundDirect:
@@ -486,6 +500,12 @@ gst_core_audio_channel_label_to_gst (AudioChannelLabel label,
       return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT;
     case kAudioChannelLabel_VerticalHeightCenter:
       return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER;
+    case kAudioChannelLabel_RearSurroundLeft:
+      return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+    case kAudioChannelLabel_RearSurroundRight:
+      return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+    case kAudioChannelLabel_TopCenterSurround:
+      return GST_AUDIO_CHANNEL_POSITION_TOP_CENTER;
 
       /* Special position values */
 
@@ -499,9 +519,6 @@ gst_core_audio_channel_label_to_gst (AudioChannelLabel label,
          Following labels are unmapped --
          i.e. mapped to GST_AUDIO_CHANNEL_POSITION_INVALID:
        */
-    case kAudioChannelLabel_RearSurroundLeft:
-    case kAudioChannelLabel_RearSurroundRight:
-    case kAudioChannelLabel_TopCenterSurround:
     case kAudioChannelLabel_LeftTotal:
     case kAudioChannelLabel_RightTotal:
     case kAudioChannelLabel_HearingImpaired:
diff --git a/sys/osxaudio/gstosxcoreaudiocommon.h b/sys/osxaudio/gstosxcoreaudiocommon.h
index c4602a6b301292aa2c635e862ba4c08f7df9123a..ba6dcffd3e0b66fb61232a0317f38b9a10af8a16 100644
--- a/sys/osxaudio/gstosxcoreaudiocommon.h
+++ b/sys/osxaudio/gstosxcoreaudiocommon.h
@@ -24,6 +24,8 @@
 #include "gstosxcoreaudio.h"
 #include <gst/audio/audio-channels.h>
 
+G_BEGIN_DECLS
+
 typedef struct
 {
   GMutex lock;
@@ -64,3 +66,5 @@ OSStatus gst_core_audio_render_notify                     (GstCoreAudio * core_a
 AudioChannelLabel gst_audio_channel_position_to_core_audio (GstAudioChannelPosition position, int channel);
 
 GstAudioChannelPosition gst_core_audio_channel_label_to_gst (AudioChannelLabel label, int channel, gboolean warn);
+
+G_END_DECLS
diff --git a/sys/osxaudio/gstosxcoreaudiohal.c b/sys/osxaudio/gstosxcoreaudiohal.c
index 7e41f59aac09349f7be302e3b9880bc81f364eae..2eb27e2b4902decfbb3ac019f23d0f07165af511 100644
--- a/sys/osxaudio/gstosxcoreaudiohal.c
+++ b/sys/osxaudio/gstosxcoreaudiohal.c
@@ -1162,7 +1162,7 @@ gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
     _monitorize_spdif (core_audio);
   } else {
     OSStatus status;
-    UInt32 propertySize;
+    UInt32 propertySize = sizeof (*frame_size);
 
     core_audio->stream_idx = 0;
     if (!gst_core_audio_set_format (core_audio, format))
@@ -1172,17 +1172,31 @@ gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
             format.mChannelsPerFrame, caps))
       goto done;
 
-    if (core_audio->is_src) {
-      propertySize = sizeof (*frame_size);
+    // Attempt to configure the requested frame size if smaller than the device's
+    // This will apparently modify the size for all audio devices in the current process so should
+    // be done conservatively
+    if (frame_size != 0) {
+      guint32 cur_frame_size;
       status = AudioUnitGetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0,     /* N/A for global */
-          frame_size, &propertySize);
-
-      if (status) {
-        GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %d",
-            (int) status);
-        goto done;
+          &cur_frame_size, &propertySize);
+      if (!status && *frame_size < cur_frame_size) {
+        status = AudioUnitSetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0,   /* N/A for global */
+            frame_size, propertySize);
+        if (status) {
+          GST_WARNING_OBJECT (core_audio->osxbuf,
+              "Failed to set desired frame size of %u: %d", *frame_size,
+              (int) status);
+        }
       }
     }
+    status = AudioUnitGetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0,       /* N/A for global */
+        frame_size, &propertySize);
+
+    if (status) {
+      GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %d",
+          (int) status);
+      goto done;
+    }
   }
 
   ret = TRUE;
diff --git a/sys/osxvideo/cocoawindow.h b/sys/osxvideo/cocoawindow.h
index 816f1bbd2d6e2f70d8c323682b37d6f12c6fc7bb..756676cdf27065f82ea1d54b36ee2876f9083277 100644
--- a/sys/osxvideo/cocoawindow.h
+++ b/sys/osxvideo/cocoawindow.h
@@ -69,9 +69,6 @@ struct _GstOSXImage;
 - (void) addToSuperview: (NSView *)superview;
 - (void) removeFromSuperview: (id)unused;
 - (void) setNavigation: (GstNavigation *) nav;
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-- (void) setMainThread: (NSThread *) thread;
-#endif
 
 @end
 
diff --git a/sys/osxvideo/cocoawindow.m b/sys/osxvideo/cocoawindow.m
index 1a350787fd134b5c8fe63f7aeee8731ae8755a8b..611652522e1ae23fb99ed4dfe79df7069179c8f4 100644
--- a/sys/osxvideo/cocoawindow.m
+++ b/sys/osxvideo/cocoawindow.m
@@ -370,8 +370,8 @@ const gchar* gst_keycode_to_keyname(gint16 keycode)
 
   pi_texture = 0;
   data = nil;
-  width = frame.size.width;
-  height = frame.size.height;
+  width = frame.size.width * [[NSScreen mainScreen] backingScaleFactor];
+  height = frame.size.height * [[NSScreen mainScreen] backingScaleFactor];
   drawingBounds = NSMakeRect(0, 0, width, height);
 
   GST_LOG ("Width: %d Height: %d", width, height);
@@ -395,7 +395,7 @@ const gchar* gst_keycode_to_keyname(gint16 keycode)
 - (void) reshape {
   NSRect bounds;
   gdouble frame_par, view_par;
-  gint view_height, view_width, c_height, c_width, c_x, c_y;
+  gint view_height, view_width, c_height, c_width, c_x, c_y, scale_factor;
 
   [super reshape];
 
@@ -410,6 +410,7 @@ const gchar* gst_keycode_to_keyname(gint16 keycode)
   bounds = [self bounds];
   view_width = bounds.size.width;
   view_height = bounds.size.height;
+  scale_factor = [[NSScreen mainScreen] backingScaleFactor];
 
   frame_par = (gdouble) width / height;
   view_par = (gdouble) view_width / view_height;
@@ -433,8 +434,8 @@ const gchar* gst_keycode_to_keyname(gint16 keycode)
     c_y = (view_height - c_height) / 2;
   }
 
-  drawingBounds = NSMakeRect(c_x, c_y, c_width, c_height);
-  glViewport (c_x, c_y, (GLint) c_width, (GLint) c_height);
+  drawingBounds = NSMakeRect(c_x * scale_factor, c_y * scale_factor,
+    c_width * scale_factor, c_height * scale_factor);
 }
 
 - (void) initTextures {
@@ -544,6 +545,9 @@ const gchar* gst_keycode_to_keyname(gint16 keycode)
   /* Black background */
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
+  glViewport ((GLint) drawingBounds.origin.x, (GLint) drawingBounds.origin.y,
+    (GLint) drawingBounds.size.width, (GLint) drawingBounds.size.height);
+
   if (!initDone) {
     [actualContext flushBuffer];
     return;
@@ -663,12 +667,6 @@ const gchar* gst_keycode_to_keyname(gint16 keycode)
   [self reshape];
 }
 
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-- (void) setMainThread: (NSThread *) thread {
-  mainThread = thread;
-}
-#endif
-
 - (void) haveSuperviewReal:(NSMutableArray *)closure {
 	BOOL haveSuperview = [self superview] != nil;
 	[closure addObject:[NSNumber numberWithBool:haveSuperview]];
diff --git a/sys/osxvideo/osxvideosink.h b/sys/osxvideo/osxvideosink.h
index d467b0e46415d6e28feaf3856522d27a9d1c3f6f..da4c079e59bfc97683b6813c2e19717d1e56ab3d 100644
--- a/sys/osxvideo/osxvideosink.h
+++ b/sys/osxvideo/osxvideosink.h
@@ -128,11 +128,6 @@ GType gst_osx_video_sink_get_type(void);
 -(void) destroy;
 -(void) showFrame: (GstBufferObject*) buf;
 -(void) setView: (NSView*) view;
-+ (BOOL) isMainThread;
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
--(void) nsAppThread;
--(void) checkMainRunLoop;
-#endif
 @end
 
 G_END_DECLS
diff --git a/sys/osxvideo/osxvideosink.m b/sys/osxvideo/osxvideosink.m
index b2c905cc432685dddd5a2bbf8726f60da17211d8..a654ef8eafe7dcc9d37f8a0af6039f11f8e4d747 100644
--- a/sys/osxvideo/osxvideosink.m
+++ b/sys/osxvideo/osxvideosink.m
@@ -43,12 +43,6 @@
 GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
 #define GST_CAT_DEFAULT gst_debug_osx_video_sink
 
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-#include <pthread.h>
-extern void _CFRunLoopSetCurrent (CFRunLoopRef rl);
-extern pthread_t _CFMainPThread;
-#endif
-
 static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -73,12 +67,6 @@ enum
 
 static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink);
 
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-static GMutex _run_loop_check_mutex;
-static GMutex _run_loop_mutex;
-static GCond _run_loop_cond;
-#endif
-
 static GstOSXVideoSinkClass *sink_class = NULL;
 static GstVideoSinkClass *parent_class = NULL;
 
@@ -96,7 +84,6 @@ static void
 gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
     NSObject * object, SEL function, NSObject *data, BOOL waitUntilDone)
 {
-
   NSThread *thread;
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
@@ -111,126 +98,6 @@ gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
   [pool release];
 }
 
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-/* Poll for cocoa events */
-static void
-run_ns_app_loop (void) {
-  NSEvent *event;
-  NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
-  NSDate *pollTime = nil;
-
-  /* when running the loop in a thread we want to sleep as long as possible */
-  pollTime = [NSDate distantFuture];
-
-  do {
-      event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:pollTime
-          inMode:NSDefaultRunLoopMode dequeue:YES];
-      [NSApp sendEvent:event];
-    }
-  while (event != nil);
-  [pool release];
-}
-
-static void
-gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
-{
-  /* check if the main run loop is running */
-  gboolean is_running;
-
-  /* the easy way */
-  is_running = [[NSRunLoop mainRunLoop] currentMode] != nil;
-  if (is_running) {
-    goto exit;
-  } else {
-    /* the previous check doesn't always work with main loops that run
-     * cocoa's main run loop manually, like the gdk one, giving false
-     * negatives. This check defers a call to the main thread and waits to
-     * be awaken by this function. */
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject;
-    gint64 abstime;
-
-    g_mutex_lock (&_run_loop_mutex);
-    [object performSelectorOnMainThread:
-          @selector(checkMainRunLoop)
-          withObject:nil waitUntilDone:NO];
-    /* Wait 100 ms */
-    abstime = g_get_monotonic_time () + 100 * 1000;
-    is_running = g_cond_wait_until (&_run_loop_cond,
-        &_run_loop_mutex, abstime);
-    g_mutex_unlock (&_run_loop_mutex);
-
-    [pool release];
-  }
-
-exit:
-  {
-  GST_DEBUG_OBJECT(sink, "The main runloop %s is running",
-      is_running ? "" : " not ");
-  if (is_running) {
-    sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING;
-    sink_class->ns_app_thread = [NSThread mainThread];
-  } else {
-    sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_NOT_RUNNING;
-  }
-  }
-}
-
-static void
-gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
-{
-  /* Cocoa applications require a main runloop running to dispatch UI
-   * events and process deferred calls to the main thread through
-   * perfermSelectorOnMainThread.
-   * Since the sink needs to create it's own Cocoa window when no
-   * external NSView is passed to the sink through the GstVideoOverlay API,
-   * we need to run the cocoa mainloop somehow.
-   * This run loop can only be started once, by the first sink needing it
-   */
-
-  g_mutex_lock (&_run_loop_check_mutex);
-
-  if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN) {
-    gst_osx_videosink_check_main_run_loop (sink);
-  }
-
-  if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING) {
-    g_mutex_unlock (&_run_loop_check_mutex);
-    return;
-  }
-
-  if (sink_class->ns_app_thread == NULL) {
-    /* run the main runloop in a separate thread */
-
-    /* override [NSThread isMainThread] with our own implementation so that we can
-     * make it believe our dedicated thread is the main thread
-     */
-    Method origIsMainThread = class_getClassMethod([NSThread class],
-        NSSelectorFromString(@"isMainThread"));
-    Method ourIsMainThread = class_getClassMethod([GstOSXVideoSinkObject class],
-        NSSelectorFromString(@"isMainThread"));
-
-    method_exchangeImplementations(origIsMainThread, ourIsMainThread);
-
-    sink_class->ns_app_thread = [[NSThread alloc]
-        initWithTarget:sink->osxvideosinkobject
-        selector:@selector(nsAppThread) object:nil];
-    [sink_class->ns_app_thread start];
-
-    g_mutex_lock (&_run_loop_mutex);
-    g_cond_wait (&_run_loop_cond, &_run_loop_mutex);
-    g_mutex_unlock (&_run_loop_mutex);
-  }
-
-  g_mutex_unlock (&_run_loop_check_mutex);
-}
-
-static void
-gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink)
-{
-}
-#endif
-
 /* This function handles osx window creation */
 static gboolean
 gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
@@ -260,11 +127,6 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
   rect.size.height = (float) osxwindow->height;
   osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
 
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-  gst_osx_video_sink_run_cocoa_loop (osxvideosink);
-  [osxwindow->gstview setMainThread:sink_class->ns_app_thread];
-#endif
-
   if (osxvideosink->superview == NULL) {
     GST_INFO_OBJECT (osxvideosink, "emitting prepare-xwindow-id");
     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (osxvideosink));
@@ -276,15 +138,19 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
      * from the main thread
      */
     GST_INFO_OBJECT (osxvideosink, "we have a superview, adding our view to it");
-    gst_osx_video_sink_call_from_main_thread(osxvideosink, osxwindow->gstview,
+    gst_osx_video_sink_call_from_main_thread (osxvideosink, osxwindow->gstview,
         @selector(addToSuperview:), osxvideosink->superview, NO);
 
   } else {
-    gst_osx_video_sink_call_from_main_thread(osxvideosink,
+    gst_osx_video_sink_call_from_main_thread (osxvideosink,
       osxvideosink->osxvideosinkobject,
       @selector(createInternalWindow), nil, YES);
     GST_INFO_OBJECT (osxvideosink, "No superview, creating an internal window.");
   }
+
+  gst_osx_video_sink_call_from_main_thread (osxvideosink, osxvideosink->osxvideosinkobject,
+    @selector(setActivationPolicy), nil, YES);
+
   [osxwindow->gstview setNavigation: GST_NAVIGATION(osxvideosink)];
   [osxvideosink->osxwindow->gstview setKeepAspectRatio: osxvideosink->keep_par];
 
@@ -301,14 +167,9 @@ gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink)
   g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
   pool = [[NSAutoreleasePool alloc] init];
 
-  GST_OBJECT_LOCK (osxvideosink);
-  gst_osx_video_sink_call_from_main_thread(osxvideosink,
+  gst_osx_video_sink_call_from_main_thread (osxvideosink,
       osxvideosink->osxvideosinkobject,
       @selector(destroy), (id) nil, YES);
-  GST_OBJECT_UNLOCK (osxvideosink);
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
-  gst_osx_video_sink_stop_cocoa_loop (osxvideosink);
-#endif
   [pool release];
 }
 
@@ -434,7 +295,7 @@ gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
 
   GST_DEBUG ("show_frame");
   bufferobject = [[GstBufferObject alloc] initWithBuffer:buf];
-  gst_osx_video_sink_call_from_main_thread(osxvideosink,
+  gst_osx_video_sink_call_from_main_thread (osxvideosink,
       osxvideosink->osxvideosinkobject,
       @selector(showFrame:), bufferobject, NO);
   [pool release];
@@ -514,6 +375,11 @@ gst_osx_video_sink_propose_allocation (GstBaseSink * base_sink, GstQuery * query
 static void
 gst_osx_video_sink_init (GstOSXVideoSink * sink)
 {
+  if ([[NSRunLoop mainRunLoop] currentMode] == nil)
+    g_warning ("An NSRunLoop needs to be running on the main thread "
+        "to ensure correct behaviour on macOS. Use gst_macos_main() or call "
+        "[NSApplication sharedApplication] in your code before using this element.");
+
   sink->osxwindow = NULL;
   sink->superview = NULL;
   sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink];
@@ -658,7 +524,7 @@ gst_osx_video_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle
   GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (overlay);
   NSView *view = (NSView *) handle_id;
 
-  gst_osx_video_sink_call_from_main_thread(osxvideosink,
+  gst_osx_video_sink_call_from_main_thread (osxvideosink,
       osxvideosink->osxvideosinkobject,
       @selector(setView:), view, YES);
 }
@@ -741,7 +607,7 @@ gst_osx_video_sink_get_type (void)
   if (!osxvideosink->osxwindow->closed) {
     osxvideosink->osxwindow->closed = TRUE;
     GST_ELEMENT_ERROR (osxvideosink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL));
-    gst_osx_video_sink_osxwindow_destroy(osxvideosink);
+    gst_osx_video_sink_osxwindow_destroy (osxvideosink);
   }
 }
 
@@ -796,10 +662,9 @@ gst_osx_video_sink_get_type (void)
 
 }
 
-+ (BOOL) isMainThread
+- (void) setActivationPolicy
 {
-  /* FIXME: ideally we should return YES only for ->ns_app_thread here */
-  return YES;
+  [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
 }
 
 - (void) setView: (NSView*)view
@@ -914,6 +779,7 @@ no_texture_buffer:
 
   pool = [[NSAutoreleasePool alloc] init];
 
+  GST_OBJECT_LOCK (osxvideosink);
   osxwindow = osxvideosink->osxwindow;
   osxvideosink->osxwindow = NULL;
 
@@ -931,48 +797,11 @@ no_texture_buffer:
     }
     g_free (osxwindow);
   }
-  [pool release];
-}
-
-#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
--(void) nsAppThread
-{
-  NSAutoreleasePool *pool;
-
-  /* set the main runloop as the runloop for the current thread. This has the
-   * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
-   * runs the main runloop.
-   */
-  _CFRunLoopSetCurrent(CFRunLoopGetMain());
-
-  /* this is needed to make IsMainThread checks in core foundation work from the
-   * current thread
-   */
-  _CFMainPThread = pthread_self();
-
-  pool = [[NSAutoreleasePool alloc] init];
-
-  [NSApplication sharedApplication];
-  [NSApp finishLaunching];
-
-  g_mutex_lock (&_run_loop_mutex);
-  g_cond_signal (&_run_loop_cond);
-  g_mutex_unlock (&_run_loop_mutex);
-
-  /* run the loop */
-  run_ns_app_loop ();
+  GST_OBJECT_UNLOCK (osxvideosink);
 
   [pool release];
 }
 
--(void) checkMainRunLoop
-{
-  g_mutex_lock (&_run_loop_mutex);
-  g_cond_signal (&_run_loop_cond);
-  g_mutex_unlock (&_run_loop_mutex);
-}
-#endif
-
 @end
 
 @ implementation GstBufferObject
@@ -993,7 +822,6 @@ no_texture_buffer:
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-
   if (!gst_element_register (plugin, "osxvideosink",
           GST_RANK_MARGINAL, GST_TYPE_OSX_VIDEO_SINK))
     return FALSE;
diff --git a/sys/v4l2/ext/v4l2-common.h b/sys/v4l2/ext/v4l2-common.h
index a2494047a7c936b4b9c1bdcedace376204f54147..6a733b416ffc5b4d2c0b0be3237e6bd9cf9fac10 100644
--- a/sys/v4l2/ext/v4l2-common.h
+++ b/sys/v4l2/ext/v4l2-common.h
@@ -10,45 +10,6 @@
  *
  * Copyright (C) 2012 Nokia Corporation
  * Contact: Sakari Ailus <sakari.ailus@iki.fi>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  Alternatively you can redistribute this file under the terms of the
- *  BSD license as stated below:
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions
- *  are met:
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in
- *     the documentation and/or other materials provided with the
- *     distribution.
- *  3. The names of its contributors may not be used to endorse or promote
- *     products derived from this software without specific prior written
- *     permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
  */
 
 #ifndef __V4L2_COMMON__
diff --git a/sys/v4l2/ext/v4l2-controls.h b/sys/v4l2/ext/v4l2-controls.h
index af15f01f471fa252c3af08b4e57100d89fad4214..151dc2c2f651899bd9d09f673e28e576e1c8d6e1 100644
--- a/sys/v4l2/ext/v4l2-controls.h
+++ b/sys/v4l2/ext/v4l2-controls.h
@@ -4,44 +4,6 @@
  *
  *  Copyright (C) 1999-2012 the contributors
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  Alternatively you can redistribute this file under the terms of the
- *  BSD license as stated below:
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions
- *  are met:
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in
- *     the documentation and/or other materials provided with the
- *     distribution.
- *  3. The names of its contributors may not be used to endorse or promote
- *     products derived from this software without specific prior written
- *     permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
  *  The contents of this header was split off from videodev2.h. All control
  *  definitions should be added to this header, which is included by
  *  videodev2.h.
@@ -152,8 +114,10 @@ enum v4l2_colorfx {
 
 /* USER-class private control IDs */
 
-/* The base for the meye driver controls. See linux/meye.h for the list
- * of controls. We reserve 16 controls for this driver. */
+/*
+ * The base for the meye driver controls. This driver was removed, but
+ * we keep this define in case any software still uses it.
+ */
 #define V4L2_CID_USER_MEYE_BASE			(V4L2_CID_USER_BASE + 0x1000)
 
 /* The base for the bttv driver controls.
@@ -218,6 +182,24 @@ enum v4l2_colorfx {
  */
 #define V4L2_CID_USER_ALLEGRO_BASE		(V4L2_CID_USER_BASE + 0x1170)
 
+/*
+ * The base for the isl7998x driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_ISL7998X_BASE		(V4L2_CID_USER_BASE + 0x1180)
+
+/*
+ * The base for DW100 driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_DW100_BASE		(V4L2_CID_USER_BASE + 0x1190)
+
+/*
+ * The base for Aspeed driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_ASPEED_BASE		(V4L2_CID_USER_BASE + 0x11a0)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
@@ -440,6 +422,11 @@ enum v4l2_mpeg_video_multi_slice_mode {
 #define V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES		(V4L2_CID_CODEC_BASE+234)
 #define V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR		(V4L2_CID_CODEC_BASE+235)
 #define V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD	(V4L2_CID_CODEC_BASE+236)
+#define V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE	(V4L2_CID_CODEC_BASE+237)
+enum v4l2_mpeg_video_intra_refresh_period_type {
+	V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM	= 0,
+	V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC	= 1,
+};
 
 /* CIDs for the MPEG-2 Part 2 (H.262) codec */
 #define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL			(V4L2_CID_CODEC_BASE+270)
@@ -812,6 +799,88 @@ enum v4l2_mpeg_video_frame_skip_mode {
 #define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY		(V4L2_CID_CODEC_BASE + 653)
 #define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE	(V4L2_CID_CODEC_BASE + 654)
 
+#define V4L2_CID_MPEG_VIDEO_AV1_PROFILE (V4L2_CID_CODEC_BASE + 655)
+/**
+ * enum v4l2_mpeg_video_av1_profile - AV1 profiles
+ *
+ * @V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN: compliant decoders must be able to decode
+ * streams with seq_profile equal to 0.
+ * @V4L2_MPEG_VIDEO_AV1_PROFILE_HIGH: compliant decoders must be able to decode
+ * streams with seq_profile equal less than or equal to 1.
+ * @V4L2_MPEG_VIDEO_AV1_PROFILE_PROFESSIONAL: compliant decoders must be able to
+ * decode streams with seq_profile less than or equal to 2.
+ *
+ * Conveys the highest profile a decoder can work with.
+ */
+enum v4l2_mpeg_video_av1_profile {
+	V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN = 0,
+	V4L2_MPEG_VIDEO_AV1_PROFILE_HIGH = 1,
+	V4L2_MPEG_VIDEO_AV1_PROFILE_PROFESSIONAL = 2,
+};
+
+#define V4L2_CID_MPEG_VIDEO_AV1_LEVEL (V4L2_CID_CODEC_BASE + 656)
+/**
+ * enum v4l2_mpeg_video_av1_level - AV1 levels
+ *
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_0: Level 2.0.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_1: Level 2.1.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_2: Level 2.2.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_3: Level 2.3.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_0: Level 3.0.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_1: Level 3.1.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_2: Level 3.2.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_3: Level 3.3.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_0: Level 4.0.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_1: Level 4.1.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_2: Level 4.2.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_3: Level 4.3.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_0: Level 5.0.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_1: Level 5.1.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_2: Level 5.2.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_3: Level 5.3.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_0: Level 6.0.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_1: Level 6.1.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_2: Level 6.2.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_3: Level 6.3.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_0: Level 7.0.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_1: Level 7.1.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_2: Level 7.2.
+ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_3: Level 7.3.
+ *
+ * Conveys the highest level a decoder can work with.
+ */
+enum v4l2_mpeg_video_av1_level {
+	V4L2_MPEG_VIDEO_AV1_LEVEL_2_0 = 0,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_2_1 = 1,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_2_2 = 2,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_2_3 = 3,
+
+	V4L2_MPEG_VIDEO_AV1_LEVEL_3_0 = 4,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_3_1 = 5,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_3_2 = 6,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_3_3 = 7,
+
+	V4L2_MPEG_VIDEO_AV1_LEVEL_4_0 = 8,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_4_1 = 9,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_4_2 = 10,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_4_3 = 11,
+
+	V4L2_MPEG_VIDEO_AV1_LEVEL_5_0 = 12,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_5_1 = 13,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_5_2 = 14,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_5_3 = 15,
+
+	V4L2_MPEG_VIDEO_AV1_LEVEL_6_0 = 16,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_6_1 = 17,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_6_2 = 18,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_6_3 = 19,
+
+	V4L2_MPEG_VIDEO_AV1_LEVEL_7_0 = 20,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_7_1 = 21,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_7_2 = 22,
+	V4L2_MPEG_VIDEO_AV1_LEVEL_7_3 = 23
+};
+
 /*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
 #define V4L2_CID_CODEC_CX2341X_BASE				(V4L2_CTRL_CLASS_CODEC | 0x1000)
 #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE		(V4L2_CID_CODEC_CX2341X_BASE+0)
@@ -999,6 +1068,8 @@ enum v4l2_auto_focus_range {
 
 #define V4L2_CID_CAMERA_SENSOR_ROTATION		(V4L2_CID_CAMERA_CLASS_BASE+35)
 
+#define V4L2_CID_HDR_SENSOR_MODE		(V4L2_CID_CAMERA_CLASS_BASE+36)
+
 /* FM Modulator class control IDs */
 
 #define V4L2_CID_FM_TX_CLASS_BASE		(V4L2_CTRL_CLASS_FM_TX | 0x900)
@@ -1560,6 +1631,8 @@ struct v4l2_h264_dpb_entry {
 #define V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC		0x01
 #define V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC		0x02
 #define V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD	0x04
+#define V4L2_H264_DECODE_PARAM_FLAG_PFRAME		0x08
+#define V4L2_H264_DECODE_PARAM_FLAG_BFRAME		0x10
 
 #define V4L2_CID_STATELESS_H264_DECODE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 7)
 /**
@@ -1714,7 +1787,7 @@ struct v4l2_vp8_segment {
  * @sharpness_level: matches sharpness_level syntax element.
  * @level: matches loop_filter_level syntax element.
  * @padding: padding field. Should be zeroed by applications.
- * @flags: see V4L2_VP8_LF_FLAG_{}.
+ * @flags: see V4L2_VP8_LF_{}.
  *
  * This structure contains loop filter related parameters.
  * See the 'mb_lf_adjustments()' part of the frame header syntax,
@@ -1981,6 +2054,469 @@ struct v4l2_ctrl_mpeg2_quantisation {
 	__u8	chroma_non_intra_quantiser_matrix[64];
 };
 
+#define V4L2_CID_STATELESS_HEVC_SPS		(V4L2_CID_CODEC_STATELESS_BASE + 400)
+#define V4L2_CID_STATELESS_HEVC_PPS		(V4L2_CID_CODEC_STATELESS_BASE + 401)
+#define V4L2_CID_STATELESS_HEVC_SLICE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 402)
+#define V4L2_CID_STATELESS_HEVC_SCALING_MATRIX	(V4L2_CID_CODEC_STATELESS_BASE + 403)
+#define V4L2_CID_STATELESS_HEVC_DECODE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 404)
+#define V4L2_CID_STATELESS_HEVC_DECODE_MODE	(V4L2_CID_CODEC_STATELESS_BASE + 405)
+#define V4L2_CID_STATELESS_HEVC_START_CODE	(V4L2_CID_CODEC_STATELESS_BASE + 406)
+#define V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS (V4L2_CID_CODEC_STATELESS_BASE + 407)
+
+enum v4l2_stateless_hevc_decode_mode {
+	V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED,
+	V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+};
+
+enum v4l2_stateless_hevc_start_code {
+	V4L2_STATELESS_HEVC_START_CODE_NONE,
+	V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+};
+
+#define V4L2_HEVC_SLICE_TYPE_B	0
+#define V4L2_HEVC_SLICE_TYPE_P	1
+#define V4L2_HEVC_SLICE_TYPE_I	2
+
+#define V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE		(1ULL << 0)
+#define V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED			(1ULL << 1)
+#define V4L2_HEVC_SPS_FLAG_AMP_ENABLED				(1ULL << 2)
+#define V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET		(1ULL << 3)
+#define V4L2_HEVC_SPS_FLAG_PCM_ENABLED				(1ULL << 4)
+#define V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED		(1ULL << 5)
+#define V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT		(1ULL << 6)
+#define V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED		(1ULL << 7)
+#define V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED	(1ULL << 8)
+
+/**
+ * struct v4l2_ctrl_hevc_sps - ITU-T Rec. H.265: Sequence parameter set
+ *
+ * @video_parameter_set_id: specifies the value of the
+ *			vps_video_parameter_set_id of the active VPS
+ * @seq_parameter_set_id: provides an identifier for the SPS for
+ *			  reference by other syntax elements
+ * @pic_width_in_luma_samples:	specifies the width of each decoded picture
+ *				in units of luma samples
+ * @pic_height_in_luma_samples: specifies the height of each decoded picture
+ *				in units of luma samples
+ * @bit_depth_luma_minus8: this value plus 8specifies the bit depth of the
+ *                         samples of the luma array
+ * @bit_depth_chroma_minus8: this value plus 8 specifies the bit depth of the
+ *                           samples of the chroma arrays
+ * @log2_max_pic_order_cnt_lsb_minus4: this value plus 4 specifies the value of
+ *                                     the variable MaxPicOrderCntLsb
+ * @sps_max_dec_pic_buffering_minus1: this value plus 1 specifies the maximum
+ *                                    required size of the decoded picture
+ *                                    buffer for the codec video sequence
+ * @sps_max_num_reorder_pics: indicates the maximum allowed number of pictures
+ * @sps_max_latency_increase_plus1: not equal to 0 is used to compute the
+ *				    value of SpsMaxLatencyPictures array
+ * @log2_min_luma_coding_block_size_minus3: plus 3 specifies the minimum
+ *					    luma coding block size
+ * @log2_diff_max_min_luma_coding_block_size: specifies the difference between
+ *					      the maximum and minimum luma
+ *					      coding block size
+ * @log2_min_luma_transform_block_size_minus2: plus 2 specifies the minimum luma
+ *					       transform block size
+ * @log2_diff_max_min_luma_transform_block_size: specifies the difference between
+ *						 the maximum and minimum luma
+ *						 transform block size
+ * @max_transform_hierarchy_depth_inter: specifies the maximum hierarchy
+ *					 depth for transform units of
+ *					 coding units coded in inter
+ *					 prediction mode
+ * @max_transform_hierarchy_depth_intra: specifies the maximum hierarchy
+ *					 depth for transform units of
+ *					 coding units coded in intra
+ *					 prediction mode
+ * @pcm_sample_bit_depth_luma_minus1: this value plus 1 specifies the number of
+ *                                    bits used to represent each of PCM sample
+ *                                    values of the luma component
+ * @pcm_sample_bit_depth_chroma_minus1: this value plus 1 specifies the number
+ *                                      of bits used to represent each of PCM
+ *                                      sample values of the chroma components
+ * @log2_min_pcm_luma_coding_block_size_minus3: this value plus 3 specifies the
+ *                                              minimum size of coding blocks
+ * @log2_diff_max_min_pcm_luma_coding_block_size: specifies the difference between
+ *						  the maximum and minimum size of
+ *						  coding blocks
+ * @num_short_term_ref_pic_sets: specifies the number of st_ref_pic_set()
+ *				 syntax structures included in the SPS
+ * @num_long_term_ref_pics_sps: specifies the number of candidate long-term
+ *				reference pictures that are specified in the SPS
+ * @chroma_format_idc: specifies the chroma sampling
+ * @sps_max_sub_layers_minus1: this value plus 1 specifies the maximum number
+ *                             of temporal sub-layers
+ * @reserved: padding field. Should be zeroed by applications.
+ * @flags: see V4L2_HEVC_SPS_FLAG_{}
+ */
+struct v4l2_ctrl_hevc_sps {
+	__u8	video_parameter_set_id;
+	__u8	seq_parameter_set_id;
+	__u16	pic_width_in_luma_samples;
+	__u16	pic_height_in_luma_samples;
+	__u8	bit_depth_luma_minus8;
+	__u8	bit_depth_chroma_minus8;
+	__u8	log2_max_pic_order_cnt_lsb_minus4;
+	__u8	sps_max_dec_pic_buffering_minus1;
+	__u8	sps_max_num_reorder_pics;
+	__u8	sps_max_latency_increase_plus1;
+	__u8	log2_min_luma_coding_block_size_minus3;
+	__u8	log2_diff_max_min_luma_coding_block_size;
+	__u8	log2_min_luma_transform_block_size_minus2;
+	__u8	log2_diff_max_min_luma_transform_block_size;
+	__u8	max_transform_hierarchy_depth_inter;
+	__u8	max_transform_hierarchy_depth_intra;
+	__u8	pcm_sample_bit_depth_luma_minus1;
+	__u8	pcm_sample_bit_depth_chroma_minus1;
+	__u8	log2_min_pcm_luma_coding_block_size_minus3;
+	__u8	log2_diff_max_min_pcm_luma_coding_block_size;
+	__u8	num_short_term_ref_pic_sets;
+	__u8	num_long_term_ref_pics_sps;
+	__u8	chroma_format_idc;
+	__u8	sps_max_sub_layers_minus1;
+
+	__u8	reserved[6];
+	__u64	flags;
+};
+
+#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED	(1ULL << 0)
+#define V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT			(1ULL << 1)
+#define V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED		(1ULL << 2)
+#define V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT			(1ULL << 3)
+#define V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED		(1ULL << 4)
+#define V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED		(1ULL << 5)
+#define V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED			(1ULL << 6)
+#define V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT	(1ULL << 7)
+#define V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED			(1ULL << 8)
+#define V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED			(1ULL << 9)
+#define V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED		(1ULL << 10)
+#define V4L2_HEVC_PPS_FLAG_TILES_ENABLED			(1ULL << 11)
+#define V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED		(1ULL << 12)
+#define V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED	(1ULL << 13)
+#define V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 14)
+#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED	(1ULL << 15)
+#define V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER	(1ULL << 16)
+#define V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT		(1ULL << 17)
+#define V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (1ULL << 18)
+#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT	(1ULL << 19)
+#define V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING			(1ULL << 20)
+
+/**
+ * struct v4l2_ctrl_hevc_pps - ITU-T Rec. H.265: Picture parameter set
+ *
+ * @pic_parameter_set_id: identifies the PPS for reference by other
+ *			  syntax elements
+ * @num_extra_slice_header_bits: specifies the number of extra slice header
+ *				 bits that are present in the slice header RBSP
+ *				 for coded pictures referring to the PPS.
+ * @num_ref_idx_l0_default_active_minus1: this value plus 1 specifies the
+ *                                        inferred value of num_ref_idx_l0_active_minus1
+ * @num_ref_idx_l1_default_active_minus1: this value plus 1 specifies the
+ *                                        inferred value of num_ref_idx_l1_active_minus1
+ * @init_qp_minus26: this value plus 26 specifies the initial value of SliceQp Y for
+ *		     each slice referring to the PPS
+ * @diff_cu_qp_delta_depth: specifies the difference between the luma coding
+ *			    tree block size and the minimum luma coding block
+ *			    size of coding units that convey cu_qp_delta_abs
+ *			    and cu_qp_delta_sign_flag
+ * @pps_cb_qp_offset: specify the offsets to the luma quantization parameter Cb
+ * @pps_cr_qp_offset: specify the offsets to the luma quantization parameter Cr
+ * @num_tile_columns_minus1: this value plus 1 specifies the number of tile columns
+ *			     partitioning the picture
+ * @num_tile_rows_minus1: this value plus 1 specifies the number of tile rows partitioning
+ *			  the picture
+ * @column_width_minus1: this value plus 1 specifies the width of the each tile column in
+ *			 units of coding tree blocks
+ * @row_height_minus1: this value plus 1 specifies the height of the each tile row in
+ *		       units of coding tree blocks
+ * @pps_beta_offset_div2: specify the default deblocking parameter offsets for
+ *			  beta divided by 2
+ * @pps_tc_offset_div2: specify the default deblocking parameter offsets for tC
+ *			divided by 2
+ * @log2_parallel_merge_level_minus2: this value plus 2 specifies the value of
+ *                                    the variable Log2ParMrgLevel
+ * @reserved: padding field. Should be zeroed by applications.
+ * @flags: see V4L2_HEVC_PPS_FLAG_{}
+ */
+struct v4l2_ctrl_hevc_pps {
+	__u8	pic_parameter_set_id;
+	__u8	num_extra_slice_header_bits;
+	__u8	num_ref_idx_l0_default_active_minus1;
+	__u8	num_ref_idx_l1_default_active_minus1;
+	__s8	init_qp_minus26;
+	__u8	diff_cu_qp_delta_depth;
+	__s8	pps_cb_qp_offset;
+	__s8	pps_cr_qp_offset;
+	__u8	num_tile_columns_minus1;
+	__u8	num_tile_rows_minus1;
+	__u8	column_width_minus1[20];
+	__u8	row_height_minus1[22];
+	__s8	pps_beta_offset_div2;
+	__s8	pps_tc_offset_div2;
+	__u8	log2_parallel_merge_level_minus2;
+	__u8	reserved;
+	__u64	flags;
+};
+
+#define V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE	0x01
+
+#define V4L2_HEVC_SEI_PIC_STRUCT_FRAME				0
+#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_FIELD			1
+#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_FIELD			2
+#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_BOTTOM			3
+#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_TOP			4
+#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_BOTTOM_TOP			5
+#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM		6
+#define V4L2_HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING			7
+#define V4L2_HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING			8
+#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_PAIRED_PREVIOUS_BOTTOM	9
+#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_PAIRED_PREVIOUS_TOP	10
+#define V4L2_HEVC_SEI_PIC_STRUCT_TOP_PAIRED_NEXT_BOTTOM		11
+#define V4L2_HEVC_SEI_PIC_STRUCT_BOTTOM_PAIRED_NEXT_TOP		12
+
+#define V4L2_HEVC_DPB_ENTRIES_NUM_MAX		16
+
+/**
+ * struct v4l2_hevc_dpb_entry - HEVC decoded picture buffer entry
+ *
+ * @timestamp: timestamp of the V4L2 capture buffer to use as reference.
+ * @flags: long term flag for the reference frame
+ * @field_pic: whether the reference is a field picture or a frame.
+ * @reserved: padding field. Should be zeroed by applications.
+ * @pic_order_cnt_val: the picture order count of the current picture.
+ */
+struct v4l2_hevc_dpb_entry {
+	__u64	timestamp;
+	__u8	flags;
+	__u8	field_pic;
+	__u16	reserved;
+	__s32	pic_order_cnt_val;
+};
+
+/**
+ * struct v4l2_hevc_pred_weight_table - HEVC weighted prediction parameters
+ *
+ * @delta_luma_weight_l0: the difference of the weighting factor applied
+ *			  to the luma prediction value for list 0
+ * @luma_offset_l0: the additive offset applied to the luma prediction value
+ *		    for list 0
+ * @delta_chroma_weight_l0: the difference of the weighting factor applied
+ *			    to the chroma prediction values for list 0
+ * @chroma_offset_l0: the difference of the additive offset applied to
+ *		      the chroma prediction values for list 0
+ * @delta_luma_weight_l1: the difference of the weighting factor applied
+ *			  to the luma prediction value for list 1
+ * @luma_offset_l1: the additive offset applied to the luma prediction value
+ *		    for list 1
+ * @delta_chroma_weight_l1: the difference of the weighting factor applied
+ *			    to the chroma prediction values for list 1
+ * @chroma_offset_l1: the difference of the additive offset applied to
+ *		      the chroma prediction values for list 1
+ * @luma_log2_weight_denom: the base 2 logarithm of the denominator for
+ *			    all luma weighting factors
+ * @delta_chroma_log2_weight_denom: the difference of the base 2 logarithm
+ *				    of the denominator for all chroma
+ *				    weighting factors
+ */
+struct v4l2_hevc_pred_weight_table {
+	__s8	delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__s8	luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__s8	delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
+	__s8	chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
+
+	__s8	delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__s8	luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__s8	delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
+	__s8	chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
+
+	__u8	luma_log2_weight_denom;
+	__s8	delta_chroma_log2_weight_denom;
+};
+
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA		(1ULL << 0)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA		(1ULL << 1)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED	(1ULL << 2)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO			(1ULL << 3)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT			(1ULL << 4)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0		(1ULL << 5)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV		(1ULL << 6)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
+#define V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT	(1ULL << 9)
+
+/**
+ * struct v4l2_ctrl_hevc_slice_params - HEVC slice parameters
+ *
+ * This control is a dynamically sized 1-dimensional array,
+ * V4L2_CTRL_FLAG_DYNAMIC_ARRAY flag must be set when using it.
+ *
+ * @bit_size: size (in bits) of the current slice data
+ * @data_byte_offset: offset (in bytes) to the video data in the current slice data
+ * @num_entry_point_offsets: specifies the number of entry point offset syntax
+ *			     elements in the slice header.
+ * @nal_unit_type: specifies the coding type of the slice (B, P or I)
+ * @nuh_temporal_id_plus1: minus 1 specifies a temporal identifier for the NAL unit
+ * @slice_type: see V4L2_HEVC_SLICE_TYPE_{}
+ * @colour_plane_id: specifies the colour plane associated with the current slice
+ * @slice_pic_order_cnt: specifies the picture order count
+ * @num_ref_idx_l0_active_minus1: this value plus 1 specifies the maximum
+ *                                reference index for reference picture list 0
+ *                                that may be used to decode the slice
+ * @num_ref_idx_l1_active_minus1: this value plus 1 specifies the maximum
+ *                                reference index for reference picture list 1
+ *                                that may be used to decode the slice
+ * @collocated_ref_idx: specifies the reference index of the collocated picture used
+ *			for temporal motion vector prediction
+ * @five_minus_max_num_merge_cand: specifies the maximum number of merging
+ *				   motion vector prediction candidates supported in
+ *				   the slice subtracted from 5
+ * @slice_qp_delta: specifies the initial value of QpY to be used for the coding
+ *		    blocks in the slice
+ * @slice_cb_qp_offset: specifies a difference to be added to the value of pps_cb_qp_offset
+ * @slice_cr_qp_offset: specifies a difference to be added to the value of pps_cr_qp_offset
+ * @slice_act_y_qp_offset: screen content extension parameters
+ * @slice_act_cb_qp_offset: screen content extension parameters
+ * @slice_act_cr_qp_offset: screen content extension parameters
+ * @slice_beta_offset_div2: specify the deblocking parameter offsets for beta divided by 2
+ * @slice_tc_offset_div2: specify the deblocking parameter offsets for tC divided by 2
+ * @pic_struct: indicates whether a picture should be displayed as a frame or as one or
+ *		more fields
+ * @reserved0: padding field. Should be zeroed by applications.
+ * @slice_segment_addr: specifies the address of the first coding tree block in
+ *			the slice segment
+ * @ref_idx_l0: the list of L0 reference elements as indices in the DPB
+ * @ref_idx_l1: the list of L1 reference elements as indices in the DPB
+ * @short_term_ref_pic_set_size: specifies the size of short-term reference
+ *				 pictures set included in the SPS
+ * @long_term_ref_pic_set_size: specifies the size of long-term reference
+ *				pictures set include in the SPS
+ * @pred_weight_table: the prediction weight coefficients for inter-picture
+ *		       prediction
+ * @reserved1: padding field. Should be zeroed by applications.
+ * @flags: see V4L2_HEVC_SLICE_PARAMS_FLAG_{}
+ */
+struct v4l2_ctrl_hevc_slice_params {
+	__u32	bit_size;
+	__u32	data_byte_offset;
+	__u32	num_entry_point_offsets;
+
+	/* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
+	__u8	nal_unit_type;
+	__u8	nuh_temporal_id_plus1;
+
+	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+	__u8	slice_type;
+	__u8	colour_plane_id;
+	__s32	slice_pic_order_cnt;
+	__u8	num_ref_idx_l0_active_minus1;
+	__u8	num_ref_idx_l1_active_minus1;
+	__u8	collocated_ref_idx;
+	__u8	five_minus_max_num_merge_cand;
+	__s8	slice_qp_delta;
+	__s8	slice_cb_qp_offset;
+	__s8	slice_cr_qp_offset;
+	__s8	slice_act_y_qp_offset;
+	__s8	slice_act_cb_qp_offset;
+	__s8	slice_act_cr_qp_offset;
+	__s8	slice_beta_offset_div2;
+	__s8	slice_tc_offset_div2;
+
+	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */
+	__u8	pic_struct;
+
+	__u8	reserved0[3];
+	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+	__u32	slice_segment_addr;
+	__u8	ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__u8	ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__u16	short_term_ref_pic_set_size;
+	__u16	long_term_ref_pic_set_size;
+
+	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
+	struct v4l2_hevc_pred_weight_table pred_weight_table;
+
+	__u8	reserved1[2];
+	__u64	flags;
+};
+
+#define V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC		0x1
+#define V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC		0x2
+#define V4L2_HEVC_DECODE_PARAM_FLAG_NO_OUTPUT_OF_PRIOR  0x4
+
+/**
+ * struct v4l2_ctrl_hevc_decode_params - HEVC decode parameters
+ *
+ * @pic_order_cnt_val: picture order count
+ * @short_term_ref_pic_set_size: specifies the size of short-term reference
+ *				 pictures set included in the SPS of the first slice
+ * @long_term_ref_pic_set_size: specifies the size of long-term reference
+ *				pictures set include in the SPS of the first slice
+ * @num_active_dpb_entries: the number of entries in dpb
+ * @num_poc_st_curr_before: the number of reference pictures in the short-term
+ *			    set that come before the current frame
+ * @num_poc_st_curr_after: the number of reference pictures in the short-term
+ *			   set that come after the current frame
+ * @num_poc_lt_curr: the number of reference pictures in the long-term set
+ * @poc_st_curr_before: provides the index of the short term before references
+ *			in DPB array
+ * @poc_st_curr_after: provides the index of the short term after references
+ *		       in DPB array
+ * @poc_lt_curr: provides the index of the long term references in DPB array
+ * @num_delta_pocs_of_ref_rps_idx: same as the derived value NumDeltaPocs[RefRpsIdx],
+ *				   can be used to parse the RPS data in slice headers
+ *				   instead of skipping it with @short_term_ref_pic_set_size.
+ * @reserved: padding field. Should be zeroed by applications.
+ * @dpb: the decoded picture buffer, for meta-data about reference frames
+ * @flags: see V4L2_HEVC_DECODE_PARAM_FLAG_{}
+ */
+struct v4l2_ctrl_hevc_decode_params {
+	__s32	pic_order_cnt_val;
+	__u16	short_term_ref_pic_set_size;
+	__u16	long_term_ref_pic_set_size;
+	__u8	num_active_dpb_entries;
+	__u8	num_poc_st_curr_before;
+	__u8	num_poc_st_curr_after;
+	__u8	num_poc_lt_curr;
+	__u8	poc_st_curr_before[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__u8	poc_st_curr_after[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__u8	poc_lt_curr[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__u8	num_delta_pocs_of_ref_rps_idx;
+	__u8	reserved[3];
+	struct	v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	__u64	flags;
+};
+
+/**
+ * struct v4l2_ctrl_hevc_scaling_matrix - HEVC scaling lists parameters
+ *
+ * @scaling_list_4x4: scaling list is used for the scaling process for
+ *		      transform coefficients. The values on each scaling
+ *		      list are expected in raster scan order
+ * @scaling_list_8x8: scaling list is used for the scaling process for
+ *		      transform coefficients. The values on each scaling
+ *		      list are expected in raster scan order
+ * @scaling_list_16x16:	scaling list is used for the scaling process for
+ *			transform coefficients. The values on each scaling
+ *			list are expected in raster scan order
+ * @scaling_list_32x32:	scaling list is used for the scaling process for
+ *			transform coefficients. The values on each scaling
+ *			list are expected in raster scan order
+ * @scaling_list_dc_coef_16x16:	scaling list is used for the scaling process
+ *				for transform coefficients. The values on each
+ *				scaling list are expected in raster scan order.
+ * @scaling_list_dc_coef_32x32:	scaling list is used for the scaling process
+ *				for transform coefficients. The values on each
+ *				scaling list are expected in raster scan order.
+ */
+struct v4l2_ctrl_hevc_scaling_matrix {
+	__u8	scaling_list_4x4[6][16];
+	__u8	scaling_list_8x8[6][64];
+	__u8	scaling_list_16x16[6][64];
+	__u8	scaling_list_32x32[2][64];
+	__u8	scaling_list_dc_coef_16x16[6];
+	__u8	scaling_list_dc_coef_32x32[2];
+};
+
 #define V4L2_CID_COLORIMETRY_CLASS_BASE	(V4L2_CTRL_CLASS_COLORIMETRY | 0x900)
 #define V4L2_CID_COLORIMETRY_CLASS	(V4L2_CTRL_CLASS_COLORIMETRY | 1)
 
@@ -2299,6 +2835,645 @@ struct v4l2_ctrl_vp9_compressed_hdr {
 	struct v4l2_vp9_mv_probs mv;
 };
 
+/* Stateless AV1 controls */
+
+#define V4L2_AV1_TOTAL_REFS_PER_FRAME	8
+#define V4L2_AV1_CDEF_MAX		8
+#define V4L2_AV1_NUM_PLANES_MAX		3 /* 1 if monochrome, 3 otherwise */
+#define V4L2_AV1_MAX_SEGMENTS		8
+#define V4L2_AV1_MAX_OPERATING_POINTS	(1 << 5) /* 5 bits to encode */
+#define V4L2_AV1_REFS_PER_FRAME		7
+#define V4L2_AV1_MAX_NUM_Y_POINTS	(1 << 4) /* 4 bits to encode */
+#define V4L2_AV1_MAX_NUM_CB_POINTS	(1 << 4) /* 4 bits to encode */
+#define V4L2_AV1_MAX_NUM_CR_POINTS	(1 << 4) /* 4 bits to encode */
+#define V4L2_AV1_AR_COEFFS_SIZE		25 /* (2 * 3 * (3 + 1)) + 1 */
+#define V4L2_AV1_MAX_NUM_PLANES		3
+#define V4L2_AV1_MAX_TILE_COLS		64
+#define V4L2_AV1_MAX_TILE_ROWS		64
+#define V4L2_AV1_MAX_TILE_COUNT		512
+
+#define V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE		  0x00000001
+#define V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK	  0x00000002
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA	  0x00000004
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER   0x00000008
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND 0x00000010
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND	  0x00000020
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION	  0x00000040
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER	  0x00000080
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT	  0x00000100
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP		  0x00000200
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS	  0x00000400
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES		  0x00000800
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF		  0x00001000
+#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION	  0x00002000
+#define V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME		  0x00004000
+#define V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE		  0x00008000
+#define V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X		  0x00010000
+#define V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y		  0x00020000
+#define V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT  0x00040000
+#define V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q	  0x00080000
+
+#define V4L2_CID_STATELESS_AV1_SEQUENCE (V4L2_CID_CODEC_STATELESS_BASE + 500)
+/**
+ * struct v4l2_ctrl_av1_sequence - AV1 Sequence
+ *
+ * Represents an AV1 Sequence OBU. See section 5.5 "Sequence header OBU syntax"
+ * for more details.
+ *
+ * @flags: See V4L2_AV1_SEQUENCE_FLAG_{}.
+ * @seq_profile: specifies the features that can be used in the coded video
+ * sequence.
+ * @order_hint_bits: specifies the number of bits used for the order_hint field
+ * at each frame.
+ * @bit_depth: the bitdepth to use for the sequence as described in section
+ * 5.5.2 "Color config syntax".
+ * @reserved: padding field. Should be zeroed by applications.
+ * @max_frame_width_minus_1: specifies the maximum frame width minus 1 for the
+ * frames represented by this sequence header.
+ * @max_frame_height_minus_1: specifies the maximum frame height minus 1 for the
+ * frames represented by this sequence header.
+ */
+struct v4l2_ctrl_av1_sequence {
+	__u32 flags;
+	__u8 seq_profile;
+	__u8 order_hint_bits;
+	__u8 bit_depth;
+	__u8 reserved;
+	__u16 max_frame_width_minus_1;
+	__u16 max_frame_height_minus_1;
+};
+
+#define V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY (V4L2_CID_CODEC_STATELESS_BASE + 501)
+/**
+ * struct v4l2_ctrl_av1_tile_group_entry - AV1 Tile Group entry
+ *
+ * Represents a single AV1 tile inside an AV1 Tile Group. Note that MiRowStart,
+ * MiRowEnd, MiColStart and MiColEnd can be retrieved from struct
+ * v4l2_av1_tile_info in struct v4l2_ctrl_av1_frame using tile_row and
+ * tile_col. See section 6.10.1 "General tile group OBU semantics" for more
+ * details.
+ *
+ * @tile_offset: offset from the OBU data, i.e. where the coded tile data
+ * actually starts.
+ * @tile_size: specifies the size in bytes of the coded tile. Equivalent to
+ * "TileSize" in the AV1 Specification.
+ * @tile_row: specifies the row of the current tile. Equivalent to "TileRow" in
+ * the AV1 Specification.
+ * @tile_col: specifies the col of the current tile. Equivalent to "TileCol" in
+ * the AV1 Specification.
+ */
+struct v4l2_ctrl_av1_tile_group_entry {
+	__u32 tile_offset;
+	__u32 tile_size;
+	__u32 tile_row;
+	__u32 tile_col;
+};
+
+/**
+ * enum v4l2_av1_warp_model - AV1 Warp Model as described in section 3
+ * "Symbols and abbreviated terms" of the AV1 Specification.
+ *
+ * @V4L2_AV1_WARP_MODEL_IDENTITY: Warp model is just an identity transform.
+ * @V4L2_AV1_WARP_MODEL_TRANSLATION: Warp model is a pure translation.
+ * @V4L2_AV1_WARP_MODEL_ROTZOOM: Warp model is a rotation + symmetric zoom +
+ * translation.
+ * @V4L2_AV1_WARP_MODEL_AFFINE: Warp model is a general affine transform.
+ */
+enum v4l2_av1_warp_model {
+	V4L2_AV1_WARP_MODEL_IDENTITY = 0,
+	V4L2_AV1_WARP_MODEL_TRANSLATION = 1,
+	V4L2_AV1_WARP_MODEL_ROTZOOM = 2,
+	V4L2_AV1_WARP_MODEL_AFFINE = 3,
+};
+
+/**
+ * enum v4l2_av1_reference_frame - AV1 reference frames
+ *
+ * @V4L2_AV1_REF_INTRA_FRAME: Intra Frame Reference
+ * @V4L2_AV1_REF_LAST_FRAME: Last Reference Frame
+ * @V4L2_AV1_REF_LAST2_FRAME: Last2 Reference Frame
+ * @V4L2_AV1_REF_LAST3_FRAME: Last3 Reference Frame
+ * @V4L2_AV1_REF_GOLDEN_FRAME: Golden Reference Frame
+ * @V4L2_AV1_REF_BWDREF_FRAME: BWD Reference Frame
+ * @V4L2_AV1_REF_ALTREF2_FRAME: Alternative2 Reference Frame
+ * @V4L2_AV1_REF_ALTREF_FRAME: Alternative Reference Frame
+ */
+enum v4l2_av1_reference_frame {
+	V4L2_AV1_REF_INTRA_FRAME = 0,
+	V4L2_AV1_REF_LAST_FRAME = 1,
+	V4L2_AV1_REF_LAST2_FRAME = 2,
+	V4L2_AV1_REF_LAST3_FRAME = 3,
+	V4L2_AV1_REF_GOLDEN_FRAME = 4,
+	V4L2_AV1_REF_BWDREF_FRAME = 5,
+	V4L2_AV1_REF_ALTREF2_FRAME = 6,
+	V4L2_AV1_REF_ALTREF_FRAME = 7,
+};
+
+#define V4L2_AV1_GLOBAL_MOTION_IS_INVALID(ref) (1 << (ref))
+
+#define V4L2_AV1_GLOBAL_MOTION_FLAG_IS_GLOBAL	   0x1
+#define V4L2_AV1_GLOBAL_MOTION_FLAG_IS_ROT_ZOOM	   0x2
+#define V4L2_AV1_GLOBAL_MOTION_FLAG_IS_TRANSLATION 0x4
+/**
+ * struct v4l2_av1_global_motion - AV1 Global Motion parameters as described in
+ * section 6.8.17 "Global motion params semantics" of the AV1 specification.
+ *
+ * @flags: A bitfield containing the flags per reference frame. See
+ * V4L2_AV1_GLOBAL_MOTION_FLAG_{}
+ * @type: The type of global motion transform used.
+ * @params: this field has the same meaning as "gm_params" in the AV1
+ * specification.
+ * @invalid: bitfield indicating whether the global motion params are invalid
+ * for a given reference frame. See section 7.11.3.6 Setup shear process and
+ * the variable "warpValid". Use V4L2_AV1_GLOBAL_MOTION_IS_INVALID(ref) to
+ * create a suitable mask.
+ * @reserved: padding field. Should be zeroed by applications.
+ */
+
+struct v4l2_av1_global_motion {
+	__u8 flags[V4L2_AV1_TOTAL_REFS_PER_FRAME];
+	enum v4l2_av1_warp_model type[V4L2_AV1_TOTAL_REFS_PER_FRAME];
+	__s32 params[V4L2_AV1_TOTAL_REFS_PER_FRAME][6];
+	__u8 invalid;
+	__u8 reserved[3];
+};
+
+/**
+ * enum v4l2_av1_frame_restoration_type - AV1 Frame Restoration Type
+ * @V4L2_AV1_FRAME_RESTORE_NONE: no filtering is applied.
+ * @V4L2_AV1_FRAME_RESTORE_WIENER: Wiener filter process is invoked.
+ * @V4L2_AV1_FRAME_RESTORE_SGRPROJ: self guided filter process is invoked.
+ * @V4L2_AV1_FRAME_RESTORE_SWITCHABLE: restoration filter is swichtable.
+ */
+enum v4l2_av1_frame_restoration_type {
+	V4L2_AV1_FRAME_RESTORE_NONE = 0,
+	V4L2_AV1_FRAME_RESTORE_WIENER = 1,
+	V4L2_AV1_FRAME_RESTORE_SGRPROJ = 2,
+	V4L2_AV1_FRAME_RESTORE_SWITCHABLE = 3,
+};
+
+#define V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR		0x1
+#define V4L2_AV1_LOOP_RESTORATION_FLAG_USES_CHROMA_LR	0x2
+
+/**
+ * struct v4l2_av1_loop_restoration - AV1 Loop Restauration as described in
+ * section 6.10.15 "Loop restoration params semantics" of the AV1 specification.
+ *
+ * @flags: See V4L2_AV1_LOOP_RESTORATION_FLAG_{}.
+ * @lr_unit_shift: specifies if the luma restoration size should be halved.
+ * @lr_uv_shift: specifies if the chroma size should be half the luma size.
+ * @reserved: padding field. Should be zeroed by applications.
+ * @frame_restoration_type: specifies the type of restoration used for each
+ * plane. See enum v4l2_av1_frame_restoration_type.
+ * @loop_restoration_size: specifies the size of loop restoration units in units
+ * of samples in the current plane.
+ */
+struct v4l2_av1_loop_restoration {
+	__u8 flags;
+	__u8 lr_unit_shift;
+	__u8 lr_uv_shift;
+	__u8 reserved;
+	enum v4l2_av1_frame_restoration_type frame_restoration_type[V4L2_AV1_NUM_PLANES_MAX];
+	__u32 loop_restoration_size[V4L2_AV1_MAX_NUM_PLANES];
+};
+
+/**
+ * struct v4l2_av1_cdef - AV1 CDEF params semantics as described in section
+ * 6.10.14 "CDEF params semantics" of the AV1 specification
+ *
+ * @damping_minus_3: controls the amount of damping in the deringing filter.
+ * @bits: specifies the number of bits needed to specify which CDEF filter to
+ * apply.
+ * @y_pri_strength: specifies the strength of the primary filter.
+ * @y_sec_strength: specifies the strength of the secondary filter.
+ * @uv_pri_strength: specifies the strength of the primary filter.
+ * @uv_sec_strength: specifies the strength of the secondary filter.
+ */
+struct v4l2_av1_cdef {
+	__u8 damping_minus_3;
+	__u8 bits;
+	__u8 y_pri_strength[V4L2_AV1_CDEF_MAX];
+	__u8 y_sec_strength[V4L2_AV1_CDEF_MAX];
+	__u8 uv_pri_strength[V4L2_AV1_CDEF_MAX];
+	__u8 uv_sec_strength[V4L2_AV1_CDEF_MAX];
+};
+
+#define V4L2_AV1_SEGMENTATION_FLAG_ENABLED	   0x1
+#define V4L2_AV1_SEGMENTATION_FLAG_UPDATE_MAP	   0x2
+#define V4L2_AV1_SEGMENTATION_FLAG_TEMPORAL_UPDATE 0x4
+#define V4L2_AV1_SEGMENTATION_FLAG_UPDATE_DATA	   0x8
+#define V4L2_AV1_SEGMENTATION_FLAG_SEG_ID_PRE_SKIP 0x10
+
+/**
+ * enum v4l2_av1_segment_feature - AV1 segment features as described in section
+ * 3 "Symbols and abbreviated terms" of the AV1 specification.
+ *
+ * @V4L2_AV1_SEG_LVL_ALT_Q: Index for quantizer segment feature.
+ * @V4L2_AV1_SEG_LVL_ALT_LF_Y_V: Index for vertical luma loop filter segment
+ * feature.
+ * @V4L2_AV1_SEG_LVL_REF_FRAME: Index for reference frame segment feature.
+ * @V4L2_AV1_SEG_LVL_REF_SKIP: Index for skip segment feature.
+ * @V4L2_AV1_SEG_LVL_REF_GLOBALMV: Index for global mv feature.
+ * @V4L2_AV1_SEG_LVL_MAX: Number of segment features.
+ */
+enum v4l2_av1_segment_feature {
+	V4L2_AV1_SEG_LVL_ALT_Q = 0,
+	V4L2_AV1_SEG_LVL_ALT_LF_Y_V = 1,
+	V4L2_AV1_SEG_LVL_REF_FRAME = 5,
+	V4L2_AV1_SEG_LVL_REF_SKIP = 6,
+	V4L2_AV1_SEG_LVL_REF_GLOBALMV = 7,
+	V4L2_AV1_SEG_LVL_MAX = 8
+};
+
+#define V4L2_AV1_SEGMENT_FEATURE_ENABLED(id)	(1 << (id))
+
+/**
+ * struct v4l2_av1_segmentation - AV1 Segmentation params as defined in section
+ * 6.8.13 "Segmentation params semantics" of the AV1 specification.
+ *
+ * @flags: see V4L2_AV1_SEGMENTATION_FLAG_{}.
+ * @last_active_seg_id: indicates the highest numbered segment id that has some
+ * enabled feature. This is used when decoding the segment id to only decode
+ * choices corresponding to used segments.
+ * @feature_enabled: bitmask defining which features are enabled in each
+ * segment. Use V4L2_AV1_SEGMENT_FEATURE_ENABLED to build a suitable mask.
+ * @feature_data: data attached to each feature. Data entry is only valid if the
+ * feature is enabled
+ */
+struct v4l2_av1_segmentation {
+	__u8 flags;
+	__u8 last_active_seg_id;
+	__u8 feature_enabled[V4L2_AV1_MAX_SEGMENTS];
+	__s16 feature_data[V4L2_AV1_MAX_SEGMENTS][V4L2_AV1_SEG_LVL_MAX];
+};
+
+#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED    0x1
+#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE     0x2
+#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT 0x4
+#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI   0x8
+
+/**
+ * struct v4l2_av1_loop_filter - AV1 Loop filter params as defined in section
+ * 6.8.10 "Loop filter semantics" and 6.8.16 "Loop filter delta parameters
+ * semantics" of the AV1 specification.
+ *
+ * @flags: see V4L2_AV1_LOOP_FILTER_FLAG_{}
+ * @level: an array containing loop filter strength values. Different loop
+ * filter strength values from the array are used depending on the image plane
+ * being filtered, and the edge direction (vertical or horizontal) being
+ * filtered.
+ * @sharpness: indicates the sharpness level. The loop_filter_level and
+ * loop_filter_sharpness together determine when a block edge is filtered, and
+ * by how much the filtering can change the sample values. The loop filter
+ * process is described in section 7.14 of the AV1 specification.
+ * @ref_deltas: contains the adjustment needed for the filter level based on the
+ * chosen reference frame. If this syntax element is not present, it maintains
+ * its previous value.
+ * @mode_deltas: contains the adjustment needed for the filter level based on
+ * the chosen mode. If this syntax element is not present, it maintains its
+ * previous value.
+ * @delta_lf_res: specifies the left shift which should be applied to decoded
+ * loop filter delta values.
+ */
+struct v4l2_av1_loop_filter {
+	__u8 flags;
+	__u8 level[4];
+	__u8 sharpness;
+	__s8 ref_deltas[V4L2_AV1_TOTAL_REFS_PER_FRAME];
+	__s8 mode_deltas[2];
+	__u8 delta_lf_res;
+};
+
+#define V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA   0x1
+#define V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX   0x2
+#define V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT 0x4
+
+/**
+ * struct v4l2_av1_quantization - AV1 Quantization params as defined in section
+ * 6.8.11 "Quantization params semantics" of the AV1 specification.
+ *
+ * @flags: see V4L2_AV1_QUANTIZATION_FLAG_{}
+ * @base_q_idx: indicates the base frame qindex. This is used for Y AC
+ * coefficients and as the base value for the other quantizers.
+ * @delta_q_y_dc: indicates the Y DC quantizer relative to base_q_idx.
+ * @delta_q_u_dc: indicates the U DC quantizer relative to base_q_idx.
+ * @delta_q_u_ac: indicates the U AC quantizer relative to base_q_idx.
+ * @delta_q_v_dc: indicates the V DC quantizer relative to base_q_idx.
+ * @delta_q_v_ac: indicates the V AC quantizer relative to base_q_idx.
+ * @qm_y: specifies the level in the quantizer matrix that should be used for
+ * luma plane decoding.
+ * @qm_u: specifies the level in the quantizer matrix that should be used for
+ * chroma U plane decoding.
+ * @qm_v: specifies the level in the quantizer matrix that should be used for
+ * chroma V plane decoding.
+ * @delta_q_res: specifies the left shift which should be applied to decoded
+ * quantizer index delta values.
+ */
+struct v4l2_av1_quantization {
+	__u8 flags;
+	__u8 base_q_idx;
+	__s8 delta_q_y_dc;
+	__s8 delta_q_u_dc;
+	__s8 delta_q_u_ac;
+	__s8 delta_q_v_dc;
+	__s8 delta_q_v_ac;
+	__u8 qm_y;
+	__u8 qm_u;
+	__u8 qm_v;
+	__u8 delta_q_res;
+};
+
+#define V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING	0x1
+
+/**
+ * struct v4l2_av1_tile_info - AV1 Tile info as defined in section 6.8.14 "Tile
+ * info semantics" of the AV1 specification.
+ *
+ * @flags: see V4L2_AV1_TILE_INFO_FLAG_{}
+ * @context_update_tile_id: specifies which tile to use for the CDF update.
+ * @tile_rows: specifies the number of tiles down the frame.
+ * @tile_cols: specifies the number of tiles across the frame.
+ * @mi_col_starts: an array specifying the start column (in units of 4x4 luma
+ * samples) for each tile across the image.
+ * @mi_row_starts: an array specifying the start row (in units of 4x4 luma
+ * samples) for each tile down the image.
+ * @width_in_sbs_minus_1: specifies the width of a tile minus 1 in units of
+ * superblocks.
+ * @height_in_sbs_minus_1:  specifies the height of a tile minus 1 in units of
+ * superblocks.
+ * @tile_size_bytes: specifies the number of bytes needed to code each tile
+ * size.
+ * @reserved: padding field. Should be zeroed by applications.
+ */
+struct v4l2_av1_tile_info {
+	__u8 flags;
+	__u8 context_update_tile_id;
+	__u8 tile_cols;
+	__u8 tile_rows;
+	__u32 mi_col_starts[V4L2_AV1_MAX_TILE_COLS + 1];
+	__u32 mi_row_starts[V4L2_AV1_MAX_TILE_ROWS + 1];
+	__u32 width_in_sbs_minus_1[V4L2_AV1_MAX_TILE_COLS];
+	__u32 height_in_sbs_minus_1[V4L2_AV1_MAX_TILE_ROWS];
+	__u8 tile_size_bytes;
+	__u8 reserved[3];
+};
+
+/**
+ * enum v4l2_av1_frame_type - AV1 Frame Type
+ *
+ * @V4L2_AV1_KEY_FRAME: Key frame
+ * @V4L2_AV1_INTER_FRAME: Inter frame
+ * @V4L2_AV1_INTRA_ONLY_FRAME: Intra-only frame
+ * @V4L2_AV1_SWITCH_FRAME: Switch frame
+ */
+enum v4l2_av1_frame_type {
+	V4L2_AV1_KEY_FRAME = 0,
+	V4L2_AV1_INTER_FRAME = 1,
+	V4L2_AV1_INTRA_ONLY_FRAME = 2,
+	V4L2_AV1_SWITCH_FRAME = 3
+};
+
+/**
+ * enum v4l2_av1_interpolation_filter - AV1 interpolation filter types
+ *
+ * @V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP: eight tap filter
+ * @V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH: eight tap smooth filter
+ * @V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP: eight tap sharp filter
+ * @V4L2_AV1_INTERPOLATION_FILTER_BILINEAR: bilinear filter
+ * @V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE: filter selection is signaled at
+ * the block level
+ *
+ * See section 6.8.9 "Interpolation filter semantics" of the AV1 specification
+ * for more details.
+ */
+enum v4l2_av1_interpolation_filter {
+	V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP = 0,
+	V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH = 1,
+	V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP = 2,
+	V4L2_AV1_INTERPOLATION_FILTER_BILINEAR = 3,
+	V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE = 4,
+};
+
+/**
+ * enum v4l2_av1_tx_mode - AV1 Tx mode as described in section 6.8.21 "TX mode
+ * semantics" of the AV1 specification.
+ * @V4L2_AV1_TX_MODE_ONLY_4X4: the inverse transform will use only 4x4
+ * transforms
+ * @V4L2_AV1_TX_MODE_LARGEST: the inverse transform will use the largest
+ * transform size that fits inside the block
+ * @V4L2_AV1_TX_MODE_SELECT: the choice of transform size is specified
+ * explicitly for each block.
+ */
+enum v4l2_av1_tx_mode {
+	V4L2_AV1_TX_MODE_ONLY_4X4 = 0,
+	V4L2_AV1_TX_MODE_LARGEST = 1,
+	V4L2_AV1_TX_MODE_SELECT = 2
+};
+
+#define V4L2_AV1_FRAME_FLAG_SHOW_FRAME			 0x00000001
+#define V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME		 0x00000002
+#define V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE	 0x00000004
+#define V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE		 0x00000008
+#define V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS	 0x00000010
+#define V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV		 0x00000020
+#define V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC		 0x00000040
+#define V4L2_AV1_FRAME_FLAG_USE_SUPERRES		 0x00000080
+#define V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV	 0x00000100
+#define V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE	 0x00000200
+#define V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS		 0x00000400
+#define V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF 0x00000800
+#define V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION		 0x00001000
+#define V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT		 0x00002000
+#define V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET		 0x00004000
+#define V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED		 0x00008000
+#define V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT		 0x00010000
+#define V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE		 0x00020000
+#define V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT	 0x00040000
+#define V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING	 0x00080000
+
+#define V4L2_CID_STATELESS_AV1_FRAME (V4L2_CID_CODEC_STATELESS_BASE + 502)
+/**
+ * struct v4l2_ctrl_av1_frame - Represents an AV1 Frame Header OBU.
+ *
+ * @tile_info: tile info
+ * @quantization: quantization params
+ * @segmentation: segmentation params
+ * @superres_denom: the denominator for the upscaling ratio.
+ * @loop_filter: loop filter params
+ * @cdef: cdef params
+ * @skip_mode_frame: specifies the frames to use for compound prediction when
+ * skip_mode is equal to 1.
+ * @primary_ref_frame: specifies which reference frame contains the CDF values
+ * and other state that should be loaded at the start of the frame.
+ * @loop_restoration: loop restoration params
+ * @global_motion: global motion params
+ * @flags: see V4L2_AV1_FRAME_FLAG_{}
+ * @frame_type: specifies the AV1 frame type
+ * @order_hint: specifies OrderHintBits least significant bits of the expected
+ * output order for this frame.
+ * @upscaled_width: the upscaled width.
+ * @interpolation_filter: specifies the filter selection used for performing
+ * inter prediction.
+ * @tx_mode: specifies how the transform size is determined.
+ * @frame_width_minus_1: add 1 to get the frame's width.
+ * @frame_height_minus_1: add 1 to get the frame's height
+ * @render_width_minus_1: add 1 to get the render width of the frame in luma
+ * samples.
+ * @render_height_minus_1: add 1 to get the render height of the frame in luma
+ * samples.
+ * @current_frame_id: specifies the frame id number for the current frame. Frame
+ * id numbers are additional information that do not affect the decoding
+ * process, but provide decoders with a way of detecting missing reference
+ * frames so that appropriate action can be taken.
+ * @buffer_removal_time: specifies the frame removal time in units of DecCT clock
+ * ticks counted from the removal time of the last random access point for
+ * operating point opNum.
+ * @reserved: padding field. Should be zeroed by applications.
+ * @order_hints: specifies the expected output order hint for each reference
+ * frame. This field corresponds to the OrderHints variable from the
+ * specification (section 5.9.2 "Uncompressed header syntax"). As such, this is
+ * only used for non-intra frames and ignored otherwise. order_hints[0] is
+ * always ignored.
+ * @reference_frame_ts: the V4L2 timestamp of the reference frame slots.
+ * @ref_frame_idx: used to index into @reference_frame_ts when decoding
+ * inter-frames. The meaning of this array is the same as in the specification.
+ * The timestamp refers to the timestamp field in struct v4l2_buffer. Use
+ * v4l2_timeval_to_ns() to convert the struct timeval to a __u64.
+ * @refresh_frame_flags: contains a bitmask that specifies which reference frame
+ * slots will be updated with the current frame after it is decoded.
+ */
+struct v4l2_ctrl_av1_frame {
+	struct v4l2_av1_tile_info tile_info;
+	struct v4l2_av1_quantization quantization;
+	__u8 superres_denom;
+	struct v4l2_av1_segmentation segmentation;
+	struct v4l2_av1_loop_filter loop_filter;
+	struct v4l2_av1_cdef cdef;
+	__u8 skip_mode_frame[2];
+	__u8 primary_ref_frame;
+	struct v4l2_av1_loop_restoration loop_restoration;
+	struct v4l2_av1_global_motion global_motion;
+	__u32 flags;
+	enum v4l2_av1_frame_type frame_type;
+	__u32 order_hint;
+	__u32 upscaled_width;
+	enum v4l2_av1_interpolation_filter interpolation_filter;
+	enum v4l2_av1_tx_mode tx_mode;
+	__u32 frame_width_minus_1;
+	__u32 frame_height_minus_1;
+	__u16 render_width_minus_1;
+	__u16 render_height_minus_1;
+
+	__u32 current_frame_id;
+	__u32 buffer_removal_time[V4L2_AV1_MAX_OPERATING_POINTS];
+	__u8 reserved[4];
+	__u32 order_hints[V4L2_AV1_TOTAL_REFS_PER_FRAME];
+	__u64 reference_frame_ts[V4L2_AV1_TOTAL_REFS_PER_FRAME];
+	__s8 ref_frame_idx[V4L2_AV1_REFS_PER_FRAME];
+	__u8 refresh_frame_flags;
+};
+
+#define V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN 0x1
+#define V4L2_AV1_FILM_GRAIN_FLAG_UPDATE_GRAIN 0x2
+#define V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA 0x4
+#define V4L2_AV1_FILM_GRAIN_FLAG_OVERLAP 0x8
+#define V4L2_AV1_FILM_GRAIN_FLAG_CLIP_TO_RESTRICTED_RANGE 0x10
+
+#define V4L2_CID_STATELESS_AV1_FILM_GRAIN (V4L2_CID_CODEC_STATELESS_BASE + 505)
+/**
+ * struct v4l2_ctrl_av1_film_grain - AV1 Film Grain parameters.
+ *
+ * Film grain parameters as specified by section 6.8.20 of the AV1 Specification.
+ *
+ * @flags: see V4L2_AV1_FILM_GRAIN_{}.
+ * @cr_mult: represents a multiplier for the cr component used in derivation of
+ * the input index to the cr component scaling function.
+ * @grain_seed: specifies the starting value for the pseudo-random numbers used
+ * during film grain synthesis.
+ * @film_grain_params_ref_idx: indicates which reference frame contains the
+ * film grain parameters to be used for this frame.
+ * @num_y_points: specifies the number of points for the piece-wise linear
+ * scaling function of the luma component.
+ * @point_y_value: represents the x (luma value) coordinate for the i-th point
+ * of the piecewise linear scaling function for luma component. The values are
+ * signaled on the scale of 0..255. In case of 10 bit video, these values
+ * correspond to luma values divided by 4. In case of 12 bit video, these values
+ * correspond to luma values divided by 16.
+ * @point_y_scaling:  represents the scaling (output) value for the i-th point
+ * of the piecewise linear scaling function for luma component.
+ * @num_cb_points: specifies the number of points for the piece-wise linear
+ * scaling function of the cb component.
+ * @point_cb_value: represents the x coordinate for the i-th point of the
+ * piece-wise linear scaling function for cb component. The values are signaled
+ * on the scale of 0..255.
+ * @point_cb_scaling: represents the scaling (output) value for the i-th point
+ * of the piecewise linear scaling function for cb component.
+ * @num_cr_points: specifies represents the number of points for the piece-wise
+ * linear scaling function of the cr component.
+ * @point_cr_value:  represents the x coordinate for the i-th point of the
+ * piece-wise linear scaling function for cr component. The values are signaled
+ * on the scale of 0..255.
+ * @point_cr_scaling:  represents the scaling (output) value for the i-th point
+ * of the piecewise linear scaling function for cr component.
+ * @grain_scaling_minus_8: represents the shift – 8 applied to the values of the
+ * chroma component. The grain_scaling_minus_8 can take values of 0..3 and
+ * determines the range and quantization step of the standard deviation of film
+ * grain.
+ * @ar_coeff_lag: specifies the number of auto-regressive coefficients for luma
+ * and chroma.
+ * @ar_coeffs_y_plus_128: specifies auto-regressive coefficients used for the Y
+ * plane.
+ * @ar_coeffs_cb_plus_128: specifies auto-regressive coefficients used for the U
+ * plane.
+ * @ar_coeffs_cr_plus_128: specifies auto-regressive coefficients used for the V
+ * plane.
+ * @ar_coeff_shift_minus_6: specifies the range of the auto-regressive
+ * coefficients. Values of 0, 1, 2, and 3 correspond to the ranges for
+ * auto-regressive coefficients of [-2, 2), [-1, 1), [-0.5, 0.5) and [-0.25,
+ * 0.25) respectively.
+ * @grain_scale_shift: specifies how much the Gaussian random numbers should be
+ * scaled down during the grain synthesis process.
+ * @cb_mult: represents a multiplier for the cb component used in derivation of
+ * the input index to the cb component scaling function.
+ * @cb_luma_mult: represents a multiplier for the average luma component used in
+ * derivation of the input index to the cb component scaling function.
+ * @cr_luma_mult: represents a multiplier for the average luma component used in
+ * derivation of the input index to the cr component scaling function.
+ * @cb_offset: represents an offset used in derivation of the input index to the
+ * cb component scaling function.
+ * @cr_offset: represents an offset used in derivation of the input index to the
+ * cr component scaling function.
+ * @reserved: padding field. Should be zeroed by applications.
+ */
+struct v4l2_ctrl_av1_film_grain {
+	__u8 flags;
+	__u8 cr_mult;
+	__u16 grain_seed;
+	__u8 film_grain_params_ref_idx;
+	__u8 num_y_points;
+	__u8 point_y_value[V4L2_AV1_MAX_NUM_Y_POINTS];
+	__u8 point_y_scaling[V4L2_AV1_MAX_NUM_Y_POINTS];
+	__u8 num_cb_points;
+	__u8 point_cb_value[V4L2_AV1_MAX_NUM_CB_POINTS];
+	__u8 point_cb_scaling[V4L2_AV1_MAX_NUM_CB_POINTS];
+	__u8 num_cr_points;
+	__u8 point_cr_value[V4L2_AV1_MAX_NUM_CR_POINTS];
+	__u8 point_cr_scaling[V4L2_AV1_MAX_NUM_CR_POINTS];
+	__u8 grain_scaling_minus_8;
+	__u8 ar_coeff_lag;
+	__u8 ar_coeffs_y_plus_128[V4L2_AV1_AR_COEFFS_SIZE];
+	__u8 ar_coeffs_cb_plus_128[V4L2_AV1_AR_COEFFS_SIZE];
+	__u8 ar_coeffs_cr_plus_128[V4L2_AV1_AR_COEFFS_SIZE];
+	__u8 ar_coeff_shift_minus_6;
+	__u8 grain_scale_shift;
+	__u8 cb_mult;
+	__u8 cb_luma_mult;
+	__u8 cr_luma_mult;
+	__u16 cb_offset;
+	__u16 cr_offset;
+	__u8 reserved[4];
+};
+
 /* MPEG-compression definitions kept for backwards compatibility */
 #define V4L2_CTRL_CLASS_MPEG            V4L2_CTRL_CLASS_CODEC
 #define V4L2_CID_MPEG_CLASS             V4L2_CID_CODEC_CLASS
diff --git a/sys/v4l2/ext/videodev2.h b/sys/v4l2/ext/videodev2.h
index 16a9494c913b1a9f21b1d576bb4a01f70847c4df..2ceb1ca32d1e8ce027a21a24444faa514078290f 100644
--- a/sys/v4l2/ext/videodev2.h
+++ b/sys/v4l2/ext/videodev2.h
@@ -243,6 +243,7 @@ enum v4l2_colorspace {
 
 	/* DCI-P3 colorspace, used by cinema projectors */
 	V4L2_COLORSPACE_DCI_P3        = 12,
+
 };
 
 /*
@@ -474,7 +475,6 @@ struct v4l2_capability {
 #define V4L2_CAP_META_CAPTURE		0x00800000  /* Is a metadata capture device */
 
 #define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
-#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
 #define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */
 #define V4L2_CAP_META_OUTPUT		0x08000000  /* Is a metadata output device */
 
@@ -549,6 +549,13 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_RGBX32  v4l2_fourcc('X', 'B', '2', '4') /* 32  RGBX-8-8-8-8  */
 #define V4L2_PIX_FMT_ARGB32  v4l2_fourcc('B', 'A', '2', '4') /* 32  ARGB-8-8-8-8  */
 #define V4L2_PIX_FMT_XRGB32  v4l2_fourcc('B', 'X', '2', '4') /* 32  XRGB-8-8-8-8  */
+#define V4L2_PIX_FMT_RGBX1010102 v4l2_fourcc('R', 'X', '3', '0') /* 32  RGBX-10-10-10-2 */
+#define V4L2_PIX_FMT_RGBA1010102 v4l2_fourcc('R', 'A', '3', '0') /* 32  RGBA-10-10-10-2 */
+#define V4L2_PIX_FMT_ARGB2101010 v4l2_fourcc('A', 'R', '3', '0') /* 32  ARGB-2-10-10-10 */
+
+/* RGB formats (6 or 8 bytes per pixel) */
+#define V4L2_PIX_FMT_BGR48_12    v4l2_fourcc('B', '3', '1', '2') /* 48  BGR 12-bit per component */
+#define V4L2_PIX_FMT_ABGR64_12   v4l2_fourcc('B', '4', '1', '2') /* 64  BGRA 12-bit per component */
 
 /* Grey formats */
 #define V4L2_PIX_FMT_GREY    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
@@ -556,6 +563,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_Y6      v4l2_fourcc('Y', '0', '6', ' ') /*  6  Greyscale     */
 #define V4L2_PIX_FMT_Y10     v4l2_fourcc('Y', '1', '0', ' ') /* 10  Greyscale     */
 #define V4L2_PIX_FMT_Y12     v4l2_fourcc('Y', '1', '2', ' ') /* 12  Greyscale     */
+#define V4L2_PIX_FMT_Y012    v4l2_fourcc('Y', '0', '1', '2') /* 12  Greyscale     */
 #define V4L2_PIX_FMT_Y14     v4l2_fourcc('Y', '1', '4', ' ') /* 14  Greyscale     */
 #define V4L2_PIX_FMT_Y16     v4l2_fourcc('Y', '1', '6', ' ') /* 16  Greyscale     */
 #define V4L2_PIX_FMT_Y16_BE  v4l2_fourcc_be('Y', '1', '6', ' ') /* 16  Greyscale BE  */
@@ -563,6 +571,7 @@ struct v4l2_pix_format {
 /* Grey bit-packed formats */
 #define V4L2_PIX_FMT_Y10BPACK    v4l2_fourcc('Y', '1', '0', 'B') /* 10  Greyscale bit-packed */
 #define V4L2_PIX_FMT_Y10P    v4l2_fourcc('Y', '1', '0', 'P') /* 10  Greyscale, MIPI RAW10 packed */
+#define V4L2_PIX_FMT_IPU3_Y10		v4l2_fourcc('i', 'p', '3', 'y') /* IPU3 packed 10-bit greyscale */
 
 /* Palette formats */
 #define V4L2_PIX_FMT_PAL8    v4l2_fourcc('P', 'A', 'L', '8') /*  8  8-bit palette */
@@ -586,7 +595,18 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_XYUV32  v4l2_fourcc('X', 'Y', 'U', 'V') /* 32  XYUV-8-8-8-8  */
 #define V4L2_PIX_FMT_VUYA32  v4l2_fourcc('V', 'U', 'Y', 'A') /* 32  VUYA-8-8-8-8  */
 #define V4L2_PIX_FMT_VUYX32  v4l2_fourcc('V', 'U', 'Y', 'X') /* 32  VUYX-8-8-8-8  */
+#define V4L2_PIX_FMT_YUVA32  v4l2_fourcc('Y', 'U', 'V', 'A') /* 32  YUVA-8-8-8-8  */
+#define V4L2_PIX_FMT_YUVX32  v4l2_fourcc('Y', 'U', 'V', 'X') /* 32  YUVX-8-8-8-8  */
 #define V4L2_PIX_FMT_M420    v4l2_fourcc('M', '4', '2', '0') /* 12  YUV 4:2:0 2 lines y, 1 line uv interleaved */
+#define V4L2_PIX_FMT_YUV48_12    v4l2_fourcc('Y', '3', '1', '2') /* 48  YUV 4:4:4 12-bit per component */
+
+/*
+ * YCbCr packed format. For each Y2xx format, xx bits of valid data occupy the MSBs
+ * of the 16 bit components, and 16-xx bits of zero padding occupy the LSBs.
+ */
+#define V4L2_PIX_FMT_Y210    v4l2_fourcc('Y', '2', '1', '0') /* 32  YUYV 4:2:2 */
+#define V4L2_PIX_FMT_Y212    v4l2_fourcc('Y', '2', '1', '2') /* 32  YUYV 4:2:2 */
+#define V4L2_PIX_FMT_Y216    v4l2_fourcc('Y', '2', '1', '6') /* 32  YUYV 4:2:2 */
 
 /* two planes -- one Y, one Cr + Cb interleaved  */
 #define V4L2_PIX_FMT_NV12    v4l2_fourcc('N', 'V', '1', '2') /* 12  Y/CbCr 4:2:0  */
@@ -595,12 +615,15 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_NV61    v4l2_fourcc('N', 'V', '6', '1') /* 16  Y/CrCb 4:2:2  */
 #define V4L2_PIX_FMT_NV24    v4l2_fourcc('N', 'V', '2', '4') /* 24  Y/CbCr 4:4:4  */
 #define V4L2_PIX_FMT_NV42    v4l2_fourcc('N', 'V', '4', '2') /* 24  Y/CrCb 4:4:4  */
+#define V4L2_PIX_FMT_P010    v4l2_fourcc('P', '0', '1', '0') /* 24  Y/CbCr 4:2:0 10-bit per component */
+#define V4L2_PIX_FMT_P012    v4l2_fourcc('P', '0', '1', '2') /* 24  Y/CbCr 4:2:0 12-bit per component */
 
 /* two non contiguous planes - one Y, one Cr + Cb interleaved  */
 #define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
 #define V4L2_PIX_FMT_NV21M   v4l2_fourcc('N', 'M', '2', '1') /* 21  Y/CrCb 4:2:0  */
 #define V4L2_PIX_FMT_NV16M   v4l2_fourcc('N', 'M', '1', '6') /* 16  Y/CbCr 4:2:2  */
 #define V4L2_PIX_FMT_NV61M   v4l2_fourcc('N', 'M', '6', '1') /* 16  Y/CrCb 4:2:2  */
+#define V4L2_PIX_FMT_P012M   v4l2_fourcc('P', 'M', '1', '2') /* 24  Y/CbCr 4:2:0 12-bit per component */
 
 /* three planes - Y Cb, Cr */
 #define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y', 'U', 'V', '9') /*  9  YUV 4:1:0     */
@@ -622,6 +645,10 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_NV12_4L4 v4l2_fourcc('V', 'T', '1', '2')   /* 12  Y/CbCr 4:2:0  4x4 tiles */
 #define V4L2_PIX_FMT_NV12_16L16 v4l2_fourcc('H', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 16x16 tiles */
 #define V4L2_PIX_FMT_NV12_32L32 v4l2_fourcc('S', 'T', '1', '2') /* 12  Y/CbCr 4:2:0 32x32 tiles */
+#define V4L2_PIX_FMT_NV15_4L4 v4l2_fourcc('V', 'T', '1', '5') /* 15 Y/CbCr 4:2:0 10-bit 4x4 tiles */
+#define V4L2_PIX_FMT_P010_4L4 v4l2_fourcc('T', '0', '1', '0') /* 12  Y/CbCr 4:2:0 10-bit 4x4 macroblocks */
+#define V4L2_PIX_FMT_NV12_8L128       v4l2_fourcc('A', 'T', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */
+#define V4L2_PIX_FMT_NV12_10BE_8L128  v4l2_fourcc_be('A', 'X', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */
 
 /* Tiled YUV formats, non contiguous planes */
 #define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 tiles */
@@ -704,6 +731,11 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_FWHT     v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */
 #define V4L2_PIX_FMT_FWHT_STATELESS     v4l2_fourcc('S', 'F', 'W', 'H') /* Stateless FWHT (vicodec) */
 #define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */
+#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */
+#define V4L2_PIX_FMT_AV1_FRAME v4l2_fourcc('A', 'V', '1', 'F') /* AV1 parsed frame */
+#define V4L2_PIX_FMT_SPK      v4l2_fourcc('S', 'P', 'K', '0') /* Sorenson Spark */
+#define V4L2_PIX_FMT_RV30     v4l2_fourcc('R', 'V', '3', '0') /* RealVideo 8 */
+#define V4L2_PIX_FMT_RV40     v4l2_fourcc('R', 'V', '4', '0') /* RealVideo 9 & 10 */
 
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
@@ -740,8 +772,11 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_INZI     v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */
 #define V4L2_PIX_FMT_CNF4     v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */
 #define V4L2_PIX_FMT_HI240    v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */
+#define V4L2_PIX_FMT_QC08C    v4l2_fourcc('Q', '0', '8', 'C') /* Qualcomm 8-bit compressed */
+#define V4L2_PIX_FMT_QC10C    v4l2_fourcc('Q', '1', '0', 'C') /* Qualcomm 10-bit compressed */
+#define V4L2_PIX_FMT_AJPG     v4l2_fourcc('A', 'J', 'P', 'G') /* Aspeed JPEG */
 
-/* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
+/* 10bit raw packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
 #define V4L2_PIX_FMT_IPU3_SBGGR10	v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */
 #define V4L2_PIX_FMT_IPU3_SGBRG10	v4l2_fourcc('i', 'p', '3', 'g') /* IPU3 packed 10-bit GBRG bayer */
 #define V4L2_PIX_FMT_IPU3_SGRBG10	v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
@@ -1543,7 +1578,8 @@ struct v4l2_bt_timings {
 	((bt)->width + V4L2_DV_BT_BLANKING_WIDTH(bt))
 #define V4L2_DV_BT_BLANKING_HEIGHT(bt) \
 	((bt)->vfrontporch + (bt)->vsync + (bt)->vbackporch + \
-	 (bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch)
+	 ((bt)->interlaced ? \
+	  ((bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch) : 0))
 #define V4L2_DV_BT_FRAME_HEIGHT(bt) \
 	((bt)->height + V4L2_DV_BT_BLANKING_HEIGHT(bt))
 
@@ -1634,7 +1670,7 @@ struct v4l2_input {
 	__u8	     name[32];		/*  Label */
 	__u32	     type;		/*  Type of input */
 	__u32	     audioset;		/*  Associated audios (bitfield) */
-	__u32        tuner;             /*  enum v4l2_tuner_type */
+	__u32        tuner;             /*  Tuner index */
 	v4l2_std_id  std;
 	__u32	     status;
 	__u32	     capabilities;
@@ -1721,6 +1757,8 @@ struct v4l2_ext_control {
 		__u8 *p_u8;
 		__u16 *p_u16;
 		__u32 *p_u32;
+		__s32 *p_s32;
+		__s64 *p_s64;
 		struct v4l2_area *p_area;
 		struct v4l2_ctrl_h264_sps *p_h264_sps;
 		struct v4l2_ctrl_h264_pps *p_h264_pps;
@@ -1735,6 +1773,15 @@ struct v4l2_ext_control {
 		struct v4l2_ctrl_mpeg2_quantisation *p_mpeg2_quantisation;
 		struct v4l2_ctrl_vp9_compressed_hdr *p_vp9_compressed_hdr_probs;
 		struct v4l2_ctrl_vp9_frame *p_vp9_frame;
+		struct v4l2_ctrl_hevc_sps *p_hevc_sps;
+		struct v4l2_ctrl_hevc_pps *p_hevc_pps;
+		struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
+		struct v4l2_ctrl_hevc_scaling_matrix *p_hevc_scaling_matrix;
+		struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
+		struct v4l2_ctrl_av1_sequence *p_av1_sequence;
+		struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
+		struct v4l2_ctrl_av1_frame *p_av1_frame;
+		struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
 		void *ptr;
 	};
 } __attribute__ ((packed));
@@ -1752,9 +1799,7 @@ struct v4l2_ext_controls {
 };
 
 #define V4L2_CTRL_ID_MASK	  (0x0fffffff)
-#ifndef __KERNEL__
 #define V4L2_CTRL_ID2CLASS(id)    ((id) & 0x0fff0000UL)
-#endif
 #define V4L2_CTRL_ID2WHICH(id)    ((id) & 0x0fff0000UL)
 #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000)
 #define V4L2_CTRL_MAX_DIMS	  (4)
@@ -1800,6 +1845,17 @@ enum v4l2_ctrl_type {
 
 	V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR	= 0x0260,
 	V4L2_CTRL_TYPE_VP9_FRAME		= 0x0261,
+
+	V4L2_CTRL_TYPE_HEVC_SPS			= 0x0270,
+	V4L2_CTRL_TYPE_HEVC_PPS			= 0x0271,
+	V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS	= 0x0272,
+	V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX	= 0x0273,
+	V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS	= 0x0274,
+
+	V4L2_CTRL_TYPE_AV1_SEQUENCE	    = 0x280,
+	V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY = 0x281,
+	V4L2_CTRL_TYPE_AV1_FRAME	    = 0x282,
+	V4L2_CTRL_TYPE_AV1_FILM_GRAIN	    = 0x283,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
@@ -1855,6 +1911,7 @@ struct v4l2_querymenu {
 #define V4L2_CTRL_FLAG_HAS_PAYLOAD	0x0100
 #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE	0x0200
 #define V4L2_CTRL_FLAG_MODIFY_LAYOUT	0x0400
+#define V4L2_CTRL_FLAG_DYNAMIC_ARRAY	0x0800
 
 /*  Query flags, to be ORed with the control ID */
 #define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000
@@ -2362,6 +2419,7 @@ struct v4l2_event_vsync {
 #define V4L2_EVENT_CTRL_CH_VALUE		(1 << 0)
 #define V4L2_EVENT_CTRL_CH_FLAGS		(1 << 1)
 #define V4L2_EVENT_CTRL_CH_RANGE		(1 << 2)
+#define V4L2_EVENT_CTRL_CH_DIMENSIONS		(1 << 3)
 
 struct v4l2_event_ctrl {
 	__u32 changes;
@@ -2604,5 +2662,10 @@ struct v4l2_create_buffers {
 /* Deprecated definitions kept for backwards compatibility */
 #define V4L2_PIX_FMT_HM12 V4L2_PIX_FMT_NV12_16L16
 #define V4L2_PIX_FMT_SUNXI_TILED_NV12 V4L2_PIX_FMT_NV12_32L32
+/*
+ * This capability was never implemented, anyone using this cap should drop it
+ * from their code.
+ */
+#define V4L2_CAP_ASYNCIO 0x02000000
 
 #endif /* __LINUX_VIDEODEV2_H */
diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c
index 2d607094475a7f522c90ebe3cbe4b6083f89c4fb..3944a6a2b7ff639d0c3e1667bc855f275a7b5653 100644
--- a/sys/v4l2/gstv4l2.c
+++ b/sys/v4l2/gstv4l2.c
@@ -62,63 +62,6 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
 #define GST_CAT_DEFAULT v4l2_debug
 
 #ifdef GST_V4L2_ENABLE_PROBE
-/* This is a minimalist probe, for speed, we only enumerate formats */
-static GstCaps *
-gst_v4l2_probe_template_caps (const gchar * device, gint video_fd,
-    enum v4l2_buf_type type)
-{
-  gint n;
-  struct v4l2_fmtdesc format;
-  GstCaps *caps;
-
-  GST_DEBUG ("Getting %s format enumerations", device);
-  caps = gst_caps_new_empty ();
-
-  for (n = 0;; n++) {
-    GstStructure *template;
-
-    memset (&format, 0, sizeof (format));
-
-    format.index = n;
-    format.type = type;
-
-    if (ioctl (video_fd, VIDIOC_ENUM_FMT, &format) < 0)
-      break;                    /* end of enumeration */
-
-    GST_LOG ("index:       %u", format.index);
-    GST_LOG ("type:        %d", format.type);
-    GST_LOG ("flags:       %08x", format.flags);
-    GST_LOG ("description: '%s'", format.description);
-    GST_LOG ("pixelformat: %" GST_FOURCC_FORMAT,
-        GST_FOURCC_ARGS (format.pixelformat));
-
-    template = gst_v4l2_object_v4l2fourcc_to_structure (format.pixelformat);
-
-    if (template) {
-      GstStructure *alt_t = NULL;
-
-      switch (format.pixelformat) {
-        case V4L2_PIX_FMT_RGB32:
-          alt_t = gst_structure_copy (template);
-          gst_structure_set (alt_t, "format", G_TYPE_STRING, "ARGB", NULL);
-          break;
-        case V4L2_PIX_FMT_BGR32:
-          alt_t = gst_structure_copy (template);
-          gst_structure_set (alt_t, "format", G_TYPE_STRING, "BGRA", NULL);
-        default:
-          break;
-      }
-
-      gst_caps_append_structure (caps, template);
-
-      if (alt_t)
-        gst_caps_append_structure (caps, alt_t);
-    }
-  }
-
-  return gst_caps_simplify (caps);
-}
-
 static gboolean
 gst_v4l2_probe_and_register (GstPlugin * plugin)
 {
@@ -126,6 +69,7 @@ gst_v4l2_probe_and_register (GstPlugin * plugin)
   gint video_fd = -1;
   struct v4l2_capability vcap;
   guint32 device_caps;
+  enum v4l2_buf_type output_type, capture_type;
 
   v4l2_element_init (plugin);
 
@@ -140,6 +84,7 @@ gst_v4l2_probe_and_register (GstPlugin * plugin)
     if (video_fd >= 0)
       close (video_fd);
 
+    /* FIXME, missing libv4l2 support */
     video_fd = open (it->device_path, O_RDWR | O_CLOEXEC);
 
     if (video_fd == -1) {
@@ -150,7 +95,8 @@ gst_v4l2_probe_and_register (GstPlugin * plugin)
     memset (&vcap, 0, sizeof (vcap));
 
     if (ioctl (video_fd, VIDIOC_QUERYCAP, &vcap) < 0) {
-      GST_DEBUG ("Failed to get device capabilities: %s", g_strerror (errno));
+      GST_DEBUG ("Failed to get device '%s' capabilities: %s",
+          it->device_path, g_strerror (errno));
       continue;
     }
 
@@ -162,26 +108,33 @@ gst_v4l2_probe_and_register (GstPlugin * plugin)
     if (!GST_V4L2_IS_M2M (device_caps))
       continue;
 
+    if (device_caps & V4L2_CAP_VIDEO_M2M_MPLANE) {
+      output_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+      capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+    } else {
+      output_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+      capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    }
+
     GST_DEBUG ("Probing '%s' located at '%s'",
         it->device_name ? it->device_name : (const gchar *) vcap.driver,
         it->device_path);
 
     /* get sink supported format (no MPLANE for codec) */
-    sink_caps = gst_caps_merge (gst_v4l2_probe_template_caps (it->device_path,
-            video_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT),
-        gst_v4l2_probe_template_caps (it->device_path, video_fd,
-            V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE));
-
+    sink_caps = gst_v4l2_object_probe_template_caps (it->device_path,
+        video_fd, output_type);
     /* get src supported format */
-    src_caps = gst_caps_merge (gst_v4l2_probe_template_caps (it->device_path,
-            video_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE),
-        gst_v4l2_probe_template_caps (it->device_path, video_fd,
-            V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE));
+    src_caps = gst_v4l2_object_probe_template_caps (it->device_path,
+        video_fd, capture_type);
 
     /* Skip devices without any supported formats */
     if (gst_caps_is_empty (sink_caps) || gst_caps_is_empty (src_caps)) {
       gst_caps_unref (sink_caps);
       gst_caps_unref (src_caps);
+
+      GST_DEBUG ("Skipping unsupported device '%s' located at '%s'",
+          it->device_name ? it->device_name : (const gchar *) vcap.driver,
+          it->device_path);
       continue;
     }
 
diff --git a/sys/v4l2/gstv4l2allocator.c b/sys/v4l2/gstv4l2allocator.c
index 48d1bb86e84767394259f518b26169dbc815333a..aa54b9a0d7b910f7d8888e83ab63525aafb30405 100644
--- a/sys/v4l2/gstv4l2allocator.c
+++ b/sys/v4l2/gstv4l2allocator.c
@@ -154,7 +154,7 @@ _v4l2mem_new (GstMemoryFlags flags, GstAllocator * allocator,
 {
   GstV4l2Memory *mem;
 
-  mem = g_slice_new0 (GstV4l2Memory);
+  mem = g_new0 (GstV4l2Memory, 1);
   gst_memory_init (GST_MEMORY_CAST (mem),
       flags, allocator, parent, maxsize, align, offset, size);
 
@@ -236,7 +236,7 @@ gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group)
       gst_memory_unref (mem);
   }
 
-  g_slice_free (GstV4l2MemoryGroup, group);
+  g_free (group);
 }
 
 static GstV4l2MemoryGroup *
@@ -248,7 +248,7 @@ gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
   GstV4l2MemoryGroup *group;
   gsize img_size, buf_size;
 
-  group = g_slice_new0 (GstV4l2MemoryGroup);
+  group = g_new0 (GstV4l2MemoryGroup, 1);
 
   group->buffer.type = format->type;
   group->buffer.index = index;
@@ -268,10 +268,17 @@ gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
     GST_ERROR_OBJECT (allocator, "Buffer index returned by VIDIOC_QUERYBUF "
         "didn't match, this indicate the presence of a bug in your driver or "
         "libv4l2");
-    g_slice_free (GstV4l2MemoryGroup, group);
+    g_free (group);
     return NULL;
   }
 
+  if (IS_QUEUED (group->buffer)) {
+    GST_WARNING_OBJECT (allocator,
+        "Driver pretends buffer %d is queued even if freshly created, "
+        "this indicates a bug in the driver.", group->buffer.index);
+    UNSET_QUEUED (group->buffer);
+  }
+
   /* Check that provided size matches the format we have negotiation. Failing
    * there usually means a driver of libv4l bug. */
   if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
@@ -357,7 +364,6 @@ gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
 
   switch (allocator->memory) {
     case V4L2_MEMORY_DMABUF:
-      close (mem->dmafd);
       mem->dmafd = -1;
       break;
     case V4L2_MEMORY_USERPTR:
@@ -375,7 +381,7 @@ gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
   }
 
   /* Keep last, allocator may be freed after this call */
-  g_object_unref (allocator);
+  gst_object_unref (allocator);
 }
 
 static void
@@ -396,12 +402,11 @@ gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem)
         obj->munmap (mem->data, group->planes[mem->plane].length);
     }
 
-    /* This apply for both mmap with expbuf, and dmabuf imported memory */
-    if (mem->dmafd >= 0)
+    if (allocator->memory == V4L2_MEMORY_MMAP && mem->dmafd >= 0)
       close (mem->dmafd);
   }
 
-  g_slice_free (GstV4l2Memory, mem);
+  g_free (mem);
 }
 
 static void
@@ -1399,7 +1404,7 @@ gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator,
 error:
   if (errno == EPIPE) {
     GST_DEBUG_OBJECT (allocator, "broken pipe signals last buffer");
-    return GST_FLOW_EOS;
+    return GST_V4L2_FLOW_LAST_BUFFER;
   }
 
   GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s",
diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c
index 5f564e5688de3ebdb0a9624d23c7c25cd757df4d..f422ce7a4700c55945ac674926fcccfb64ad22f5 100644
--- a/sys/v4l2/gstv4l2bufferpool.c
+++ b/sys/v4l2/gstv4l2bufferpool.c
@@ -84,7 +84,8 @@ static void gst_v4l2_buffer_pool_complete_release_buffer (GstBufferPool * bpool,
     GstBuffer * buffer, gboolean queued);
 
 static gboolean
-gst_v4l2_is_buffer_valid (GstBuffer * buffer, GstV4l2MemoryGroup ** out_group)
+gst_v4l2_is_buffer_valid (GstBuffer * buffer, GstV4l2MemoryGroup ** out_group,
+    gboolean check_writable)
 {
   GstMemory *mem = gst_buffer_peek_memory (buffer, 0);
   gboolean valid = FALSE;
@@ -108,7 +109,7 @@ gst_v4l2_is_buffer_valid (GstBuffer * buffer, GstV4l2MemoryGroup ** out_group)
       if (group->mem[i] != gst_buffer_peek_memory (buffer, i))
         goto done;
 
-      if (!gst_memory_is_writable (group->mem[i]))
+      if (check_writable && !gst_memory_is_writable (group->mem[i]))
         goto done;
     }
 
@@ -127,7 +128,7 @@ gst_v4l2_buffer_pool_resize_buffer (GstBufferPool * bpool, GstBuffer * buffer)
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
   GstV4l2MemoryGroup *group;
 
-  if (gst_v4l2_is_buffer_valid (buffer, &group)) {
+  if (gst_v4l2_is_buffer_valid (buffer, &group, TRUE)) {
     gst_v4l2_allocator_reset_group (pool->vallocator, group);
   } else {
     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY);
@@ -164,16 +165,18 @@ gst_v4l2_buffer_pool_copy_buffer (GstV4l2BufferPool * pool, GstBuffer * dest,
     gst_video_frame_unmap (&dest_frame);
   } else {
     GstMapInfo map;
+    gsize filled_size;
 
     GST_DEBUG_OBJECT (pool, "copy raw bytes");
 
     if (!gst_buffer_map (src, &map, GST_MAP_READ))
       goto invalid_buffer;
 
-    gst_buffer_fill (dest, 0, map.data, gst_buffer_get_size (src));
+    filled_size =
+        gst_buffer_fill (dest, 0, map.data, gst_buffer_get_size (src));
 
     gst_buffer_unmap (src, &map);
-    gst_buffer_resize (dest, 0, gst_buffer_get_size (src));
+    gst_buffer_resize (dest, 0, filled_size);
   }
 
   gst_buffer_copy_into (dest, src,
@@ -220,7 +223,7 @@ _unmap_userptr_frame (struct UserPtrData *data)
   if (data->buffer)
     gst_buffer_unref (data->buffer);
 
-  g_slice_free (struct UserPtrData, data);
+  g_free (data);
 }
 
 static GstFlowReturn
@@ -236,7 +239,7 @@ gst_v4l2_buffer_pool_import_userptr (GstV4l2BufferPool * pool,
   GST_LOG_OBJECT (pool, "importing userptr");
 
   /* get the group */
-  if (!gst_v4l2_is_buffer_valid (dest, &group))
+  if (!gst_v4l2_is_buffer_valid (dest, &group, TRUE))
     goto not_our_buffer;
 
   if (V4L2_TYPE_IS_OUTPUT (pool->obj->type))
@@ -244,7 +247,7 @@ gst_v4l2_buffer_pool_import_userptr (GstV4l2BufferPool * pool,
   else
     flags = GST_MAP_WRITE;
 
-  data = g_slice_new0 (struct UserPtrData);
+  data = g_new0 (struct UserPtrData, 1);
 
   if (finfo && (finfo->format != GST_VIDEO_FORMAT_UNKNOWN &&
           finfo->format != GST_VIDEO_FORMAT_ENCODED)) {
@@ -325,7 +328,7 @@ not_our_buffer:
 invalid_buffer:
   {
     GST_ERROR_OBJECT (pool, "could not map buffer");
-    g_slice_free (struct UserPtrData, data);
+    g_free (data);
     return GST_FLOW_ERROR;
   }
 non_contiguous_mem:
@@ -353,7 +356,7 @@ gst_v4l2_buffer_pool_import_dmabuf (GstV4l2BufferPool * pool,
 
   GST_LOG_OBJECT (pool, "importing dmabuf");
 
-  if (!gst_v4l2_is_buffer_valid (dest, &group))
+  if (!gst_v4l2_is_buffer_valid (dest, &group, TRUE))
     goto not_our_buffer;
 
   if (n_mem > GST_VIDEO_MAX_PLANES)
@@ -682,9 +685,11 @@ gst_v4l2_buffer_pool_streamon (GstV4l2BufferPool * pool)
         guint num_queued;
         guint i, n = 0;
 
+        GST_OBJECT_LOCK (pool);
         num_queued = g_atomic_int_get (&pool->num_queued);
         if (num_queued < pool->num_allocated)
           n = pool->num_allocated - num_queued;
+        GST_OBJECT_UNLOCK (pool);
 
         /* For captures, we need to enqueue buffers before we start streaming,
          * so the driver don't underflow immediately. As we have put then back
@@ -1030,7 +1035,9 @@ gst_v4l2_buffer_pool_orphan (GstV4l2Object * v4l2object)
   GstV4l2BufferPool *pool;
   gboolean ret;
 
-  g_return_val_if_fail (bpool, FALSE);
+  /* Nothing to do if there is no pool */
+  if (!bpool)
+    return TRUE;
 
   pool = GST_V4L2_BUFFER_POOL (bpool);
 
@@ -1077,7 +1084,7 @@ gst_v4l2_buffer_pool_flush_start (GstBufferPool * bpool)
 
   GST_DEBUG_OBJECT (pool, "start flushing");
 
-  gst_poll_set_flushing (pool->poll, TRUE);
+  gst_poll_set_flushing (pool->obj->poll, TRUE);
 
   GST_OBJECT_LOCK (pool);
   pool->empty = FALSE;
@@ -1098,13 +1105,12 @@ gst_v4l2_buffer_pool_flush_stop (GstBufferPool * bpool)
   if (pool->other_pool && gst_buffer_pool_is_active (pool->other_pool))
     gst_buffer_pool_set_flushing (pool->other_pool, FALSE);
 
-  gst_poll_set_flushing (pool->poll, FALSE);
+  gst_poll_set_flushing (pool->obj->poll, FALSE);
 }
 
 static GstFlowReturn
 gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait)
 {
-  gint ret;
   GstClockTime timeout;
 
   if (wait)
@@ -1119,7 +1125,7 @@ gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait)
 
     if (!wait && pool->empty) {
       GST_OBJECT_UNLOCK (pool);
-      goto no_buffers;
+      return GST_V4L2_FLOW_LAST_BUFFER;
     }
 
     while (pool->empty)
@@ -1128,87 +1134,14 @@ gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait)
     GST_OBJECT_UNLOCK (pool);
   }
 
-  if (!pool->can_poll_device) {
+  if (!pool->obj->can_poll_device) {
     if (wait)
-      goto done;
+      return GST_FLOW_OK;
     else
-      goto no_buffers;
-  }
-
-  GST_LOG_OBJECT (pool, "polling device");
-
-again:
-  ret = gst_poll_wait (pool->poll, timeout);
-  if (G_UNLIKELY (ret < 0)) {
-    switch (errno) {
-      case EBUSY:
-        goto stopped;
-      case EAGAIN:
-      case EINTR:
-        goto again;
-      case ENXIO:
-        GST_WARNING_OBJECT (pool,
-            "v4l2 device doesn't support polling. Disabling"
-            " using libv4l2 in this case may cause deadlocks");
-        pool->can_poll_device = FALSE;
-        goto done;
-      default:
-        goto select_error;
-    }
-  }
-
-  if (gst_poll_fd_has_error (pool->poll, &pool->pollfd))
-    goto select_error;
-
-  /* PRI is used to signal that events are available */
-  if (gst_poll_fd_has_pri (pool->poll, &pool->pollfd)) {
-    struct v4l2_event event = { 0, };
-
-    if (!gst_v4l2_dequeue_event (pool->obj, &event))
-      goto dqevent_failed;
-
-    if (event.type != V4L2_EVENT_SOURCE_CHANGE) {
-      GST_INFO_OBJECT (pool, "Received unhandled event, ignoring.");
-      goto again;
-    }
-
-    if ((event.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) == 0) {
-      GST_DEBUG_OBJECT (pool,
-          "Received non-resolution source-change, ignoring.");
-      goto again;
-    }
-
-    return GST_V4L2_FLOW_RESOLUTION_CHANGE;
+      return GST_V4L2_FLOW_LAST_BUFFER;
   }
 
-  if (ret == 0)
-    goto no_buffers;
-
-done:
-  return GST_FLOW_OK;
-
-  /* ERRORS */
-stopped:
-  {
-    GST_DEBUG_OBJECT (pool, "stop called");
-    return GST_FLOW_FLUSHING;
-  }
-select_error:
-  {
-    GST_ELEMENT_ERROR (pool->obj->element, RESOURCE, READ, (NULL),
-        ("poll error %d: %s (%d)", ret, g_strerror (errno), errno));
-    return GST_FLOW_ERROR;
-  }
-no_buffers:
-  {
-    return GST_V4L2_FLOW_LAST_BUFFER;
-  }
-dqevent_failed:
-  {
-    GST_ELEMENT_ERROR (pool->obj->element, RESOURCE, READ, (NULL),
-        ("dqevent error: %s (%d)", g_strerror (errno), errno));
-    return GST_FLOW_ERROR;
-  }
+  return gst_v4l2_object_poll (pool->obj, timeout);
 }
 
 static GstFlowReturn
@@ -1219,6 +1152,8 @@ gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf,
   gint old_buffer_state;
   gint index;
 
+  GST_OBJECT_LOCK (pool);
+
   index = group->buffer.index;
 
   old_buffer_state =
@@ -1254,8 +1189,6 @@ gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf,
     }
   }
 
-  GST_OBJECT_LOCK (pool);
-
   /* If the pool was orphaned, don't try to queue any returned buffers.
    * This is done with the objet lock in order to synchronize with
    * orphaning. */
@@ -1277,6 +1210,7 @@ gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf,
 already_queued:
   {
     GST_ERROR_OBJECT (pool, "the buffer %i was already queued", index);
+    GST_OBJECT_UNLOCK (pool);
     return GST_FLOW_ERROR;
   }
 was_orphaned:
@@ -1304,13 +1238,13 @@ static GstFlowReturn
 gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
     gboolean * outstanding, gboolean wait)
 {
+  GstBufferPool *bpool = GST_BUFFER_POOL_CAST (pool);
   GstFlowReturn res;
   GstBuffer *outbuf = NULL;
   GstV4l2Object *obj = pool->obj;
   GstClockTime timestamp;
   GstV4l2MemoryGroup *group;
-  GstVideoMeta *vmeta;
-  gsize size;
+  const GstVideoInfo *info = &obj->info;
   gint i;
   gint old_buffer_state;
 
@@ -1330,7 +1264,7 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
   GST_LOG_OBJECT (pool, "dequeueing a buffer");
 
   res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group);
-  if (res == GST_FLOW_EOS)
+  if (res == GST_V4L2_FLOW_LAST_BUFFER)
     goto eos;
   if (res != GST_FLOW_OK)
     goto dqbuf_failed;
@@ -1359,11 +1293,19 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
     GST_OBJECT_UNLOCK (pool);
   }
 
+  if (group->buffer.flags & V4L2_BUF_FLAG_LAST &&
+      group->planes[0].bytesused == 0) {
+    GST_DEBUG_OBJECT (pool, "Empty last buffer, signalling eos.");
+    gst_v4l2_buffer_pool_complete_release_buffer (bpool, outbuf, FALSE);
+    *buffer = NULL;
+    goto eos;
+  }
+
   timestamp = GST_TIMEVAL_TO_TIME (group->buffer.timestamp);
 
-  size = 0;
-  vmeta = gst_buffer_get_video_meta (outbuf);
   for (i = 0; i < group->n_mem; i++) {
+    const GstVideoFormatInfo *finfo = info->finfo;
+
     GST_LOG_OBJECT (pool,
         "dequeued buffer %p seq:%d (ix=%d), mem %p used %d, plane=%d, flags %08x, ts %"
         GST_TIME_FORMAT ", pool-queued=%d, buffer=%p, previous-state=%i",
@@ -1371,9 +1313,20 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
         group->planes[i].bytesused, i, group->buffer.flags,
         GST_TIME_ARGS (timestamp), pool->num_queued, outbuf, old_buffer_state);
 
-    if (vmeta) {
-      vmeta->offset[i] = size;
-      size += gst_memory_get_sizes (group->mem[i], NULL, NULL);
+    if (GST_VIDEO_INFO_FORMAT (&pool->caps_info) == GST_VIDEO_FORMAT_ENCODED)
+      break;
+
+    if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+        obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+      /* Ensure our offset matches the expected plane size, or image size if
+       * there is only one memory */
+      if (group->n_mem == 1) {
+        gst_memory_resize (group->mem[0], 0, info->size + info->offset[0]);
+        break;
+      }
+
+      if (!GST_VIDEO_FORMAT_INFO_IS_TILED (finfo))
+        gst_memory_resize (group->mem[i], 0, obj->plane_size[i]);
     }
   }
 
@@ -1450,7 +1403,7 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
       break;
   }
 
-  if (GST_VIDEO_INFO_FORMAT (&obj->info) == GST_VIDEO_FORMAT_ENCODED) {
+  if (!gst_v4l2_object_is_raw (obj)) {
     if ((group->buffer.flags & V4L2_BUF_FLAG_KEYFRAME) ||
         GST_V4L2_PIXELFORMAT (obj) == V4L2_PIX_FMT_MJPEG ||
         GST_V4L2_PIXELFORMAT (obj) == V4L2_PIX_FMT_JPEG ||
@@ -1480,7 +1433,7 @@ poll_failed:
   }
 eos:
   {
-    return GST_FLOW_EOS;
+    return GST_V4L2_FLOW_LAST_BUFFER;
   }
 dqbuf_failed:
   {
@@ -1574,7 +1527,7 @@ done:
   /* Mark buffer as outstanding */
   if (ret == GST_FLOW_OK) {
     GstV4l2MemoryGroup *group;
-    if (gst_v4l2_is_buffer_valid (*buffer, &group)) {
+    if (gst_v4l2_is_buffer_valid (*buffer, &group, TRUE)) {
       GST_LOG_OBJECT (pool, "mark buffer %u outstanding", group->buffer.index);
       g_atomic_int_or (&pool->buffer_state[group->buffer.index],
           BUFFER_STATE_OUTSTANDING);
@@ -1625,7 +1578,7 @@ gst_v4l2_buffer_pool_complete_release_buffer (GstBufferPool * bpool,
         case GST_V4L2_IO_DMABUF_IMPORT:
         {
           GstV4l2MemoryGroup *group;
-          if (gst_v4l2_is_buffer_valid (buffer, &group)) {
+          if (gst_v4l2_is_buffer_valid (buffer, &group, TRUE)) {
             GstFlowReturn ret = GST_FLOW_OK;
 
             gst_v4l2_allocator_reset_group (pool->vallocator, group);
@@ -1666,7 +1619,7 @@ gst_v4l2_buffer_pool_complete_release_buffer (GstBufferPool * bpool,
           GstV4l2MemoryGroup *group;
           guint index;
 
-          if (!gst_v4l2_is_buffer_valid (buffer, &group)) {
+          if (!gst_v4l2_is_buffer_valid (buffer, &group, TRUE)) {
             /* Simply release invalid/modified buffer, the allocator will
              * give it back later */
             GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY);
@@ -1717,7 +1670,7 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
   GstV4l2MemoryGroup *group;
   gboolean queued = FALSE;
 
-  if (gst_v4l2_is_buffer_valid (buffer, &group)) {
+  if (gst_v4l2_is_buffer_valid (buffer, &group, TRUE)) {
     gint old_buffer_state =
         g_atomic_int_and (&pool->buffer_state[group->buffer.index],
         ~BUFFER_STATE_OUTSTANDING);
@@ -1757,8 +1710,6 @@ gst_v4l2_buffer_pool_finalize (GObject * object)
   if (pool->video_fd >= 0)
     pool->obj->close (pool->video_fd);
 
-  gst_poll_free (pool->poll);
-
   /* This can't be done in dispose method because we must not set pointer
    * to NULL as it is part of the v4l2object and dispose could be called
    * multiple times */
@@ -1774,8 +1725,6 @@ gst_v4l2_buffer_pool_finalize (GObject * object)
 static void
 gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool)
 {
-  pool->poll = gst_poll_new (TRUE);
-  pool->can_poll_device = TRUE;
   g_cond_init (&pool->empty_cond);
   pool->empty = TRUE;
   pool->orphaned = FALSE;
@@ -1838,17 +1787,8 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
   g_object_ref_sink (pool);
   g_free (name);
 
-  gst_poll_fd_init (&pool->pollfd);
-  pool->pollfd.fd = fd;
-  gst_poll_add_fd (pool->poll, &pool->pollfd);
-  if (V4L2_TYPE_IS_OUTPUT (obj->type))
-    gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE);
-  else
-    gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE);
-
   pool->video_fd = fd;
   pool->obj = obj;
-  pool->can_poll_device = TRUE;
 
   pool->vallocator = gst_v4l2_allocator_new (GST_OBJECT (pool), obj);
   if (pool->vallocator == NULL)
@@ -2004,8 +1944,9 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
             /* If we have no more buffer, and can allocate it time to do so */
             if (num_queued == 0) {
               if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) {
+                GST_DEBUG_OBJECT (pool, "Resurrect for empty queue");
                 ret = gst_v4l2_buffer_pool_resurrect_buffer (pool);
-                if (ret == GST_FLOW_OK)
+                if (ret == GST_FLOW_OK || ret == GST_FLOW_FLUSHING)
                   goto done;
               }
             }
@@ -2015,8 +1956,9 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
               GstBuffer *copy;
 
               if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) {
+                GST_DEBUG_OBJECT (pool, "Resurrect for threshold");
                 ret = gst_v4l2_buffer_pool_resurrect_buffer (pool);
-                if (ret == GST_FLOW_OK)
+                if (ret == GST_FLOW_OK || ret == GST_FLOW_FLUSHING)
                   goto done;
               }
 
@@ -2122,11 +2064,16 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
           GstV4l2MemoryGroup *group;
           gint index;
           gboolean outstanding;
+          gsize queued_size = 0;
+          gsize remaining_size = 0;
+          guint split_count = 1;
+          guint num_queued;
 
           if ((*buf)->pool != bpool)
             goto copying;
 
-          if (!gst_v4l2_is_buffer_valid (*buf, &group))
+          /* Output buffers don't have to be writable */
+          if (!gst_v4l2_is_buffer_valid (*buf, &group, FALSE))
             goto copying;
 
           index = group->buffer.index;
@@ -2163,7 +2110,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
             }
 
             /* retrieve the group */
-            gst_v4l2_is_buffer_valid (to_queue, &group);
+            gst_v4l2_is_buffer_valid (to_queue, &group, TRUE);
           }
 
           if ((ret =
@@ -2176,7 +2123,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
            * streaming now */
           if (!gst_v4l2_buffer_pool_streamon (pool)) {
             /* don't check return value because qbuf would have failed */
-            gst_v4l2_is_buffer_valid (to_queue, &group);
+            gst_v4l2_is_buffer_valid (to_queue, &group, TRUE);
 
             /* qbuf has stored to_queue buffer but we are not in
              * streaming state, so the flush logic won't be performed.
@@ -2195,9 +2142,16 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
             goto start_failed;
           }
 
+          /* Save the amount of data that has been submitted for encoded data */
+          if (GST_VIDEO_INFO_FORMAT (&pool->caps_info) ==
+              GST_VIDEO_FORMAT_ENCODED) {
+            queued_size = gst_buffer_get_size (to_queue);
+            remaining_size = gst_buffer_get_size (*buf) - queued_size;
+          }
+
           /* Remove our ref, we will still hold this buffer in acquire as needed,
            * otherwise the pool will think it is outstanding and will refuse to stop. */
-          gst_buffer_unref (to_queue);
+          gst_clear_buffer (&to_queue);
 
           /* release as many buffer as possible */
           while (gst_v4l2_buffer_pool_dqbuf (pool, &buffer, &outstanding,
@@ -2207,7 +2161,8 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
                   FALSE);
           }
 
-          if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) {
+          num_queued = g_atomic_int_get (&pool->num_queued);
+          if (num_queued >= pool->min_latency && num_queued > split_count) {
             /* all buffers are queued, try to dequeue one and release it back
              * into the pool so that _acquire can get to it again. */
             ret =
@@ -2218,6 +2173,15 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf,
               gst_v4l2_buffer_pool_complete_release_buffer (bpool, buffer,
                   FALSE);
           }
+
+          /* For encoded data, just queue de remaining in the next available
+           * buffer. */
+          if (remaining_size) {
+            *buf = gst_buffer_make_writable (*buf);
+            gst_buffer_resize (*buf, queued_size, -1);
+            split_count++;
+            goto copying;
+          }
           break;
         }
         default:
@@ -2248,7 +2212,7 @@ buffer_truncated:
   }
 eos:
   {
-    GST_DEBUG_OBJECT (pool, "end of stream reached");
+    GST_DEBUG_OBJECT (pool, "end of sequence reached");
     gst_buffer_unref (*buf);
     *buf = NULL;
     return GST_V4L2_FLOW_LAST_BUFFER;
@@ -2298,22 +2262,71 @@ gst_v4l2_buffer_pool_copy_at_threshold (GstV4l2BufferPool * pool, gboolean copy)
   GST_OBJECT_UNLOCK (pool);
 }
 
-gboolean
+static GstFlowReturn
+gst_v4l2_buffer_pool_flush_events (GstV4l2Object * v4l2object)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  gboolean event_found;
+
+  /* FIXME simplify this when we drop legacy support for driver without poll()
+   * support. When we do, we can switch the video_fd to non blocking, and just
+   * pop the events directly. */
+
+  do {
+    struct v4l2_event event = { 0, };
+    gint poll_ret;
+
+    event_found = FALSE;
+
+    gst_poll_set_flushing (v4l2object->poll, FALSE);
+
+    do {
+      /* GstPoll don't have 0ns timeout, but 1 will do */
+      poll_ret = gst_poll_wait (v4l2object->poll, 1);
+    } while (poll_ret == EAGAIN || poll_ret == EINTR);
+
+    if (gst_poll_fd_has_pri (v4l2object->poll, &v4l2object->pollfd)) {
+      if (!gst_v4l2_dequeue_event (v4l2object, &event))
+        return GST_FLOW_ERROR;
+
+      event_found = TRUE;
+
+      if (event.type == V4L2_EVENT_SOURCE_CHANGE &&
+          (event.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION)) {
+        GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+            "Can't streamon capture as the resolution have changed.");
+        ret = GST_V4L2_FLOW_RESOLUTION_CHANGE;
+      }
+    }
+  } while (event_found);
+
+  return ret;
+}
+
+GstFlowReturn
 gst_v4l2_buffer_pool_flush (GstV4l2Object * v4l2object)
 {
   GstBufferPool *bpool = gst_v4l2_object_get_buffer_pool (v4l2object);
   GstV4l2BufferPool *pool;
-  gboolean ret = TRUE;
+  GstFlowReturn ret = GST_FLOW_OK;
 
   if (!bpool)
-    return FALSE;
+    return GST_FLOW_ERROR;
 
   pool = GST_V4L2_BUFFER_POOL (bpool);
 
+  GST_OBJECT_LOCK (pool);
   gst_v4l2_buffer_pool_streamoff (pool);
+  GST_OBJECT_UNLOCK (pool);
 
-  if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type))
-    ret = gst_v4l2_buffer_pool_streamon (pool);
+  if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type)) {
+    ret = gst_v4l2_buffer_pool_flush_events (v4l2object);
+
+    /* If the format haven't change, avoid reallocation to go back to
+     * streaming */
+    if (ret == GST_FLOW_OK)
+      ret = gst_v4l2_buffer_pool_streamon (pool);
+  }
 
   gst_object_unref (bpool);
   return ret;
@@ -2331,13 +2344,5 @@ gst_v4l2_buffer_pool_flush (GstV4l2Object * v4l2object)
 void
 gst_v4l2_buffer_pool_enable_resolution_change (GstV4l2BufferPool * pool)
 {
-  guint32 input_id = 0;
-
-  g_return_if_fail (!gst_buffer_pool_is_active (GST_BUFFER_POOL (pool)));
-
-  /* Make sure we subscribe for the current input */
-  gst_v4l2_get_input (pool->obj, &input_id);
-
-  if (gst_v4l2_subscribe_event (pool->obj, V4L2_EVENT_SOURCE_CHANGE, input_id))
-    gst_poll_fd_ctl_pri (pool->poll, &pool->pollfd, TRUE);
+  gst_v4l2_object_subscribe_event (pool->obj, V4L2_EVENT_SOURCE_CHANGE);
 }
diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h
index 60340c244f3e10933e47e8237f5d92efa6e6d18c..db8c554306995701b2f3fbee30d9ca760cc02ec7 100644
--- a/sys/v4l2/gstv4l2bufferpool.h
+++ b/sys/v4l2/gstv4l2bufferpool.h
@@ -64,9 +64,6 @@ struct _GstV4l2BufferPool
 
   GstV4l2Object *obj;        /* the v4l2 object */
   gint video_fd;             /* a dup(2) of the v4l2object's video_fd */
-  GstPoll *poll;             /* a poll for video_fd */
-  GstPollFD pollfd;
-  gboolean can_poll_device;
 
   gboolean empty;
   GCond empty_cond;
@@ -118,7 +115,7 @@ void                gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * poo
 void                gst_v4l2_buffer_pool_copy_at_threshold (GstV4l2BufferPool * pool,
                                                             gboolean copy);
 
-gboolean            gst_v4l2_buffer_pool_flush   (GstV4l2Object * v4l2object);
+GstFlowReturn       gst_v4l2_buffer_pool_flush   (GstV4l2Object * v4l2object);
 
 gboolean            gst_v4l2_buffer_pool_orphan  (GstV4l2Object * v4l2object);
 
diff --git a/sys/v4l2/gstv4l2deviceprovider.c b/sys/v4l2/gstv4l2deviceprovider.c
index 7c2c87fea32b03cba096af7f5851d94e3e566d9f..5eeec7dc8b1e1b186dccdf684bf34b4b7f6f60b2 100644
--- a/sys/v4l2/gstv4l2deviceprovider.c
+++ b/sys/v4l2/gstv4l2deviceprovider.c
@@ -369,6 +369,8 @@ provider_thread (gpointer data)
   if (context == NULL || loop == NULL) {
     provider->started = TRUE;
     g_cond_broadcast (&provider->started_cond);
+    g_clear_pointer (&loop, g_main_loop_unref);
+    g_clear_pointer (&context, g_main_context_unref);
     GST_OBJECT_UNLOCK (provider);
     return NULL;
   }
@@ -451,19 +453,24 @@ gst_v4l2_device_provider_stop (GstDeviceProvider * provider)
   self->loop = NULL;
   GST_OBJECT_UNLOCK (self);
 
-  if (!context || !loop)
+  if (!context || !loop) {
+    g_clear_pointer (&self->loop, g_main_loop_unref);
+    g_clear_pointer (&self->context, g_main_context_unref);
     return;
+  }
 
   idle_stop_source = g_idle_source_new ();
   g_source_set_callback (idle_stop_source, (GSourceFunc) g_main_loop_quit, loop,
-      (GDestroyNotify) g_main_loop_unref);
+      NULL);
   g_source_attach (idle_stop_source, context);
   g_source_unref (idle_stop_source);
-  g_main_context_unref (context);
 
   g_thread_join (self->thread);
   self->thread = NULL;
   self->started = FALSE;
+
+  g_main_loop_unref (loop);
+  g_main_context_unref (context);
 }
 
 #endif
diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c
index 032fd69bd902fdedd32c998f4dd190d4ffed163e..5032da313ea27431bea8976b8408ec65d8bc31f3 100644
--- a/sys/v4l2/gstv4l2object.c
+++ b/sys/v4l2/gstv4l2object.c
@@ -53,7 +53,7 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
 #define DEFAULT_PROP_TV_NORM            0
 #define DEFAULT_PROP_IO_MODE            GST_V4L2_IO_AUTO
 
-#define ENCODED_BUFFER_SIZE             (2 * 1024 * 1024)
+#define ENCODED_BUFFER_MIN_SIZE         (256 * 1024)
 #define GST_V4L2_DEFAULT_WIDTH          320
 #define GST_V4L2_DEFAULT_HEIGHT         240
 
@@ -68,138 +68,163 @@ enum
  */
 typedef enum
 {
-  GST_V4L2_RAW = 1 << 0,
-  GST_V4L2_CODEC = 1 << 1,
-  GST_V4L2_TRANSPORT = 1 << 2,
-  GST_V4L2_NO_PARSE = 1 << 3,
+  GST_V4L2_RESOLUTION_AND_RATE = 1u << 4,
+  GST_V4L2_RAW = (1u << 0) | GST_V4L2_RESOLUTION_AND_RATE,
+  GST_V4L2_CODEC = 1u << 1,
+  GST_V4L2_TRANSPORT = 1u << 2,
+  GST_V4L2_NO_PARSE = 1u << 3,
+  GST_V4L2_BAYER = GST_V4L2_RAW | (1u << 5),
   GST_V4L2_ALL = 0xffff
 } GstV4L2FormatFlags;
 
 typedef struct
 {
-  guint32 format;
-  gboolean dimensions;
+  guint32 v4l2_format;
+  GstVideoFormat gst_format;
   GstV4L2FormatFlags flags;
 } GstV4L2FormatDesc;
 
+/* *INDENT-OFF* */
+#define MAP_FMT(v4l2_format, gst_fmt) V4L2_PIX_FMT_##v4l2_format, GST_VIDEO_FORMAT_##gst_fmt
 static const GstV4L2FormatDesc gst_v4l2_formats[] = {
   /* RGB formats */
-  {V4L2_PIX_FMT_RGB332, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_ARGB555, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_XRGB555, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_ARGB555X, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_XRGB555X, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGB565, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGB565X, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_BGR666, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_BGR24, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGB24, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_ABGR32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_XBGR32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_BGRA32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_BGRX32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGBA32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGBX32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_ARGB32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_XRGB32, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (RGB332, UNKNOWN),   GST_V4L2_RAW},
+  {MAP_FMT (ARGB555, UNKNOWN),  GST_V4L2_RAW},
+  {MAP_FMT (XRGB555, RGB15),    GST_V4L2_RAW},
+  {MAP_FMT (XRGB555X, RGB15),   GST_V4L2_RAW},
+  {MAP_FMT (ARGB555X, UNKNOWN), GST_V4L2_RAW},
+  {MAP_FMT (RGB565, RGB16),     GST_V4L2_RAW},
+  {MAP_FMT (RGB565X, UNKNOWN),  GST_V4L2_RAW},
+  {MAP_FMT (BGR666, UNKNOWN),   GST_V4L2_RAW},
+  {MAP_FMT (BGR24, BGR),        GST_V4L2_RAW},
+  {MAP_FMT (RGB24, RGB),        GST_V4L2_RAW},
+  {MAP_FMT (ABGR32, BGRA),      GST_V4L2_RAW},
+  {MAP_FMT (XBGR32, BGRx),      GST_V4L2_RAW},
+  {MAP_FMT (BGRA32, ABGR),      GST_V4L2_RAW},
+  {MAP_FMT (BGRX32, xBGR),      GST_V4L2_RAW},
+  {MAP_FMT (RGBA32, RGBA),      GST_V4L2_RAW},
+  {MAP_FMT (RGBX32, RGBx),      GST_V4L2_RAW},
+  {MAP_FMT (ARGB32, ARGB),      GST_V4L2_RAW},
+  {MAP_FMT (XRGB32, xRGB),      GST_V4L2_RAW},
 
   /* Deprecated Packed RGB Image Formats (alpha ambiguity) */
-  {V4L2_PIX_FMT_RGB444, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGB555, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGB555X, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_BGR32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_RGB32, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (RGB444, UNKNOWN), GST_V4L2_RAW},
+  {MAP_FMT (RGB555, RGB15),   GST_V4L2_RAW},
+  {MAP_FMT (RGB555X, BGR15),  GST_V4L2_RAW},
+  {MAP_FMT (BGR32, BGRx),     GST_V4L2_RAW},
+  {MAP_FMT (BGR32, BGRA),     GST_V4L2_RAW},
+  {MAP_FMT (RGB32, xRGB),     GST_V4L2_RAW},
+  {MAP_FMT (RGB32, ARGB),     GST_V4L2_RAW},
 
   /* Grey formats */
-  {V4L2_PIX_FMT_GREY, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y4, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y6, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y10, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y12, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y16, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y16_BE, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y10BPACK, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (GREY, GRAY8),       GST_V4L2_RAW},
+  {MAP_FMT (Y4, UNKNOWN),       GST_V4L2_RAW},
+  {MAP_FMT (Y6, UNKNOWN),       GST_V4L2_RAW},
+  {MAP_FMT (Y10, UNKNOWN),      GST_V4L2_RAW},
+  {MAP_FMT (Y12, UNKNOWN),      GST_V4L2_RAW},
+  {MAP_FMT (Y16, GRAY16_LE),    GST_V4L2_RAW},
+  {MAP_FMT (Y16_BE, GRAY16_BE), GST_V4L2_RAW},
+  {MAP_FMT (Y10BPACK, UNKNOWN), GST_V4L2_RAW},
 
   /* Palette formats */
-  {V4L2_PIX_FMT_PAL8, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (PAL8, UNKNOWN),   GST_V4L2_RAW},
 
   /* Chrominance formats */
-  {V4L2_PIX_FMT_UV8, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (UV8, UNKNOWN),    GST_V4L2_RAW},
 
   /* Luminance+Chrominance formats */
-  {V4L2_PIX_FMT_YVU410, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YVU420, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YVU420M, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUYV, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YYUV, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YVYU, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_UYVY, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_VYUY, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV422P, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV411P, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_Y41P, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV444, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV555, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV565, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV32, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV410, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV420, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_YUV420M, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_HI240, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_HM12, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_M420, TRUE, GST_V4L2_RAW},
-
-  /* two planes -- one Y, one Cr + Cb interleaved  */
-  {V4L2_PIX_FMT_NV12, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV12M, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV12MT, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV12MT_16X16, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV12M_8L128, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV12M_10BE_8L128, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV21, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV21M, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV16, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV16M, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV61, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV61M, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV24, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_NV42, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_MM21, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (YVU410, YVU9),    GST_V4L2_RAW},
+  {MAP_FMT (YVU420M, YV12),   GST_V4L2_RAW},
+  {MAP_FMT (YVU420, YV12),    GST_V4L2_RAW},
+  {MAP_FMT (YUYV, YUY2),      GST_V4L2_RAW},
+  {MAP_FMT (YYUV, UNKNOWN),   GST_V4L2_RAW},
+  {MAP_FMT (YVYU, YVYU),      GST_V4L2_RAW},
+  {MAP_FMT (UYVY, UYVY),      GST_V4L2_RAW},
+  {MAP_FMT (VYUY, UNKNOWN),   GST_V4L2_RAW},
+  {MAP_FMT (YUV422M, Y42B),   GST_V4L2_RAW},
+  {MAP_FMT (YUV422P, Y42B),   GST_V4L2_RAW},
+  {MAP_FMT (YUV411P, Y41B),   GST_V4L2_RAW},
+  {MAP_FMT (Y41P, UNKNOWN),   GST_V4L2_RAW},
+  {MAP_FMT (YUV444, UNKNOWN), GST_V4L2_RAW},
+  {MAP_FMT (YUV555, UNKNOWN), GST_V4L2_RAW},
+  {MAP_FMT (YUV565, UNKNOWN), GST_V4L2_RAW},
+  {MAP_FMT (YUV32, UNKNOWN),  GST_V4L2_RAW},
+  {MAP_FMT (YUV410, YUV9),    GST_V4L2_RAW},
+  {MAP_FMT (YUV420M, I420),   GST_V4L2_RAW},
+  {MAP_FMT (YUV420, I420),    GST_V4L2_RAW},
+  {MAP_FMT (HI240, UNKNOWN),  GST_V4L2_RAW},
+  {MAP_FMT (M420, UNKNOWN),   GST_V4L2_RAW},
+
+  /* two planes -- one Y,         one Cr + Cb interleaved  */
+  {MAP_FMT (NV12M, NV12),                       GST_V4L2_RAW},
+  {MAP_FMT (NV12, NV12),                        GST_V4L2_RAW},
+  {MAP_FMT (NV12MT, NV12_64Z32),                GST_V4L2_RAW},
+  {MAP_FMT (NV12MT_16X16, UNKNOWN),             GST_V4L2_RAW},
+  {MAP_FMT (NV12_16L16, UNKNOWN),               GST_V4L2_RAW},
+  {MAP_FMT (NV12M_8L128, NV12_8L128),           GST_V4L2_RAW},
+  {MAP_FMT (NV12M_10BE_8L128, NV12_10BE_8L128), GST_V4L2_RAW},
+  {MAP_FMT (NV21M, NV21),                       GST_V4L2_RAW},
+  {MAP_FMT (NV21, NV21),                        GST_V4L2_RAW},
+  {MAP_FMT (NV16M, NV16),                       GST_V4L2_RAW},
+  {MAP_FMT (NV16, NV16),                        GST_V4L2_RAW},
+  {MAP_FMT (NV61M, NV61),                       GST_V4L2_RAW},
+  {MAP_FMT (NV61, NV61),                        GST_V4L2_RAW},
+  {MAP_FMT (NV24, NV24),                        GST_V4L2_RAW},
+  {MAP_FMT (NV42, UNKNOWN),                     GST_V4L2_RAW},
+  {MAP_FMT (MM21, NV12_16L32S),                 GST_V4L2_RAW},
+  {MAP_FMT (P010, P010_10LE),                   GST_V4L2_RAW},
 
   /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
-  {V4L2_PIX_FMT_SBGGR8, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_SGBRG8, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_SGRBG8, TRUE, GST_V4L2_RAW},
-  {V4L2_PIX_FMT_SRGGB8, TRUE, GST_V4L2_RAW},
+  {MAP_FMT (SBGGR8, ENCODED),   GST_V4L2_BAYER},
+  {MAP_FMT (SGBRG8, ENCODED),   GST_V4L2_BAYER},
+  {MAP_FMT (SGRBG8, ENCODED),   GST_V4L2_BAYER},
+  {MAP_FMT (SRGGB8, ENCODED),   GST_V4L2_BAYER},
+  {MAP_FMT (SBGGR10, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGBRG10, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGRBG10, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SRGGB10, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SBGGR12, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGBRG12, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGRBG12, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SRGGB12, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SBGGR14, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGBRG14, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGRBG14, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SRGGB14, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SBGGR16, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGBRG16, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SGRBG16, ENCODED),  GST_V4L2_BAYER},
+  {MAP_FMT (SRGGB16, ENCODED),  GST_V4L2_BAYER},
 
   /* compressed formats */
-  {V4L2_PIX_FMT_MJPEG, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_JPEG, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_PJPG, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_DV, FALSE, GST_V4L2_TRANSPORT},
-  {V4L2_PIX_FMT_MPEG, FALSE, GST_V4L2_TRANSPORT},
-  {V4L2_PIX_FMT_FWHT, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_H264, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_H264_NO_SC, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_H264_MVC, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_HEVC, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_H263, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_MPEG1, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_MPEG2, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_MPEG4, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_XVID, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_VC1_ANNEX_G, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_VC1_ANNEX_L, FALSE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_VP8, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE},
-  {V4L2_PIX_FMT_VP9, FALSE, GST_V4L2_CODEC | GST_V4L2_NO_PARSE},
+  {MAP_FMT (MJPEG, ENCODED),        GST_V4L2_CODEC},
+  {MAP_FMT (JPEG, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (PJPG, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (DV, ENCODED),           GST_V4L2_TRANSPORT},
+  {MAP_FMT (MPEG, ENCODED),         GST_V4L2_TRANSPORT},
+  {MAP_FMT (FWHT, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (H264, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (H264_NO_SC, ENCODED),   GST_V4L2_CODEC},
+  {MAP_FMT (H264_MVC, ENCODED),     GST_V4L2_CODEC},
+  {MAP_FMT (HEVC, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (H263, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (MPEG1, ENCODED),        GST_V4L2_CODEC},
+  {MAP_FMT (MPEG2, ENCODED),        GST_V4L2_CODEC},
+  {MAP_FMT (MPEG4, ENCODED),        GST_V4L2_CODEC},
+  {MAP_FMT (XVID, ENCODED),         GST_V4L2_CODEC},
+  {MAP_FMT (VC1_ANNEX_G, ENCODED),  GST_V4L2_CODEC},
+  {MAP_FMT (VC1_ANNEX_L, ENCODED),  GST_V4L2_CODEC},
+  {MAP_FMT (VP8, ENCODED),          GST_V4L2_CODEC | GST_V4L2_NO_PARSE},
+  {MAP_FMT (VP9, ENCODED),          GST_V4L2_CODEC | GST_V4L2_NO_PARSE},
 
   /*  Vendor-specific formats   */
-  {V4L2_PIX_FMT_WNVA, TRUE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_SN9C10X, TRUE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_PWC1, TRUE, GST_V4L2_CODEC},
-  {V4L2_PIX_FMT_PWC2, TRUE, GST_V4L2_CODEC},
+  {MAP_FMT (WNVA, ENCODED),     GST_V4L2_CODEC | GST_V4L2_RESOLUTION_AND_RATE},
+  {MAP_FMT (SN9C10X, ENCODED),  GST_V4L2_CODEC | GST_V4L2_RESOLUTION_AND_RATE},
+  {MAP_FMT (PWC1, ENCODED),     GST_V4L2_CODEC | GST_V4L2_RESOLUTION_AND_RATE},
+  {MAP_FMT (PWC2, ENCODED),     GST_V4L2_CODEC | GST_V4L2_RESOLUTION_AND_RATE},
 };
-
+#undef MAP_FMT
+/* *INDENT-ON* */
 #define GST_V4L2_FORMAT_COUNT (G_N_ELEMENTS (gst_v4l2_formats))
 
 static GSList *gst_v4l2_object_get_format_list (GstV4l2Object * v4l2object);
@@ -525,6 +550,9 @@ gst_v4l2_object_new (GstElement * element,
 
   v4l2object->no_initial_format = FALSE;
 
+  v4l2object->poll = gst_poll_new (TRUE);
+  v4l2object->can_poll_device = TRUE;
+
   /* We now disable libv4l2 by default, but have an env to enable it. */
 #ifdef HAVE_LIBV4L2
   if (g_getenv ("GST_V4L2_USE_LIBV4L2")) {
@@ -550,6 +578,19 @@ gst_v4l2_object_new (GstElement * element,
   return v4l2object;
 }
 
+
+static gboolean
+gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object)
+{
+  g_slist_foreach (v4l2object->formats, (GFunc) g_free, NULL);
+  g_slist_free (v4l2object->formats);
+  v4l2object->formats = NULL;
+  v4l2object->fmtdesc = NULL;
+
+  return TRUE;
+}
+
+
 void
 gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
 {
@@ -559,6 +600,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
   g_free (v4l2object->par);
   g_free (v4l2object->channel);
 
+  gst_poll_free (v4l2object->poll);
+
   if (v4l2object->formats) {
     gst_v4l2_object_clear_format_list (v4l2object);
   }
@@ -575,16 +618,6 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
 }
 
 
-gboolean
-gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object)
-{
-  g_slist_foreach (v4l2object->formats, (GFunc) g_free, NULL);
-  g_slist_free (v4l2object->formats);
-  v4l2object->formats = NULL;
-
-  return TRUE;
-}
-
 static gint
 gst_v4l2_object_prop_to_cid (guint prop_id)
 {
@@ -897,6 +930,20 @@ gst_v4l2_set_defaults (GstV4l2Object * v4l2object)
   }
 }
 
+static void
+gst_v4l2_object_init_poll (GstV4l2Object * v4l2object)
+{
+  gst_poll_fd_init (&v4l2object->pollfd);
+  v4l2object->pollfd.fd = v4l2object->video_fd;
+  gst_poll_add_fd (v4l2object->poll, &v4l2object->pollfd);
+  if (V4L2_TYPE_IS_OUTPUT (v4l2object->type))
+    gst_poll_fd_ctl_write (v4l2object->poll, &v4l2object->pollfd, TRUE);
+  else
+    gst_poll_fd_ctl_read (v4l2object->poll, &v4l2object->pollfd, TRUE);
+
+  v4l2object->can_poll_device = TRUE;
+}
+
 gboolean
 gst_v4l2_object_open (GstV4l2Object * v4l2object, GstV4l2Error * error)
 {
@@ -905,17 +952,20 @@ gst_v4l2_object_open (GstV4l2Object * v4l2object, GstV4l2Error * error)
   else
     return FALSE;
 
+  gst_v4l2_object_init_poll (v4l2object);
+
   return TRUE;
 }
 
 gboolean
 gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * other)
 {
-  gboolean ret;
-
-  ret = gst_v4l2_dup (v4l2object, other);
+  if (gst_v4l2_dup (v4l2object, other)) {
+    gst_v4l2_object_init_poll (v4l2object);
+    return TRUE;
+  }
 
-  return ret;
+  return FALSE;
 }
 
 gboolean
@@ -944,6 +994,10 @@ gst_v4l2_object_close (GstV4l2Object * v4l2object)
     v4l2object->channel = NULL;
   }
 
+  /* remove old fd from poll */
+  if (v4l2object->poll)
+    gst_poll_remove_fd (v4l2object->poll, &v4l2object->pollfd);
+
   return TRUE;
 }
 
@@ -1093,6 +1147,7 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt)
       rank = YUV_BASE_RANK + 10;
       break;
     case V4L2_PIX_FMT_YVU420:  /* YV12, 12 bits per pixel */
+    case V4L2_PIX_FMT_YVU420M:
       rank = YUV_BASE_RANK + 6;
       break;
     case V4L2_PIX_FMT_UYVY:    /* UYVY, 16 bits per pixel */
@@ -1108,6 +1163,7 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt)
       rank = YUV_BASE_RANK + 4;
       break;
     case V4L2_PIX_FMT_YUV422P: /* Y42B, 16 bits per pixel */
+    case V4L2_PIX_FMT_YUV422M:
       rank = YUV_BASE_RANK + 8;
       break;
 
@@ -1123,6 +1179,22 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt)
     case V4L2_PIX_FMT_SGBRG8:
     case V4L2_PIX_FMT_SGRBG8:
     case V4L2_PIX_FMT_SRGGB8:
+    case V4L2_PIX_FMT_SBGGR10:
+    case V4L2_PIX_FMT_SGBRG10:
+    case V4L2_PIX_FMT_SGRBG10:
+    case V4L2_PIX_FMT_SRGGB10:
+    case V4L2_PIX_FMT_SBGGR12:
+    case V4L2_PIX_FMT_SGBRG12:
+    case V4L2_PIX_FMT_SGRBG12:
+    case V4L2_PIX_FMT_SRGGB12:
+    case V4L2_PIX_FMT_SBGGR14:
+    case V4L2_PIX_FMT_SGBRG14:
+    case V4L2_PIX_FMT_SGRBG14:
+    case V4L2_PIX_FMT_SRGGB14:
+    case V4L2_PIX_FMT_SBGGR16:
+    case V4L2_PIX_FMT_SGBRG16:
+    case V4L2_PIX_FMT_SGRBG16:
+    case V4L2_PIX_FMT_SRGGB16:
       rank = BAYER_BASE_RANK;
       break;
 
@@ -1234,7 +1306,7 @@ failed:
   {
     g_free (format);
 
-    if (v4l2object->element)
+    if (!v4l2object->element)
       return FALSE;
 
     GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS,
@@ -1283,177 +1355,107 @@ gst_v4l2_object_get_format_list (GstV4l2Object * v4l2object)
   return v4l2object->formats;
 }
 
-static GstVideoFormat
-gst_v4l2_object_v4l2fourcc_to_video_format (guint32 fourcc)
+static const GstV4L2FormatDesc *
+gst_v4l2_object_get_desc_from_v4l2fourcc (guint32 fourcc)
 {
-  GstVideoFormat format;
+  gint i;
 
-  switch (fourcc) {
-    case V4L2_PIX_FMT_GREY:    /*  8  Greyscale     */
-      format = GST_VIDEO_FORMAT_GRAY8;
-      break;
-    case V4L2_PIX_FMT_Y16:
-      format = GST_VIDEO_FORMAT_GRAY16_LE;
-      break;
-    case V4L2_PIX_FMT_Y16_BE:
-      format = GST_VIDEO_FORMAT_GRAY16_BE;
-      break;
-    case V4L2_PIX_FMT_XRGB555:
-    case V4L2_PIX_FMT_RGB555:
-      format = GST_VIDEO_FORMAT_RGB15;
-      break;
-    case V4L2_PIX_FMT_XRGB555X:
-    case V4L2_PIX_FMT_RGB555X:
-      format = GST_VIDEO_FORMAT_BGR15;
-      break;
-    case V4L2_PIX_FMT_RGB565:
-      format = GST_VIDEO_FORMAT_RGB16;
-      break;
-    case V4L2_PIX_FMT_RGB24:
-      format = GST_VIDEO_FORMAT_RGB;
-      break;
-    case V4L2_PIX_FMT_BGR24:
-      format = GST_VIDEO_FORMAT_BGR;
-      break;
-    case V4L2_PIX_FMT_XRGB32:
-    case V4L2_PIX_FMT_RGB32:
-      format = GST_VIDEO_FORMAT_xRGB;
-      break;
-    case V4L2_PIX_FMT_RGBX32:
-      format = GST_VIDEO_FORMAT_RGBx;
-      break;
-    case V4L2_PIX_FMT_XBGR32:
-    case V4L2_PIX_FMT_BGR32:
-      format = GST_VIDEO_FORMAT_BGRx;
-      break;
-    case V4L2_PIX_FMT_BGRX32:
-      format = GST_VIDEO_FORMAT_xBGR;
-      break;
-    case V4L2_PIX_FMT_ABGR32:
-      format = GST_VIDEO_FORMAT_BGRA;
-      break;
-    case V4L2_PIX_FMT_BGRA32:
-      format = GST_VIDEO_FORMAT_ABGR;
-      break;
-    case V4L2_PIX_FMT_RGBA32:
-      format = GST_VIDEO_FORMAT_RGBA;
-      break;
-    case V4L2_PIX_FMT_ARGB32:
-      format = GST_VIDEO_FORMAT_ARGB;
-      break;
-    case V4L2_PIX_FMT_NV12:
-    case V4L2_PIX_FMT_NV12M:
-      format = GST_VIDEO_FORMAT_NV12;
-      break;
-    case V4L2_PIX_FMT_NV12MT:
-      format = GST_VIDEO_FORMAT_NV12_64Z32;
-      break;
-    case V4L2_PIX_FMT_MM21:
-      format = GST_VIDEO_FORMAT_NV12_16L32S;
-      break;
-    case V4L2_PIX_FMT_NV12M_8L128:
-      format = GST_VIDEO_FORMAT_NV12_8L128;
-      break;
-    case V4L2_PIX_FMT_NV12M_10BE_8L128:
-      format = GST_VIDEO_FORMAT_NV12_10BE_8L128;
-      break;
-    case V4L2_PIX_FMT_NV21:
-    case V4L2_PIX_FMT_NV21M:
-      format = GST_VIDEO_FORMAT_NV21;
-      break;
-    case V4L2_PIX_FMT_YVU410:
-      format = GST_VIDEO_FORMAT_YVU9;
-      break;
-    case V4L2_PIX_FMT_YUV410:
-      format = GST_VIDEO_FORMAT_YUV9;
-      break;
-    case V4L2_PIX_FMT_YUV420:
-    case V4L2_PIX_FMT_YUV420M:
-      format = GST_VIDEO_FORMAT_I420;
-      break;
-    case V4L2_PIX_FMT_YUYV:
-      format = GST_VIDEO_FORMAT_YUY2;
-      break;
-    case V4L2_PIX_FMT_YVU420:
-      format = GST_VIDEO_FORMAT_YV12;
-      break;
-    case V4L2_PIX_FMT_UYVY:
-      format = GST_VIDEO_FORMAT_UYVY;
-      break;
-    case V4L2_PIX_FMT_YUV411P:
-      format = GST_VIDEO_FORMAT_Y41B;
-      break;
-    case V4L2_PIX_FMT_YUV422P:
-      format = GST_VIDEO_FORMAT_Y42B;
-      break;
-    case V4L2_PIX_FMT_YVYU:
-      format = GST_VIDEO_FORMAT_YVYU;
-      break;
-    case V4L2_PIX_FMT_NV16:
-    case V4L2_PIX_FMT_NV16M:
-      format = GST_VIDEO_FORMAT_NV16;
-      break;
-    case V4L2_PIX_FMT_NV61:
-    case V4L2_PIX_FMT_NV61M:
-      format = GST_VIDEO_FORMAT_NV61;
-      break;
-    case V4L2_PIX_FMT_NV24:
-      format = GST_VIDEO_FORMAT_NV24;
-      break;
-    default:
-      format = GST_VIDEO_FORMAT_UNKNOWN;
-      break;
+  for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
+    if (gst_v4l2_formats[i].v4l2_format == fourcc) {
+      return &gst_v4l2_formats[i];
+    }
   }
 
-  return format;
+  return NULL;
+}
+
+GstVideoFormat
+gst_v4l2_object_v4l2fourcc_to_video_format (guint32 fourcc)
+{
+  const GstV4L2FormatDesc *desc;
+  desc = gst_v4l2_object_get_desc_from_v4l2fourcc (fourcc);
+  if (!desc)
+    return GST_VIDEO_FORMAT_UNKNOWN;
+
+  return desc->gst_format;
 }
 
+
 static gboolean
 gst_v4l2_object_v4l2fourcc_is_rgb (guint32 fourcc)
 {
-  gboolean ret = FALSE;
+  const GstV4L2FormatDesc *desc;
+  const GstVideoFormatInfo *finfo;
 
-  switch (fourcc) {
-    case V4L2_PIX_FMT_XRGB555:
-    case V4L2_PIX_FMT_RGB555:
-    case V4L2_PIX_FMT_XRGB555X:
-    case V4L2_PIX_FMT_RGB555X:
-    case V4L2_PIX_FMT_RGB565:
-    case V4L2_PIX_FMT_RGB24:
-    case V4L2_PIX_FMT_BGR24:
-    case V4L2_PIX_FMT_XRGB32:
-    case V4L2_PIX_FMT_RGB32:
-    case V4L2_PIX_FMT_RGBA32:
-    case V4L2_PIX_FMT_RGBX32:
-    case V4L2_PIX_FMT_XBGR32:
-    case V4L2_PIX_FMT_BGR32:
-    case V4L2_PIX_FMT_BGRA32:
-    case V4L2_PIX_FMT_BGRX32:
-    case V4L2_PIX_FMT_ABGR32:
-    case V4L2_PIX_FMT_ARGB32:
-    case V4L2_PIX_FMT_SBGGR8:
-    case V4L2_PIX_FMT_SGBRG8:
-    case V4L2_PIX_FMT_SGRBG8:
-    case V4L2_PIX_FMT_SRGGB8:
-      ret = TRUE;
-      break;
-    default:
-      break;
+  desc = gst_v4l2_object_get_desc_from_v4l2fourcc (fourcc);
+  if (!desc || desc->gst_format == GST_VIDEO_FORMAT_UNKNOWN)
+    return FALSE;
+
+  finfo = gst_video_format_get_info (desc->gst_format);
+  if (finfo) {
+    if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo))
+      return TRUE;
+    if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo))
+      return TRUE;
+  }
+
+  if ((desc->flags & GST_V4L2_BAYER) == GST_V4L2_BAYER)
+    return TRUE;
+
+  return FALSE;
+}
+
+static const GstV4L2FormatDesc *
+gst_v4l2_object_get_desc_from_video_format (GstVideoFormat format,
+    const GstV4L2FormatDesc ** fallback)
+{
+  const GstV4L2FormatDesc *ret = NULL;
+  gint i;
+
+  if (fallback)
+    *fallback = NULL;
+
+  for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
+    if (gst_v4l2_formats[i].gst_format == format) {
+      if (!ret)
+        ret = &gst_v4l2_formats[i];
+      else if (fallback && !*fallback)
+        *fallback = &gst_v4l2_formats[i];
+      else
+        break;
+    }
   }
 
   return ret;
 }
 
+static gboolean
+gst_v4l2_object_v4l2fourcc_is_codec (guint32 fourcc)
+{
+  const GstV4L2FormatDesc *desc;
+
+  desc = gst_v4l2_object_get_desc_from_v4l2fourcc (fourcc);
+  if (!desc)
+    return FALSE;
+
+  if (desc->flags & (GST_V4L2_CODEC | GST_V4L2_TRANSPORT))
+    return TRUE;
+
+  return FALSE;
+}
+
 static GstStructure *
 gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc)
 {
   GstStructure *structure = NULL;
+  const gchar *bayer_format = NULL;
 
   switch (fourcc) {
     case V4L2_PIX_FMT_MJPEG:   /* Motion-JPEG */
     case V4L2_PIX_FMT_PJPG:    /* Progressive-JPEG */
     case V4L2_PIX_FMT_JPEG:    /* JFIF JPEG */
-      structure = gst_structure_new_empty ("image/jpeg");
+      structure = gst_structure_new ("image/jpeg",
+          "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
       break;
     case V4L2_PIX_FMT_MPEG1:
       structure = gst_structure_new ("video/mpeg",
@@ -1502,56 +1504,6 @@ gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc)
     case V4L2_PIX_FMT_VP9:
       structure = gst_structure_new_empty ("video/x-vp9");
       break;
-    case V4L2_PIX_FMT_GREY:    /*  8  Greyscale     */
-    case V4L2_PIX_FMT_Y16:
-    case V4L2_PIX_FMT_Y16_BE:
-    case V4L2_PIX_FMT_XRGB555:
-    case V4L2_PIX_FMT_RGB555:
-    case V4L2_PIX_FMT_XRGB555X:
-    case V4L2_PIX_FMT_RGB555X:
-    case V4L2_PIX_FMT_RGB565:
-    case V4L2_PIX_FMT_RGB24:
-    case V4L2_PIX_FMT_BGR24:
-    case V4L2_PIX_FMT_RGB32:
-    case V4L2_PIX_FMT_XRGB32:
-    case V4L2_PIX_FMT_ARGB32:
-    case V4L2_PIX_FMT_RGBX32:
-    case V4L2_PIX_FMT_RGBA32:
-    case V4L2_PIX_FMT_BGR32:
-    case V4L2_PIX_FMT_BGRX32:
-    case V4L2_PIX_FMT_BGRA32:
-    case V4L2_PIX_FMT_XBGR32:
-    case V4L2_PIX_FMT_ABGR32:
-    case V4L2_PIX_FMT_NV12:    /* 12  Y/CbCr 4:2:0  */
-    case V4L2_PIX_FMT_NV12M:
-    case V4L2_PIX_FMT_NV12MT:
-    case V4L2_PIX_FMT_MM21:
-    case V4L2_PIX_FMT_NV12M_8L128:
-    case V4L2_PIX_FMT_NV12M_10BE_8L128:
-    case V4L2_PIX_FMT_NV21:    /* 12  Y/CrCb 4:2:0  */
-    case V4L2_PIX_FMT_NV21M:
-    case V4L2_PIX_FMT_NV16:    /* 16  Y/CbCr 4:2:2  */
-    case V4L2_PIX_FMT_NV16M:
-    case V4L2_PIX_FMT_NV61:    /* 16  Y/CrCb 4:2:2  */
-    case V4L2_PIX_FMT_NV61M:
-    case V4L2_PIX_FMT_NV24:    /* 24  Y/CrCb 4:4:4  */
-    case V4L2_PIX_FMT_YVU410:
-    case V4L2_PIX_FMT_YUV410:
-    case V4L2_PIX_FMT_YUV420:  /* I420/IYUV */
-    case V4L2_PIX_FMT_YUV420M:
-    case V4L2_PIX_FMT_YUYV:
-    case V4L2_PIX_FMT_YVU420:
-    case V4L2_PIX_FMT_UYVY:
-    case V4L2_PIX_FMT_YUV422P:
-    case V4L2_PIX_FMT_YVYU:
-    case V4L2_PIX_FMT_YUV411P:{
-      GstVideoFormat format;
-      format = gst_v4l2_object_v4l2fourcc_to_video_format (fourcc);
-      if (format != GST_VIDEO_FORMAT_UNKNOWN)
-        structure = gst_structure_new ("video/x-raw",
-            "format", G_TYPE_STRING, gst_video_format_to_string (format), NULL);
-      break;
-    }
     case V4L2_PIX_FMT_DV:
       structure =
           gst_structure_new ("video/x-dv", "systemstream", G_TYPE_BOOLEAN, TRUE,
@@ -1564,14 +1516,64 @@ gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc)
     case V4L2_PIX_FMT_WNVA:    /* Winnov hw compress */
       break;
     case V4L2_PIX_FMT_SBGGR8:
+      bayer_format = "bggr";
+      break;
+    case V4L2_PIX_FMT_SBGGR10:
+      bayer_format = "bggr10le";
+      break;
+    case V4L2_PIX_FMT_SBGGR12:
+      bayer_format = "bggr12le";
+      break;
+    case V4L2_PIX_FMT_SBGGR14:
+      bayer_format = "bggr14le";
+      break;
+    case V4L2_PIX_FMT_SBGGR16:
+      bayer_format = "bggr16le";
+      break;
     case V4L2_PIX_FMT_SGBRG8:
+      bayer_format = "gbrg";
+      break;
+    case V4L2_PIX_FMT_SGBRG10:
+      bayer_format = "gbrg10le";
+      break;
+    case V4L2_PIX_FMT_SGBRG12:
+      bayer_format = "gbrg12le";
+      break;
+    case V4L2_PIX_FMT_SGBRG14:
+      bayer_format = "gbrg14le";
+      break;
+    case V4L2_PIX_FMT_SGBRG16:
+      bayer_format = "gbrg16le";
+      break;
     case V4L2_PIX_FMT_SGRBG8:
+      bayer_format = "grbg";
+      break;
+    case V4L2_PIX_FMT_SGRBG10:
+      bayer_format = "grbg10le";
+      break;
+    case V4L2_PIX_FMT_SGRBG12:
+      bayer_format = "grbg12le";
+      break;
+    case V4L2_PIX_FMT_SGRBG14:
+      bayer_format = "grbg14le";
+      break;
+    case V4L2_PIX_FMT_SGRBG16:
+      bayer_format = "grbg16le";
+      break;
     case V4L2_PIX_FMT_SRGGB8:
-      structure = gst_structure_new ("video/x-bayer", "format", G_TYPE_STRING,
-          fourcc == V4L2_PIX_FMT_SBGGR8 ? "bggr" :
-          fourcc == V4L2_PIX_FMT_SGBRG8 ? "gbrg" :
-          fourcc == V4L2_PIX_FMT_SGRBG8 ? "grbg" :
-          /* fourcc == V4L2_PIX_FMT_SRGGB8 ? */ "rggb", NULL);
+      bayer_format = "rggb";
+      break;
+    case V4L2_PIX_FMT_SRGGB10:
+      bayer_format = "rggb10le";
+      break;
+    case V4L2_PIX_FMT_SRGGB12:
+      bayer_format = "rggb12le";
+      break;
+    case V4L2_PIX_FMT_SRGGB14:
+      bayer_format = "rggb14le";
+      break;
+    case V4L2_PIX_FMT_SRGGB16:
+      bayer_format = "rggb16le";
       break;
     case V4L2_PIX_FMT_SN9C10X:
       structure = gst_structure_new_empty ("video/x-sonix");
@@ -1582,32 +1584,26 @@ gst_v4l2_object_v4l2fourcc_to_bare_struct (guint32 fourcc)
     case V4L2_PIX_FMT_PWC2:
       structure = gst_structure_new_empty ("video/x-pwc2");
       break;
-    case V4L2_PIX_FMT_RGB332:
-    case V4L2_PIX_FMT_BGR666:
-    case V4L2_PIX_FMT_ARGB555X:
-    case V4L2_PIX_FMT_RGB565X:
-    case V4L2_PIX_FMT_RGB444:
-    case V4L2_PIX_FMT_YYUV:    /* 16  YUV 4:2:2     */
-    case V4L2_PIX_FMT_HI240:   /*  8  8-bit color   */
-    case V4L2_PIX_FMT_Y4:
-    case V4L2_PIX_FMT_Y6:
-    case V4L2_PIX_FMT_Y10:
-    case V4L2_PIX_FMT_Y12:
-    case V4L2_PIX_FMT_Y10BPACK:
-    case V4L2_PIX_FMT_YUV444:
-    case V4L2_PIX_FMT_YUV555:
-    case V4L2_PIX_FMT_YUV565:
-    case V4L2_PIX_FMT_Y41P:
-    case V4L2_PIX_FMT_YUV32:
-    case V4L2_PIX_FMT_NV12MT_16X16:
-    case V4L2_PIX_FMT_NV42:
-    case V4L2_PIX_FMT_H264_MVC:
     default:
-      GST_DEBUG ("Unsupported fourcc 0x%08x %" GST_FOURCC_FORMAT,
-          fourcc, GST_FOURCC_ARGS (fourcc));
       break;
   }
 
+  if (bayer_format)
+    structure = gst_structure_new ("video/x-bayer", "format", G_TYPE_STRING,
+        bayer_format, NULL);
+
+  if (!structure) {
+    GstVideoFormat format;
+    format = gst_v4l2_object_v4l2fourcc_to_video_format (fourcc);
+
+    if (format <= GST_VIDEO_FORMAT_ENCODED)
+      GST_DEBUG ("Unsupported V4L2 fourcc 0x%08x %" GST_FOURCC_FORMAT,
+          fourcc, GST_FOURCC_ARGS (fourcc));
+    else
+      structure = gst_structure_new ("video/x-raw",
+          "format", G_TYPE_STRING, gst_video_format_to_string (format), NULL);
+  }
+
   return structure;
 }
 
@@ -1623,10 +1619,10 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc)
     goto done;
 
   for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
-    if (gst_v4l2_formats[i].format != fourcc)
+    if (gst_v4l2_formats[i].v4l2_format != fourcc)
       continue;
 
-    if (gst_v4l2_formats[i].dimensions) {
+    if (gst_v4l2_formats[i].flags & GST_V4L2_RESOLUTION_AND_RATE) {
       gst_structure_set (template,
           "width", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
           "height", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
@@ -1639,6 +1635,22 @@ done:
   return template;
 }
 
+gboolean
+gst_v4l2_object_is_raw (GstV4l2Object * v4l2object)
+{
+  gint i;
+
+  if (GST_VIDEO_INFO_FORMAT (&v4l2object->info) != GST_VIDEO_FORMAT_ENCODED)
+    return TRUE;
+
+  for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
+    if (gst_v4l2_formats[i].v4l2_format == GST_V4L2_PIXELFORMAT (v4l2object)) {
+      return !!(gst_v4l2_formats[i].flags & GST_V4L2_RAW);
+    }
+  }
+  return FALSE;
+}
+
 /* Add an 'alternate' variant of the caps with the feature */
 static void
 add_alternate_variant (GstV4l2Object * v4l2object, GstCaps * caps,
@@ -1659,8 +1671,34 @@ add_alternate_variant (GstV4l2Object * v4l2object, GstCaps * caps,
       gst_caps_features_new (GST_CAPS_FEATURE_FORMAT_INTERLACED, NULL));
 }
 
+static void
+add_non_colorimetry_caps (GstV4l2Object * v4l2object, GstCaps * caps)
+{
+  gint caps_size;
+  gint i;
+
+  caps_size = gst_caps_get_size (caps);
+  for (i = 0; i < caps_size; i++) {
+    GstStructure *structure = gst_caps_get_structure (caps, i);
+    GstCapsFeatures *features = gst_caps_get_features (caps, i);
+
+    if (gst_structure_has_name (structure, "video/x-raw")
+        && gst_structure_has_field (structure, "colorimetry")) {
+      GstStructure *alt_s = gst_structure_copy (structure);
+      gst_structure_remove_field (alt_s, "colorimetry");
+      if (gst_caps_features_contains (features,
+              GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY))
+        gst_caps_append_structure (caps, alt_s);
+      else
+        gst_caps_append_structure_full (caps, alt_s,
+            gst_caps_features_copy (features));
+    }
+  }
+}
+
 static GstCaps *
-gst_v4l2_object_get_caps_helper (GstV4L2FormatFlags flags)
+gst_v4l2_object_get_caps_helper (GstV4L2FormatFlags flags,
+    const GstV4L2FormatDesc * formats, const guint len)
 {
   GstStructure *structure;
   GstCaps *caps, *caps_interlaced;
@@ -1668,25 +1706,25 @@ gst_v4l2_object_get_caps_helper (GstV4L2FormatFlags flags)
 
   caps = gst_caps_new_empty ();
   caps_interlaced = gst_caps_new_empty ();
-  for (i = 0; i < GST_V4L2_FORMAT_COUNT; i++) {
+  for (i = 0; i < len; i++) {
+    guint32 fourcc = formats[i].v4l2_format;
 
-    if ((gst_v4l2_formats[i].flags & flags) == 0)
+    if ((formats[i].flags & flags) == 0)
       continue;
 
-    structure =
-        gst_v4l2_object_v4l2fourcc_to_bare_struct (gst_v4l2_formats[i].format);
+    structure = gst_v4l2_object_v4l2fourcc_to_bare_struct (fourcc);
 
     if (structure) {
       GstStructure *alt_s = NULL;
 
-      if (gst_v4l2_formats[i].dimensions) {
+      if (formats[i].flags & GST_V4L2_RESOLUTION_AND_RATE) {
         gst_structure_set (structure,
             "width", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
             "height", GST_TYPE_INT_RANGE, 1, GST_V4L2_MAX_SIZE,
             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
       }
 
-      switch (gst_v4l2_formats[i].format) {
+      switch (fourcc) {
         case V4L2_PIX_FMT_RGB32:
           alt_s = gst_structure_copy (structure);
           gst_structure_set (alt_s, "format", G_TYPE_STRING, "ARGB", NULL);
@@ -1721,7 +1759,8 @@ gst_v4l2_object_get_all_caps (void)
   static GstCaps *caps = NULL;
 
   if (g_once_init_enter (&caps)) {
-    GstCaps *all_caps = gst_v4l2_object_get_caps_helper (GST_V4L2_ALL);
+    GstCaps *all_caps = gst_v4l2_object_get_caps_helper (GST_V4L2_ALL,
+        gst_v4l2_formats, GST_V4L2_FORMAT_COUNT);
     GST_MINI_OBJECT_FLAG_SET (all_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
     g_once_init_leave (&caps, all_caps);
   }
@@ -1735,7 +1774,8 @@ gst_v4l2_object_get_raw_caps (void)
   static GstCaps *caps = NULL;
 
   if (g_once_init_enter (&caps)) {
-    GstCaps *raw_caps = gst_v4l2_object_get_caps_helper (GST_V4L2_RAW);
+    GstCaps *raw_caps = gst_v4l2_object_get_caps_helper (GST_V4L2_RAW,
+        gst_v4l2_formats, GST_V4L2_FORMAT_COUNT);
     GST_MINI_OBJECT_FLAG_SET (raw_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
     g_once_init_leave (&caps, raw_caps);
   }
@@ -1749,7 +1789,8 @@ gst_v4l2_object_get_codec_caps (void)
   static GstCaps *caps = NULL;
 
   if (g_once_init_enter (&caps)) {
-    GstCaps *codec_caps = gst_v4l2_object_get_caps_helper (GST_V4L2_CODEC);
+    GstCaps *codec_caps = gst_v4l2_object_get_caps_helper (GST_V4L2_CODEC,
+        gst_v4l2_formats, GST_V4L2_FORMAT_COUNT);
     GST_MINI_OBJECT_FLAG_SET (codec_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
     g_once_init_leave (&caps, codec_caps);
   }
@@ -1757,6 +1798,46 @@ gst_v4l2_object_get_codec_caps (void)
   return caps;
 }
 
+/* This is a minimalist probe, for speed, we only enumerate formats */
+GstCaps *
+gst_v4l2_object_probe_template_caps (const gchar * device, gint video_fd,
+    enum v4l2_buf_type type)
+{
+  GArray *formats = g_array_new (FALSE, TRUE, sizeof (GstV4L2FormatDesc));
+  GstCaps *caps;
+  gint n;
+
+  GST_DEBUG ("Getting %s format enumerations", device);
+  for (n = 0;; n++) {
+    const GstV4L2FormatDesc *desc;
+    struct v4l2_fmtdesc fmtdesc = {
+      .index = n,
+      .type = type,
+    };
+
+    /* FIXME, missing libv4l2 support */
+    if (ioctl (video_fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0)
+      break;                    /* end of enumeration */
+
+    GST_LOG ("index:       %u", fmtdesc.index);
+    GST_LOG ("type:        %d", fmtdesc.type);
+    GST_LOG ("flags:       %08x", fmtdesc.flags);
+    GST_LOG ("description: '%s'", fmtdesc.description);
+    GST_LOG ("pixelformat: %" GST_FOURCC_FORMAT,
+        GST_FOURCC_ARGS (fmtdesc.pixelformat));
+
+    desc = gst_v4l2_object_get_desc_from_v4l2fourcc (fmtdesc.pixelformat);
+    if (desc)
+      g_array_append_val (formats, *desc);
+  }
+
+  caps = gst_v4l2_object_get_caps_helper (GST_V4L2_ALL,
+      (const GstV4L2FormatDesc *) formats->data, formats->len);
+  g_array_free (formats, TRUE);
+
+  return caps;
+}
+
 /* collect data for the given caps
  * @caps: given input caps
  * @format: location for the v4l format
@@ -1781,181 +1862,106 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps,
     goto invalid_format;
 
   if (g_str_equal (mimetype, "video/x-raw")) {
-    switch (GST_VIDEO_INFO_FORMAT (info)) {
-      case GST_VIDEO_FORMAT_I420:
-        fourcc = V4L2_PIX_FMT_YUV420;
-        fourcc_nc = V4L2_PIX_FMT_YUV420M;
-        break;
-      case GST_VIDEO_FORMAT_YUY2:
-        fourcc = V4L2_PIX_FMT_YUYV;
-        break;
-      case GST_VIDEO_FORMAT_UYVY:
-        fourcc = V4L2_PIX_FMT_UYVY;
-        break;
-      case GST_VIDEO_FORMAT_YV12:
-        fourcc = V4L2_PIX_FMT_YVU420;
-        break;
-      case GST_VIDEO_FORMAT_Y41B:
-        fourcc = V4L2_PIX_FMT_YUV411P;
-        break;
-      case GST_VIDEO_FORMAT_Y42B:
-        fourcc = V4L2_PIX_FMT_YUV422P;
-        break;
-      case GST_VIDEO_FORMAT_NV12:
-        fourcc = V4L2_PIX_FMT_NV12;
-        fourcc_nc = V4L2_PIX_FMT_NV12M;
-        break;
-      case GST_VIDEO_FORMAT_NV12_64Z32:
-        fourcc_nc = V4L2_PIX_FMT_NV12MT;
-        break;
-      case GST_VIDEO_FORMAT_NV12_16L32S:
-        fourcc_nc = V4L2_PIX_FMT_MM21;
-        break;
-      case GST_VIDEO_FORMAT_NV12_8L128:
-        fourcc_nc = V4L2_PIX_FMT_NV12M_8L128;
-        break;
-      case GST_VIDEO_FORMAT_NV12_10BE_8L128:
-        fourcc_nc = V4L2_PIX_FMT_NV12M_10BE_8L128;
-        break;
-      case GST_VIDEO_FORMAT_NV21:
-        fourcc = V4L2_PIX_FMT_NV21;
-        fourcc_nc = V4L2_PIX_FMT_NV21M;
-        break;
-      case GST_VIDEO_FORMAT_NV16:
-        fourcc = V4L2_PIX_FMT_NV16;
-        fourcc_nc = V4L2_PIX_FMT_NV16M;
-        break;
-      case GST_VIDEO_FORMAT_NV61:
-        fourcc = V4L2_PIX_FMT_NV61;
-        fourcc_nc = V4L2_PIX_FMT_NV61M;
-        break;
-      case GST_VIDEO_FORMAT_NV24:
-        fourcc = V4L2_PIX_FMT_NV24;
-        break;
-      case GST_VIDEO_FORMAT_YVYU:
-        fourcc = V4L2_PIX_FMT_YVYU;
-        break;
-      case GST_VIDEO_FORMAT_RGB15:
-        fourcc = V4L2_PIX_FMT_RGB555;
-        fourcc_nc = V4L2_PIX_FMT_XRGB555;
-        break;
-      case GST_VIDEO_FORMAT_RGB16:
-        fourcc = V4L2_PIX_FMT_RGB565;
-        break;
-      case GST_VIDEO_FORMAT_RGB:
-        fourcc = V4L2_PIX_FMT_RGB24;
-        break;
-      case GST_VIDEO_FORMAT_BGR:
-        fourcc = V4L2_PIX_FMT_BGR24;
-        break;
-      case GST_VIDEO_FORMAT_xRGB:
-        fourcc = V4L2_PIX_FMT_RGB32;
-        fourcc_nc = V4L2_PIX_FMT_XRGB32;
-        break;
-      case GST_VIDEO_FORMAT_RGBx:
-        fourcc = V4L2_PIX_FMT_RGBX32;
-        break;
-      case GST_VIDEO_FORMAT_ARGB:
-        fourcc = V4L2_PIX_FMT_RGB32;
-        fourcc_nc = V4L2_PIX_FMT_ARGB32;
-        break;
-      case GST_VIDEO_FORMAT_RGBA:
-        fourcc = V4L2_PIX_FMT_RGBA32;
-        break;
-      case GST_VIDEO_FORMAT_BGRx:
-        fourcc = V4L2_PIX_FMT_BGR32;
-        fourcc_nc = V4L2_PIX_FMT_XBGR32;
-        break;
-      case GST_VIDEO_FORMAT_xBGR:
-        fourcc = V4L2_PIX_FMT_BGRX32;
-        break;
-      case GST_VIDEO_FORMAT_BGRA:
-        fourcc = V4L2_PIX_FMT_BGR32;
-        fourcc_nc = V4L2_PIX_FMT_ABGR32;
-        break;
-      case GST_VIDEO_FORMAT_ABGR:
-        fourcc = V4L2_PIX_FMT_BGRA32;
-        break;
-      case GST_VIDEO_FORMAT_GRAY8:
-        fourcc = V4L2_PIX_FMT_GREY;
-        break;
-      case GST_VIDEO_FORMAT_GRAY16_LE:
-        fourcc = V4L2_PIX_FMT_Y16;
-        break;
-      case GST_VIDEO_FORMAT_GRAY16_BE:
-        fourcc = V4L2_PIX_FMT_Y16_BE;
-        break;
-      case GST_VIDEO_FORMAT_BGR15:
-        fourcc = V4L2_PIX_FMT_RGB555X;
-        fourcc_nc = V4L2_PIX_FMT_XRGB555X;
-        break;
-      default:
-        break;
-    }
-  } else {
-    if (g_str_equal (mimetype, "video/mpegts")) {
-      fourcc = V4L2_PIX_FMT_MPEG;
-    } else if (g_str_equal (mimetype, "video/x-dv")) {
-      fourcc = V4L2_PIX_FMT_DV;
-    } else if (g_str_equal (mimetype, "image/jpeg")) {
-      fourcc = V4L2_PIX_FMT_JPEG;
-    } else if (g_str_equal (mimetype, "video/mpeg")) {
-      gint version;
-      if (gst_structure_get_int (structure, "mpegversion", &version)) {
-        switch (version) {
-          case 1:
-            fourcc = V4L2_PIX_FMT_MPEG1;
-            break;
-          case 2:
-            fourcc = V4L2_PIX_FMT_MPEG2;
-            break;
-          case 4:
-            fourcc = V4L2_PIX_FMT_MPEG4;
-            fourcc_nc = V4L2_PIX_FMT_XVID;
-            break;
-          default:
-            break;
-        }
-      }
-    } else if (g_str_equal (mimetype, "video/x-fwht")) {
-      fourcc = V4L2_PIX_FMT_FWHT;
-    } else if (g_str_equal (mimetype, "video/x-h263")) {
-      fourcc = V4L2_PIX_FMT_H263;
-    } else if (g_str_equal (mimetype, "video/x-h264")) {
-      const gchar *stream_format =
-          gst_structure_get_string (structure, "stream-format");
-      if (g_str_equal (stream_format, "avc"))
-        fourcc = V4L2_PIX_FMT_H264_NO_SC;
-      else
-        fourcc = V4L2_PIX_FMT_H264;
-    } else if (g_str_equal (mimetype, "video/x-h265")) {
-      fourcc = V4L2_PIX_FMT_HEVC;
-    } else if (g_str_equal (mimetype, "video/x-vp8")) {
-      fourcc = V4L2_PIX_FMT_VP8;
-    } else if (g_str_equal (mimetype, "video/x-vp9")) {
-      fourcc = V4L2_PIX_FMT_VP9;
-    } else if (g_str_equal (mimetype, "video/x-bayer")) {
-      const gchar *format = gst_structure_get_string (structure, "format");
-      if (format) {
-        if (!g_ascii_strcasecmp (format, "bggr"))
-          fourcc = V4L2_PIX_FMT_SBGGR8;
-        else if (!g_ascii_strcasecmp (format, "gbrg"))
-          fourcc = V4L2_PIX_FMT_SGBRG8;
-        else if (!g_ascii_strcasecmp (format, "grbg"))
-          fourcc = V4L2_PIX_FMT_SGRBG8;
-        else if (!g_ascii_strcasecmp (format, "rggb"))
-          fourcc = V4L2_PIX_FMT_SRGGB8;
+    const GstV4L2FormatDesc *desc, *fallback_desc;
+    GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
+    desc = gst_v4l2_object_get_desc_from_video_format (format, &fallback_desc);
+    if (desc)
+      fourcc_nc = desc->v4l2_format;
+    if (fallback_desc)
+      fourcc = fallback_desc->v4l2_format;
+  } else if (g_str_equal (mimetype, "video/mpegts")) {
+    fourcc = V4L2_PIX_FMT_MPEG;
+  } else if (g_str_equal (mimetype, "video/x-dv")) {
+    fourcc = V4L2_PIX_FMT_DV;
+  } else if (g_str_equal (mimetype, "image/jpeg")) {
+    fourcc = V4L2_PIX_FMT_JPEG;
+  } else if (g_str_equal (mimetype, "video/mpeg")) {
+    gint version;
+    if (gst_structure_get_int (structure, "mpegversion", &version)) {
+      switch (version) {
+        case 1:
+          fourcc = V4L2_PIX_FMT_MPEG1;
+          break;
+        case 2:
+          fourcc = V4L2_PIX_FMT_MPEG2;
+          break;
+        case 4:
+          fourcc = V4L2_PIX_FMT_MPEG4;
+          fourcc_nc = V4L2_PIX_FMT_XVID;
+          break;
+        default:
+          break;
       }
-    } else if (g_str_equal (mimetype, "video/x-sonix")) {
-      fourcc = V4L2_PIX_FMT_SN9C10X;
-    } else if (g_str_equal (mimetype, "video/x-pwc1")) {
-      fourcc = V4L2_PIX_FMT_PWC1;
-    } else if (g_str_equal (mimetype, "video/x-pwc2")) {
-      fourcc = V4L2_PIX_FMT_PWC2;
     }
+  } else if (g_str_equal (mimetype, "video/x-fwht")) {
+    fourcc = V4L2_PIX_FMT_FWHT;
+  } else if (g_str_equal (mimetype, "video/x-h263")) {
+    fourcc = V4L2_PIX_FMT_H263;
+  } else if (g_str_equal (mimetype, "video/x-h264")) {
+    const gchar *stream_format =
+        gst_structure_get_string (structure, "stream-format");
+    if (g_str_equal (stream_format, "avc"))
+      fourcc = V4L2_PIX_FMT_H264_NO_SC;
+    else
+      fourcc = V4L2_PIX_FMT_H264;
+  } else if (g_str_equal (mimetype, "video/x-h265")) {
+    fourcc = V4L2_PIX_FMT_HEVC;
+  } else if (g_str_equal (mimetype, "video/x-vp8")) {
+    fourcc = V4L2_PIX_FMT_VP8;
+  } else if (g_str_equal (mimetype, "video/x-vp9")) {
+    fourcc = V4L2_PIX_FMT_VP9;
+  } else if (g_str_equal (mimetype, "video/x-bayer")) {
+    const gchar *format = gst_structure_get_string (structure, "format");
+    if (format) {
+      if (!g_ascii_strcasecmp (format, "bggr"))
+        fourcc = V4L2_PIX_FMT_SBGGR8;
+      else if (!g_ascii_strcasecmp (format, "gbrg"))
+        fourcc = V4L2_PIX_FMT_SGBRG8;
+      else if (!g_ascii_strcasecmp (format, "grbg"))
+        fourcc = V4L2_PIX_FMT_SGRBG8;
+      else if (!g_ascii_strcasecmp (format, "rggb"))
+        fourcc = V4L2_PIX_FMT_SRGGB8;
+      else if (!g_ascii_strcasecmp (format, "bggr10le"))
+        fourcc = V4L2_PIX_FMT_SBGGR10;
+      else if (!g_ascii_strcasecmp (format, "gbrg10le"))
+        fourcc = V4L2_PIX_FMT_SGBRG10;
+      else if (!g_ascii_strcasecmp (format, "grbg10le"))
+        fourcc = V4L2_PIX_FMT_SGRBG10;
+      else if (!g_ascii_strcasecmp (format, "rggb10le"))
+        fourcc = V4L2_PIX_FMT_SRGGB10;
+      else if (!g_ascii_strcasecmp (format, "bggr12le"))
+        fourcc = V4L2_PIX_FMT_SBGGR12;
+      else if (!g_ascii_strcasecmp (format, "gbrg12le"))
+        fourcc = V4L2_PIX_FMT_SGBRG12;
+      else if (!g_ascii_strcasecmp (format, "grbg12le"))
+        fourcc = V4L2_PIX_FMT_SGRBG12;
+      else if (!g_ascii_strcasecmp (format, "rggb12le"))
+        fourcc = V4L2_PIX_FMT_SRGGB12;
+      else if (!g_ascii_strcasecmp (format, "bggr14le"))
+        fourcc = V4L2_PIX_FMT_SBGGR14;
+      else if (!g_ascii_strcasecmp (format, "gbrg14le"))
+        fourcc = V4L2_PIX_FMT_SGBRG14;
+      else if (!g_ascii_strcasecmp (format, "grbg14le"))
+        fourcc = V4L2_PIX_FMT_SGRBG14;
+      else if (!g_ascii_strcasecmp (format, "rggb14le"))
+        fourcc = V4L2_PIX_FMT_SRGGB14;
+      else if (!g_ascii_strcasecmp (format, "bggr16le"))
+        fourcc = V4L2_PIX_FMT_SBGGR16;
+      else if (!g_ascii_strcasecmp (format, "gbrg16le"))
+        fourcc = V4L2_PIX_FMT_SGBRG16;
+      else if (!g_ascii_strcasecmp (format, "grbg16le"))
+        fourcc = V4L2_PIX_FMT_SGRBG16;
+      else if (!g_ascii_strcasecmp (format, "rggb16le"))
+        fourcc = V4L2_PIX_FMT_SRGGB16;
+    }
+  } else if (g_str_equal (mimetype, "video/x-sonix")) {
+    fourcc = V4L2_PIX_FMT_SN9C10X;
+  } else if (g_str_equal (mimetype, "video/x-pwc1")) {
+    fourcc = V4L2_PIX_FMT_PWC1;
+  } else if (g_str_equal (mimetype, "video/x-pwc2")) {
+    fourcc = V4L2_PIX_FMT_PWC2;
   }
 
-
   /* Prefer the non-contiguous if supported */
   v4l2object->prefered_non_contiguous = TRUE;
 
@@ -1979,17 +1985,20 @@ gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps,
   /* ERRORS */
 invalid_format:
   {
-    GST_DEBUG_OBJECT (v4l2object, "invalid format");
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+        "Invalid format %" GST_PTR_FORMAT, caps);
     return FALSE;
   }
 unhandled_format:
   {
-    GST_DEBUG_OBJECT (v4l2object, "unhandled format");
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+        "Unhandled format %" GST_PTR_FORMAT, caps);
     return FALSE;
   }
 unsupported_format:
   {
-    GST_DEBUG_OBJECT (v4l2object, "unsupported format");
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+        "Unsupported format: %" GST_PTR_FORMAT, caps);
     return FALSE;
   }
 }
@@ -2055,12 +2064,12 @@ static gboolean
 gst_v4l2_object_get_colorspace (GstV4l2Object * v4l2object,
     struct v4l2_format *fmt, GstVideoColorimetry * cinfo)
 {
-  gboolean is_rgb =
-      gst_v4l2_object_v4l2fourcc_is_rgb (fmt->fmt.pix.pixelformat);
+  gboolean is_rgb = FALSE;
   enum v4l2_colorspace colorspace;
   enum v4l2_quantization range;
   enum v4l2_ycbcr_encoding matrix;
   enum v4l2_xfer_func transfer;
+  guint32 pixelformat;
   gboolean ret = TRUE;
 
   if (V4L2_TYPE_IS_MULTIPLANAR (fmt->type)) {
@@ -2068,13 +2077,25 @@ gst_v4l2_object_get_colorspace (GstV4l2Object * v4l2object,
     range = fmt->fmt.pix_mp.quantization;
     matrix = fmt->fmt.pix_mp.ycbcr_enc;
     transfer = fmt->fmt.pix_mp.xfer_func;
+    pixelformat = fmt->fmt.pix_mp.pixelformat;
   } else {
     colorspace = fmt->fmt.pix.colorspace;
     range = fmt->fmt.pix.quantization;
     matrix = fmt->fmt.pix.ycbcr_enc;
     transfer = fmt->fmt.pix.xfer_func;
+    pixelformat = fmt->fmt.pix.pixelformat;
   }
 
+  /* Detect that we are dealing with RGB, this can be that the pixelformat is
+   * an RGB format, or we have an encoded format for which RGB color matrix
+   * was set in set_format. */
+  if (gst_v4l2_object_v4l2fourcc_is_rgb (pixelformat) ||
+      (v4l2object->matrix == GST_VIDEO_COLOR_MATRIX_RGB &&
+          gst_v4l2_object_v4l2fourcc_is_codec (pixelformat)))
+    is_rgb = TRUE;
+  else
+    is_rgb = FALSE;
+
   /* First step, set the defaults for each primaries */
   switch (colorspace) {
     case V4L2_COLORSPACE_SMPTE170M:
@@ -2090,6 +2111,11 @@ gst_v4l2_object_get_colorspace (GstV4l2Object * v4l2object,
       cinfo->primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
       break;
     case V4L2_COLORSPACE_SRGB:
+      cinfo->range = GST_VIDEO_COLOR_RANGE_16_235;
+      cinfo->matrix = GST_VIDEO_COLOR_MATRIX_BT601;
+      cinfo->transfer = GST_VIDEO_TRANSFER_SRGB;
+      cinfo->primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
+      break;
     case V4L2_COLORSPACE_JPEG:
       cinfo->range = GST_VIDEO_COLOR_RANGE_0_255;
       cinfo->matrix = GST_VIDEO_COLOR_MATRIX_BT601;
@@ -2152,11 +2178,7 @@ gst_v4l2_object_get_colorspace (GstV4l2Object * v4l2object,
       break;
     case V4L2_QUANTIZATION_DEFAULT:
       /* replicated V4L2_MAP_QUANTIZATION_DEFAULT macro behavior */
-      if (is_rgb && colorspace == V4L2_COLORSPACE_BT2020)
-        cinfo->range = GST_VIDEO_COLOR_RANGE_16_235;
-      else if (is_rgb || matrix == V4L2_YCBCR_ENC_XV601
-          || matrix == V4L2_YCBCR_ENC_XV709
-          || colorspace == V4L2_COLORSPACE_JPEG)
+      if (is_rgb || colorspace == V4L2_COLORSPACE_JPEG)
         cinfo->range = GST_VIDEO_COLOR_RANGE_0_255;
       else
         cinfo->range = GST_VIDEO_COLOR_RANGE_16_235;
@@ -2357,8 +2379,11 @@ gst_v4l2_object_add_interlace_mode (GstV4l2Object * v4l2object,
   if (gst_v4l2src_value_simplify (&interlace_formats)
       || gst_value_list_get_size (&interlace_formats) > 0)
     gst_structure_take_value (s, "interlace-mode", &interlace_formats);
-  else
-    GST_WARNING_OBJECT (v4l2object, "Failed to determine interlace mode");
+  else {
+    GST_WARNING_OBJECT (v4l2object->dbg_obj,
+        "Failed to determine interlace mode");
+    g_value_unset (&interlace_formats);
+  }
 
   return;
 }
@@ -2751,7 +2776,7 @@ gst_v4l2_object_update_and_append (GstV4l2Object * v4l2object,
     gint i = 0;
 
     for (; i < GST_V4L2_FORMAT_COUNT; i++) {
-      if (format == gst_v4l2_formats[i].format &&
+      if (format == gst_v4l2_formats[i].v4l2_format &&
           gst_v4l2_formats[i].flags & GST_V4L2_CODEC &&
           !(gst_v4l2_formats[i].flags & GST_V4L2_NO_PARSE)) {
         gst_structure_set (s, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
@@ -2812,6 +2837,8 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object,
     goto enum_framesizes_failed;
 
   if (size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+    guint32 maxw = 0, maxh = 0;
+
     do {
       GST_LOG_OBJECT (v4l2object->dbg_obj, "got discrete frame size %dx%d",
           size.discrete.width, size.discrete.height);
@@ -2828,8 +2855,16 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object,
           results = g_list_prepend (results, tmp);
       }
 
+      if (w > maxw && h > maxh) {
+        maxw = w;
+        maxh = h;
+      }
+
       size.index++;
     } while (v4l2object->ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &size) >= 0);
+
+    v4l2object->max_width = maxw;
+    v4l2object->max_height = maxh;
     GST_DEBUG_OBJECT (v4l2object->dbg_obj,
         "done iterating discrete frame sizes");
   } else if (size.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
@@ -2842,22 +2877,25 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object,
         size.stepwise.min_height);
     GST_DEBUG_OBJECT (v4l2object->dbg_obj, "max width:   %d",
         size.stepwise.max_width);
-    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "min height:  %d",
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "max height:  %d",
         size.stepwise.max_height);
     GST_DEBUG_OBJECT (v4l2object->dbg_obj, "step width:  %d",
         size.stepwise.step_width);
     GST_DEBUG_OBJECT (v4l2object->dbg_obj, "step height: %d",
         size.stepwise.step_height);
 
-    w = MAX (size.stepwise.min_width, 1);
-    h = MAX (size.stepwise.min_height, 1);
+    step_w = MAX (size.stepwise.step_width, 1);
+    step_h = MAX (size.stepwise.step_height, 1);
+    w = MAX (size.stepwise.min_width, step_w);
+    h = MAX (size.stepwise.min_height, step_h);
     maxw = MIN (size.stepwise.max_width, G_MAXINT);
     maxh = MIN (size.stepwise.max_height, G_MAXINT);
 
-    step_w = MAX (size.stepwise.step_width, 1);
-    step_h = MAX (size.stepwise.step_height, 1);
+    /* ensure maxes are multiples of the steps */
+    maxw -= maxw % step_w;
+    maxh -= maxh % step_h;
 
-    /* FIXME: check for sanity and that min/max are multiples of the steps */
+    /* FIXME: check for sanity */
 
     /* we only query details for the max width/height since it's likely the
      * most restricted if there are any resolution-dependent restrictions */
@@ -2876,6 +2914,9 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object,
 
       /* no point using the results list here, since there's only one struct */
       gst_v4l2_object_update_and_append (v4l2object, pixelformat, ret, tmp);
+
+      v4l2object->max_width = maxw;
+      v4l2object->max_height = maxh;
     }
   } else if (size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
     guint32 maxw, maxh;
@@ -2905,6 +2946,9 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object,
 
       /* no point using the results list here, since there's only one struct */
       gst_v4l2_object_update_and_append (v4l2object, pixelformat, ret, tmp);
+
+      v4l2object->max_width = maxw;
+      v4l2object->max_height = maxh;
     }
   } else {
     goto unknown_type;
@@ -2977,6 +3021,8 @@ default_frame_sizes:
       min_w = min_h = 1;
     if (max_w == 0 || max_h == 0)
       max_w = max_h = GST_V4L2_MAX_SIZE;
+    v4l2object->max_width = max_w;
+    v4l2object->max_height = max_h;
 
     /* Since we can't get framerate directly, try to use the current norm */
     if (v4l2object->tv_norm && v4l2object->norms) {
@@ -3257,10 +3303,9 @@ gst_v4l2_object_set_stride (GstVideoInfo * info, GstVideoAlignment * align,
 
     padded_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (finfo, plane,
         info->height + align->padding_top + align->padding_bottom);
-    padded_height = (padded_height + tile_height - 1) / tile_height;
 
     x_tiles = stride / GST_VIDEO_FORMAT_INFO_TILE_STRIDE (finfo, plane);
-    y_tiles = padded_height / tile_height;
+    y_tiles = (padded_height + tile_height - 1) / tile_height;
     info->stride[plane] = GST_VIDEO_TILE_MAKE_STRIDE (x_tiles, y_tiles);
   } else {
     info->stride[plane] = stride;
@@ -3330,10 +3375,10 @@ gst_v4l2_object_save_format (GstV4l2Object * v4l2object,
     padded_width = stride / pstride;
   } else {
     /* pstride can be 0 for complex formats */
-    GST_WARNING_OBJECT (v4l2object->element,
+    GST_WARNING_OBJECT (v4l2object->dbg_obj,
         "format %s has a pstride of 0, cannot compute padded with",
         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
-    padded_width = stride;
+    padded_width = format->fmt.pix.width;
   }
 
   if (padded_width < format->fmt.pix.width)
@@ -3349,7 +3394,9 @@ gst_v4l2_object_save_format (GstV4l2Object * v4l2object,
   if (GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) {
     guint tile_height;
     tile_height = GST_VIDEO_FORMAT_INFO_TILE_HEIGHT (finfo, 0);
-    padded_height = (padded_height + tile_height - 1) / tile_height;
+    /* Round-up to tile_height as drivers are not forced to do so */
+    padded_height =
+        (padded_height + tile_height - 1) / tile_height * tile_height;
   }
 
   align->padding_bottom =
@@ -3395,18 +3442,10 @@ gst_v4l2_object_save_format (GstV4l2Object * v4l2object,
     if ((align->padding_left + align->padding_top) > 0)
       GST_WARNING_OBJECT (v4l2object->dbg_obj,
           "Left and top padding is not permitted for tiled formats");
+    memset (v4l2object->plane_size, 0, sizeof (v4l2object->plane_size));
   } else {
-    for (i = 0; i < finfo->n_planes; i++) {
-      gint vedge, hedge;
-
-      /* FIXME we assume plane as component as this is true for all supported
-       * format we support. */
-
-      hedge = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (finfo, i, align->padding_left);
-      vedge = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (finfo, i, align->padding_top);
-
-      info->offset[i] += (vedge * info->stride[i]) +
-          (hedge * GST_VIDEO_INFO_COMP_PSTRIDE (info, i));
+    if (!gst_video_info_align_full (info, align, v4l2object->plane_size)) {
+      GST_WARNING_OBJECT (v4l2object->dbg_obj, "Failed to align video info");
     }
   }
 
@@ -3500,6 +3539,18 @@ gst_v4l2_video_colorimetry_matches (const GstVideoColorimetry * cinfo,
       && gst_video_colorimetry_is_equal (cinfo, &ci_jpeg))
     return TRUE;
 
+  /* bypass check the below GST_VIDEO_TRANSFER_ARIB_STD_B67 type,
+   * because kernel do not support it. GST_VIDEO_TRANSFER_ARIB_STD_B67 is cast
+   * to GST_VIDEO_TRANSFER_BT2020_12 type in gst_v4l2_object_get_colorspace */
+  if (info.colorimetry.transfer == GST_VIDEO_TRANSFER_ARIB_STD_B67) {
+    info.colorimetry.transfer = cinfo->transfer;
+    GST_WARNING
+        ("v4l2 framework do not support GST_VIDEO_TRANSFER_ARIB_STD_B67");
+
+    if (gst_video_colorimetry_is_equal (&info.colorimetry, cinfo))
+      return TRUE;
+  }
+
   /* bypass check the below transfer types, because those types are cast to
    * V4L2_XFER_FUNC_NONE type when try format or set format and V4L2_XFER_FUNC_NONE
    * type is cast to GST_VIDEO_TRANSFER_GAMMA10 type in gst_v4l2_object_get_colorspace */
@@ -3544,6 +3595,19 @@ field_to_str (enum v4l2_field f)
   return "unknown";
 }
 
+static guint
+calculate_max_sizeimage (GstV4l2Object * v4l2object, guint pixel_bitdepth)
+{
+  guint max_width, max_height;
+  guint sizeimage;
+
+  max_width = v4l2object->max_width;
+  max_height = v4l2object->max_height;
+  sizeimage = max_width * max_height * pixel_bitdepth / 8 / 2;
+
+  return MAX (ENCODED_BUFFER_MIN_SIZE, sizeimage);
+}
+
 static gboolean
 gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
     gboolean try_only, GstV4l2Error * error)
@@ -3557,6 +3621,7 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
   GstVideoInfo info;
   GstVideoAlignment align;
   gint width, height, fps_n, fps_d;
+  guint pixel_bitdepth = 8;
   gint n_v4l_planes;
   gint i = 0;
   gboolean is_mplane;
@@ -3580,6 +3645,7 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
   gst_video_info_init (&info);
   gst_video_alignment_reset (&align);
   v4l2object->transfer = GST_VIDEO_TRANSFER_UNKNOWN;
+  v4l2object->matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN;
 
   if (!gst_v4l2_object_get_caps_info (v4l2object, caps, &fmtdesc, &info))
     goto invalid_caps;
@@ -3603,19 +3669,23 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
 
   field = get_v4l2_field_for_info (&info);
   if (field != V4L2_FIELD_NONE)
-    GST_DEBUG_OBJECT (v4l2object->element, "interlaced video");
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "interlaced video");
   else
-    GST_DEBUG_OBJECT (v4l2object->element, "progressive video");
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "progressive video");
 
   /* We first pick the main colorspace from the primaries */
   switch (info.colorimetry.primaries) {
     case GST_VIDEO_COLOR_PRIMARIES_BT709:
-      /* There is two colorspaces using these primaries, use the range to
-       * differentiate */
-      if (info.colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235)
-        colorspace = V4L2_COLORSPACE_REC709;
-      else
-        colorspace = V4L2_COLORSPACE_SRGB;
+      /* There is three colorspaces using these primaries, use the range
+       * and format type to differentiate them  */
+      if (info.colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235) {
+        if (GST_VIDEO_INFO_IS_RGB (&info))
+          colorspace = V4L2_COLORSPACE_SRGB;
+        else
+          colorspace = V4L2_COLORSPACE_REC709;
+      } else {
+        colorspace = V4L2_COLORSPACE_JPEG;
+      }
       break;
     case GST_VIDEO_COLOR_PRIMARIES_BT2020:
       colorspace = V4L2_COLORSPACE_BT2020;
@@ -3662,6 +3732,8 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
 
   switch (info.colorimetry.matrix) {
     case GST_VIDEO_COLOR_MATRIX_RGB:
+      /* save the matrix so we can restore it on get() call from default */
+      v4l2object->matrix = GST_VIDEO_COLOR_MATRIX_RGB;
       /* Unspecified, leave to default */
       break;
       /* FCC is about the same as BT601 with less digit */
@@ -3755,6 +3827,14 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
       "%" GST_FOURCC_FORMAT " stride: %d", width, height,
       GST_FOURCC_ARGS (pixelformat), GST_VIDEO_INFO_PLANE_STRIDE (&info, 0));
 
+  s = gst_caps_get_structure (caps, 0);
+
+  if (gst_structure_has_field (s, "bit-depth-chroma")) {
+    gst_structure_get_uint (s, "bit-depth-chroma", &pixel_bitdepth);
+    GST_DEBUG_OBJECT (v4l2object->element, "Got pixel bit depth %u from caps",
+        pixel_bitdepth);
+  }
+
   memset (&format, 0x00, sizeof (struct v4l2_format));
   format.type = v4l2object->type;
 
@@ -3779,7 +3859,8 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
     }
 
     if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ENCODED)
-      format.fmt.pix_mp.plane_fmt[0].sizeimage = ENCODED_BUFFER_SIZE;
+      format.fmt.pix_mp.plane_fmt[0].sizeimage =
+          calculate_max_sizeimage (v4l2object, pixel_bitdepth);
   } else {
     gint stride = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0);
 
@@ -3798,7 +3879,8 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
     format.fmt.pix.bytesperline = stride;
 
     if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ENCODED)
-      format.fmt.pix.sizeimage = ENCODED_BUFFER_SIZE;
+      format.fmt.pix.sizeimage =
+          calculate_max_sizeimage (v4l2object, pixel_bitdepth);
   }
 
   GST_DEBUG_OBJECT (v4l2object->dbg_obj, "Desired format is %dx%d, format "
@@ -3823,12 +3905,16 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
     format.fmt.pix_mp.quantization = range;
     format.fmt.pix_mp.ycbcr_enc = matrix;
     format.fmt.pix_mp.xfer_func = transfer;
+    if (V4L2_TYPE_IS_CAPTURE (v4l2object->type))
+      format.fmt.pix_mp.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
   } else {
     format.fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
     format.fmt.pix.colorspace = colorspace;
     format.fmt.pix.quantization = range;
     format.fmt.pix.ycbcr_enc = matrix;
     format.fmt.pix.xfer_func = transfer;
+    if (V4L2_TYPE_IS_CAPTURE (v4l2object->type))
+      format.fmt.pix.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
   }
 
   GST_DEBUG_OBJECT (v4l2object->dbg_obj, "Desired colorspace is %d:%d:%d:%d",
@@ -3876,6 +3962,101 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
   if (format.fmt.pix.pixelformat != pixelformat)
     goto invalid_pixelformat;
 
+  /* Passing HDR10 information
+   *
+   * TODO:
+   *  - Missing capture (v4l2src) HDR10 configuration and/or reporting
+   *  - The API is not capable of HDR to HDR conversion as controls are not specific to queues
+   */
+  if (V4L2_TYPE_IS_OUTPUT (v4l2object->type)) {
+    GstVideoMasteringDisplayInfo video_master_display_info;
+    GstVideoContentLightLevel video_content_light_level;
+    struct v4l2_ext_control ext_control[2];
+    struct v4l2_ctrl_hdr10_mastering_display hdr10_mastering_display;
+    struct v4l2_ctrl_hdr10_cll_info hdr10_cll_info;
+    int count = 0;
+
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "Passing HDR10 medata");
+    memset (&hdr10_cll_info, 0, sizeof (struct v4l2_ctrl_hdr10_cll_info));
+    memset (&hdr10_mastering_display,
+        0, sizeof (struct v4l2_ctrl_hdr10_mastering_display));
+
+    if (gst_video_mastering_display_info_from_caps (&video_master_display_info,
+            caps)) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "video mastering display info: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+          video_master_display_info.display_primaries[0].x,
+          video_master_display_info.display_primaries[0].y,
+          video_master_display_info.display_primaries[1].x,
+          video_master_display_info.display_primaries[1].y,
+          video_master_display_info.display_primaries[2].x,
+          video_master_display_info.display_primaries[2].y,
+          video_master_display_info.white_point.x,
+          video_master_display_info.white_point.y,
+          video_master_display_info.max_display_mastering_luminance,
+          video_master_display_info.min_display_mastering_luminance);
+
+      hdr10_mastering_display.display_primaries_x[2] =
+          video_master_display_info.display_primaries[0].x;
+      hdr10_mastering_display.display_primaries_y[2] =
+          video_master_display_info.display_primaries[0].y;
+      hdr10_mastering_display.display_primaries_x[0] =
+          video_master_display_info.display_primaries[1].x;
+      hdr10_mastering_display.display_primaries_y[0] =
+          video_master_display_info.display_primaries[1].y;
+      hdr10_mastering_display.display_primaries_x[1] =
+          video_master_display_info.display_primaries[2].x;
+      hdr10_mastering_display.display_primaries_y[1] =
+          video_master_display_info.display_primaries[2].y;
+      hdr10_mastering_display.white_point_x =
+          video_master_display_info.white_point.x;
+      hdr10_mastering_display.white_point_y =
+          video_master_display_info.white_point.y;
+      hdr10_mastering_display.min_display_mastering_luminance =
+          video_master_display_info.min_display_mastering_luminance;
+      hdr10_mastering_display.max_display_mastering_luminance =
+          video_master_display_info.max_display_mastering_luminance;
+
+      ext_control[count].id = V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY;
+      ext_control[count].size =
+          sizeof (struct v4l2_ctrl_hdr10_mastering_display);
+      ext_control[count].ptr = &hdr10_mastering_display;
+      count++;
+    }
+
+    if (gst_video_content_light_level_from_caps (&video_content_light_level,
+            caps)) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj, "video content light level: %d:%d",
+          video_content_light_level.max_content_light_level,
+          video_content_light_level.max_frame_average_light_level);
+
+      hdr10_cll_info.max_content_light_level =
+          video_content_light_level.max_content_light_level;
+      hdr10_cll_info.max_pic_average_light_level =
+          video_content_light_level.max_frame_average_light_level;
+
+      ext_control[count].id = V4L2_CID_COLORIMETRY_HDR10_CLL_INFO;
+      ext_control[count].size = sizeof (struct v4l2_ctrl_hdr10_cll_info);
+      ext_control[count].ptr = &hdr10_cll_info;
+      count++;
+    }
+
+    if (count != 0) {
+      struct v4l2_ext_controls ext_controls = {
+        .ctrl_class = V4L2_CTRL_CLASS_COLORIMETRY,
+        .count = count,
+        .controls = ext_control,
+      };
+
+      if (v4l2object->ioctl (fd, VIDIOC_S_EXT_CTRLS, &ext_controls) < 0) {
+        if (errno != ENOTTY) {
+          GST_WARNING_OBJECT (v4l2object->dbg_obj,
+              "Failed to set HDR10 metadata, err: %s", g_strerror (errno));
+        }
+      }
+    }
+  }
+
   /* Only negotiate size with raw data.
    * For some codecs the dimensions are *not* in the bitstream, IIRC VC1
    * in ASF mode for example, there is also not reason for a driver to
@@ -3896,8 +4077,6 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
     goto invalid_planes;
 
   /* used to check colorimetry and interlace mode fields presence */
-  s = gst_caps_get_structure (caps, 0);
-
   if (gst_v4l2_object_get_interlace_mode (format.fmt.pix.field,
           &info.interlace_mode)) {
     if (gst_structure_has_field (s, "interlace-mode")) {
@@ -3988,15 +4167,25 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
       goto done;
     }
 
-    /* Note: V4L2 wants the frame interval, we have the frame rate */
-    streamparm.parm.capture.timeperframe.numerator = fps_d;
-    streamparm.parm.capture.timeperframe.denominator = fps_n;
+    /* Variable framerate is not a V4L2 concept, simply default to 30fps */
+    if (fps_n == 0 && fps_d == 1) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "Variable framerate not supported, assuming 30fps");
+      streamparm.parm.capture.timeperframe.numerator = 1;
+      streamparm.parm.capture.timeperframe.denominator = 30;
+    } else {
+      /* Note: V4L2 wants the frame interval, we have the frame rate */
+      streamparm.parm.capture.timeperframe.numerator = fps_d;
+      streamparm.parm.capture.timeperframe.denominator = fps_n;
+    }
 
     /* some cheap USB cam's won't accept any change */
     if (v4l2object->ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0)
       goto set_parm_failed;
 
-    if (streamparm.parm.capture.timeperframe.numerator > 0 &&
+    if (fps_n == 0 && fps_d == 1) {
+      /* just keep reporting variable framerate */
+    } else if (streamparm.parm.capture.timeperframe.numerator > 0 &&
         streamparm.parm.capture.timeperframe.denominator > 0) {
       /* get new values */
       fps_d = streamparm.parm.capture.timeperframe.numerator;
@@ -4031,14 +4220,24 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
       goto done;
     }
 
-    /* Note: V4L2 wants the frame interval, we have the frame rate */
-    streamparm.parm.output.timeperframe.numerator = fps_d;
-    streamparm.parm.output.timeperframe.denominator = fps_n;
+    /* Variable framerate is not a V4L2 concept, simply default to 30fps */
+    if (fps_n == 0 && fps_d == 1) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "Variable framerate not supported, assuming 30fps");
+      streamparm.parm.output.timeperframe.numerator = 1;
+      streamparm.parm.output.timeperframe.denominator = 30;
+    } else {
+      /* Note: V4L2 wants the frame interval, we have the frame rate */
+      streamparm.parm.output.timeperframe.numerator = fps_d;
+      streamparm.parm.output.timeperframe.denominator = fps_n;
+    }
 
     if (v4l2object->ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0)
       goto set_parm_failed;
 
-    if (streamparm.parm.output.timeperframe.numerator > 0 &&
+    if (fps_n == 0 && fps_d == 1) {
+      /* just keep reporting variable framerate */
+    } else if (streamparm.parm.output.timeperframe.numerator > 0 &&
         streamparm.parm.output.timeperframe.denominator > 0) {
       /* get new values */
       fps_d = streamparm.parm.output.timeperframe.numerator;
@@ -4248,6 +4447,7 @@ gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info)
   gst_video_info_init (info);
   gst_video_alignment_reset (&align);
   v4l2object->transfer = GST_VIDEO_TRANSFER_UNKNOWN;
+  v4l2object->matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN;
 
   memset (&fmt, 0x00, sizeof (struct v4l2_format));
   fmt.type = v4l2object->type;
@@ -4311,8 +4511,9 @@ gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info)
       goto unsupported_field;
   }
 
-  gst_video_info_set_interlaced_format (info, format, interlace_mode, width,
-      height);
+  if (!gst_video_info_set_interlaced_format (info, format, interlace_mode,
+          width, height))
+    goto invalid_dimensions;
 
   gst_v4l2_object_get_colorspace (v4l2object, &fmt, &info->colorimetry);
   gst_v4l2_object_get_streamparm (v4l2object, info);
@@ -4380,7 +4581,7 @@ unsupported_format:
  * Returns: %TRUE on success, %FALSE on failure.
  */
 gboolean
-gst_v4l2_object_set_crop (GstV4l2Object * obj, struct v4l2_rect * crop_rect)
+gst_v4l2_object_set_crop (GstV4l2Object * obj, struct v4l2_rect *crop_rect)
 {
   struct v4l2_selection sel = { 0 };
   struct v4l2_crop crop = { 0 };
@@ -4503,14 +4704,13 @@ gst_v4l2_object_get_crop_rect (GstV4l2Object * obj, guint target,
 }
 
 gboolean
-gst_v4l2_object_get_crop_bounds (GstV4l2Object * obj, struct v4l2_rect * result)
+gst_v4l2_object_get_crop_bounds (GstV4l2Object * obj, struct v4l2_rect *result)
 {
   return gst_v4l2_object_get_crop_rect (obj, V4L2_SEL_TGT_CROP_BOUNDS, result);
 }
 
 gboolean
-gst_v4l2_object_get_crop_default (GstV4l2Object * obj,
-    struct v4l2_rect * result)
+gst_v4l2_object_get_crop_default (GstV4l2Object * obj, struct v4l2_rect *result)
 {
   return gst_v4l2_object_get_crop_rect (obj, V4L2_SEL_TGT_CROP_DEFAULT, result);
 }
@@ -4589,6 +4789,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
 
   GST_LOG_OBJECT (v4l2object->dbg_obj, "start flushing");
 
+  gst_poll_set_flushing (v4l2object->poll, TRUE);
+
   if (!pool)
     return ret;
 
@@ -4607,6 +4809,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
 
   GST_LOG_OBJECT (v4l2object->dbg_obj, "stop flushing");
 
+  gst_poll_set_flushing (v4l2object->poll, FALSE);
+
   if (!pool)
     return ret;
 
@@ -4628,6 +4832,8 @@ gst_v4l2_object_stop (GstV4l2Object * v4l2object)
   if (!GST_V4L2_IS_ACTIVE (v4l2object))
     goto done;
 
+  gst_poll_set_flushing (v4l2object->poll, TRUE);
+
   pool = gst_v4l2_object_get_buffer_pool (v4l2object);
   if (pool) {
     if (!gst_v4l2_buffer_pool_orphan (v4l2object)) {
@@ -4643,8 +4849,8 @@ gst_v4l2_object_stop (GstV4l2Object * v4l2object)
         if (old_pool)
           gst_object_unref (old_pool);
       }
-      gst_object_unref (pool);
     }
+    gst_object_unref (pool);
   }
 
   GST_V4L2_SET_INACTIVE (v4l2object);
@@ -4656,13 +4862,25 @@ done:
 GstCaps *
 gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter)
 {
-  GstCaps *ret;
+  GstCaps *caps, *interlaced_caps;
   GSList *walk;
   GSList *formats;
+  guint32 fourcc = 0;
+
+  if (v4l2object->fmtdesc)
+    fourcc = GST_V4L2_PIXELFORMAT (v4l2object);
 
+  gst_v4l2_object_clear_format_list (v4l2object);
   formats = gst_v4l2_object_get_format_list (v4l2object);
 
-  ret = gst_caps_new_empty ();
+  /* Recover the fmtdesc, it may no longer exist, in which case it will be set
+   * to null */
+  if (fourcc)
+    v4l2object->fmtdesc =
+        gst_v4l2_object_get_format_from_fourcc (v4l2object, fourcc);
+
+  caps = gst_caps_new_empty ();
+  interlaced_caps = gst_caps_new_empty ();
 
   if (v4l2object->keep_aspect && !v4l2object->par) {
     struct v4l2_cropcap cropcap;
@@ -4671,10 +4889,24 @@ gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter)
 
     cropcap.type = v4l2object->type;
     if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_CROPCAP, &cropcap) < 0) {
-      if (errno != ENOTTY)
-        GST_WARNING_OBJECT (v4l2object->dbg_obj,
-            "Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: %s",
-            g_strerror (errno));
+
+      switch (errno) {
+        case ENODATA:
+        case ENOTTY:
+          GST_INFO_OBJECT (v4l2object->dbg_obj,
+              "Driver does not support VIDIOC_CROPCAP (%s), assuming pixel aspect ratio 1/1",
+              g_strerror (errno));
+          break;
+
+        default:
+          GST_WARNING_OBJECT (v4l2object->dbg_obj,
+              "Failed to probe pixel aspect ratio with VIDIOC_CROPCAP: %s",
+              g_strerror (errno));
+      }
+      v4l2object->par = g_new0 (GValue, 1);
+      g_value_init (v4l2object->par, GST_TYPE_FRACTION);
+      gst_value_set_fraction (v4l2object->par, 1, 1);
+
     } else if (cropcap.pixelaspect.numerator && cropcap.pixelaspect.denominator) {
       v4l2object->par = g_new0 (GValue, 1);
       g_value_init (v4l2object->par, GST_TYPE_FRACTION);
@@ -4704,6 +4936,7 @@ gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter)
       GstCaps *format_caps = gst_caps_new_empty ();
 
       gst_caps_append_structure (format_caps, gst_structure_copy (template));
+      add_alternate_variant (v4l2object, format_caps, template);
 
       if (!gst_caps_can_intersect (format_caps, filter)) {
         gst_caps_unref (format_caps);
@@ -4717,27 +4950,42 @@ gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter)
     tmp = gst_v4l2_object_probe_caps_for_format (v4l2object,
         format->pixelformat, template);
     if (tmp) {
-      gst_caps_append (ret, tmp);
-
       /* Add a variant of the caps with the Interlaced feature so we can negotiate it if needed */
-      add_alternate_variant (v4l2object, ret, gst_caps_get_structure (ret,
-              gst_caps_get_size (ret) - 1));
+      gint i;
+      for (i = 0; i < gst_caps_get_size (tmp); i++) {
+        GstStructure *s = gst_caps_get_structure (tmp, i);
+        add_alternate_variant (v4l2object, interlaced_caps, s);
+      }
+
+      gst_caps_append (caps, tmp);
     }
 
     gst_structure_free (template);
   }
 
+  caps = gst_caps_simplify (caps);
+  interlaced_caps = gst_caps_simplify (interlaced_caps);
+
+  gst_caps_append (caps, interlaced_caps);
+
   if (filter) {
     GstCaps *tmp;
 
-    tmp = ret;
-    ret = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
+    tmp = caps;
+    caps = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
     gst_caps_unref (tmp);
   }
 
-  GST_INFO_OBJECT (v4l2object->dbg_obj, "probed caps: %" GST_PTR_FORMAT, ret);
+  /* Add a variant of the caps without the colorimetry so that we can negotiate
+     successfully even if the detected colorimetry from upstream is not supported
+     by the device */
+  if (caps) {
+    add_non_colorimetry_caps (v4l2object, caps);
+  }
+
+  GST_INFO_OBJECT (v4l2object->dbg_obj, "probed caps: %" GST_PTR_FORMAT, caps);
 
-  return ret;
+  return caps;
 }
 
 GstCaps *
@@ -5447,3 +5695,138 @@ gst_v4l2_object_get_buffer_pool (GstV4l2Object * v4l2object)
 
   return ret;
 }
+
+/**
+ * gst_v4l2_object_poll:
+ * @v4l2object: a #GstV4l2Object
+ * @timeout: timeout of type #GstClockTime
+ *
+ * Poll the video file descriptor for read when this is a capture, write when
+ * this is an output. It will also watch for errors and source change events.
+ * If a source change event is received, %GST_V4L2_FLOW_RESOLUTION_CHANGE will
+ * be returned. If the poll was interrupted, %GST_FLOW_FLUSHING is returned.
+ * If there was no read or write indicator, %GST_V4L2_FLOW_LAST_BUFFER is
+ * returned. It may also return %GST_FLOW_ERROR if some unexpected error
+ * occured.
+ *
+ * Returns: GST_FLOW_OK if buffers are ready to be queued or dequeued.
+ */
+GstFlowReturn
+gst_v4l2_object_poll (GstV4l2Object * v4l2object, GstClockTime timeout)
+{
+  gint ret;
+
+  if (!v4l2object->can_poll_device) {
+    if (timeout != 0)
+      goto done;
+    else
+      goto no_buffers;
+  }
+
+  GST_LOG_OBJECT (v4l2object->dbg_obj, "polling device");
+
+again:
+  ret = gst_poll_wait (v4l2object->poll, timeout);
+  if (G_UNLIKELY (ret < 0)) {
+    switch (errno) {
+      case EBUSY:
+        goto stopped;
+      case EAGAIN:
+      case EINTR:
+        goto again;
+      case ENXIO:
+        GST_WARNING_OBJECT (v4l2object->dbg_obj,
+            "v4l2 device doesn't support polling. Disabling"
+            " using libv4l2 in this case may cause deadlocks");
+        v4l2object->can_poll_device = FALSE;
+        goto done;
+      default:
+        goto select_error;
+    }
+  }
+
+  if (gst_poll_fd_has_error (v4l2object->poll, &v4l2object->pollfd))
+    goto select_error;
+
+  /* PRI is used to signal that events are available */
+  if (gst_poll_fd_has_pri (v4l2object->poll, &v4l2object->pollfd)) {
+    struct v4l2_event event = { 0, };
+
+    if (!gst_v4l2_dequeue_event (v4l2object, &event))
+      goto dqevent_failed;
+
+    if (event.type != V4L2_EVENT_SOURCE_CHANGE) {
+      GST_INFO_OBJECT (v4l2object->dbg_obj,
+          "Received unhandled event, ignoring.");
+      goto again;
+    }
+
+    if ((event.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) == 0) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "Received non-resolution source-change, ignoring.");
+      goto again;
+    }
+
+    if (v4l2object->formats)
+      gst_v4l2_object_clear_format_list (v4l2object);
+
+    return GST_V4L2_FLOW_RESOLUTION_CHANGE;
+  }
+
+  if (ret == 0)
+    goto no_buffers;
+
+done:
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+stopped:
+  {
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "stop called");
+    return GST_FLOW_FLUSHING;
+  }
+select_error:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
+        ("poll error %d: %s (%d)", ret, g_strerror (errno), errno));
+    return GST_FLOW_ERROR;
+  }
+no_buffers:
+  {
+    return GST_V4L2_FLOW_LAST_BUFFER;
+  }
+dqevent_failed:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
+        ("dqevent error: %s (%d)", g_strerror (errno), errno));
+    return GST_FLOW_ERROR;
+  }
+}
+
+/**
+ * gst_v4l2_object_subscribe_event:
+ * @v4l2object: a #GstV4l2Object
+ * @event: the event ID
+ *
+ * Subscribe to an event, and enable polling for these. Note that only
+ * %V4L2_EVENT_SOURCE_CHANGE is currently supported by the poll helper.
+ *
+ * Returns: %TRUE if the driver supports this event
+ */
+gboolean
+gst_v4l2_object_subscribe_event (GstV4l2Object * v4l2object, guint32 event)
+{
+  guint32 id = 0;
+
+  g_return_val_if_fail (v4l2object != NULL, FALSE);
+  g_return_val_if_fail (GST_V4L2_IS_OPEN (v4l2object), FALSE);
+
+  v4l2object->get_in_out_func (v4l2object, &id);
+
+  if (gst_v4l2_subscribe_event (v4l2object, event, id)) {
+    gst_poll_fd_ctl_pri (v4l2object->poll, &v4l2object->pollfd, TRUE);
+    return TRUE;
+  }
+
+  return FALSE;
+}
diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h
index 3a5c961601e1e965526e061d35c065d5818a1912..2e8d9264daad2f14f06510e2a42e69693fce2c10 100644
--- a/sys/v4l2/gstv4l2object.h
+++ b/sys/v4l2/gstv4l2object.h
@@ -76,6 +76,8 @@ typedef gboolean  (*GstV4l2UpdateFpsFunction) (GstV4l2Object * v4l2object);
  * 'unsigned long' for the 2nd parameter */
 #ifdef __ANDROID__
 typedef unsigned ioctl_req_t;
+#elif defined(__linux__) && !defined(__GLIBC__) /* musl/linux */
+typedef int ioctl_req_t;
 #else
 typedef gulong ioctl_req_t;
 #endif
@@ -134,6 +136,9 @@ struct _GstV4l2Object {
   /* the video-device's file descriptor */
   gint video_fd;
   GstV4l2IOMode mode;
+  GstPoll *poll;
+  GstPollFD pollfd;
+  gboolean can_poll_device;
 
   gboolean active;
 
@@ -143,6 +148,8 @@ struct _GstV4l2Object {
   GstVideoInfo info;
   GstVideoAlignment align;
   GstVideoTransferFunction transfer;
+  gsize plane_size[GST_VIDEO_MAX_PLANES];
+  GstVideoColorMatrix matrix;
 
   /* Features */
   gboolean need_video_meta;
@@ -222,6 +229,9 @@ struct _GstV4l2Object {
    * on slow USB firmwares. When this is set, gst_v4l2_set_format() will modify
    * the caps to reflect what was negotiated during fixation */
   gboolean skip_try_fmt_probes;
+  
+  guint max_width;
+  guint max_height;
 };
 
 struct _GstV4l2ObjectClassHelper {
@@ -277,7 +287,6 @@ gboolean     gst_v4l2_object_get_property_helper       (GstV4l2Object *v4l2objec
 gboolean     gst_v4l2_object_open            (GstV4l2Object * v4l2object, GstV4l2Error * error);
 gboolean     gst_v4l2_object_open_shared     (GstV4l2Object * v4l2object, GstV4l2Object * other);
 gboolean     gst_v4l2_object_close           (GstV4l2Object * v4l2object);
-gboolean     gst_v4l2_object_clear_format_list (GstV4l2Object * v4l2object);
 
 /* probing */
 
@@ -287,6 +296,9 @@ GstCaps*     gst_v4l2_object_get_raw_caps (void);
 
 GstCaps*     gst_v4l2_object_get_codec_caps (void);
 
+GstCaps*     gst_v4l2_object_probe_template_caps (const gchar * device, gint video_fd,
+                                                  enum v4l2_buf_type type);
+
 gboolean     gst_v4l2_object_set_format  (GstV4l2Object * v4l2object, GstCaps * caps, GstV4l2Error * error);
 gboolean     gst_v4l2_object_try_format  (GstV4l2Object * v4l2object, GstCaps * caps, GstV4l2Error * error);
 gboolean     gst_v4l2_object_try_import  (GstV4l2Object * v4l2object, GstBuffer * buffer);
@@ -315,6 +327,13 @@ GstBufferPool * gst_v4l2_object_get_buffer_pool (GstV4l2Object * v4l2object);
 
 GstStructure * gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc);
 
+GstVideoFormat gst_v4l2_object_v4l2fourcc_to_video_format (guint32 fourcc);
+
+GstFlowReturn  gst_v4l2_object_poll (GstV4l2Object * v4l2object, GstClockTime timeout);
+gboolean       gst_v4l2_object_subscribe_event (GstV4l2Object * v4l2object, guint32 event);
+
+gboolean     gst_v4l2_object_is_raw (GstV4l2Object * obj);
+
 /* crop / compose */
 gboolean     gst_v4l2_object_set_crop (GstV4l2Object * obj, struct v4l2_rect *result);
 gboolean     gst_v4l2_object_get_crop_bounds (GstV4l2Object * obj, struct v4l2_rect *bounds);
diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c
index e33b71ec3cdec58e0b70d98832a6ca49f2b851fc..3272480f763345a8e4c28d6cffffd8001fc6a3b4 100644
--- a/sys/v4l2/gstv4l2src.c
+++ b/sys/v4l2/gstv4l2src.c
@@ -438,68 +438,46 @@ gst_v4l2_src_parse_fixed_struct (GstStructure * s,
     gst_structure_get_fraction (s, "framerate", fps_n, fps_d);
 }
 
-/* TODO Consider framerate */
 static gint
 gst_v4l2src_fixed_caps_compare (GstCaps * caps_a, GstCaps * caps_b,
     struct PreferredCapsInfo *pref)
 {
   GstStructure *a, *b;
-  gint aw = G_MAXINT, ah = G_MAXINT, ad = G_MAXINT;
-  gint bw = G_MAXINT, bh = G_MAXINT, bd = G_MAXINT;
-  gint ret;
+  gint aw = G_MAXINT, ah = G_MAXINT;
+  gint bw = G_MAXINT, bh = G_MAXINT;
+  gint a_fps_n = G_MAXINT, a_fps_d = 1;
+  gint b_fps_n = G_MAXINT, b_fps_d = 1;
+  gint a_distance, b_distance;
+  gint ret = 0;
 
   a = gst_caps_get_structure (caps_a, 0);
   b = gst_caps_get_structure (caps_b, 0);
 
-  gst_v4l2_src_parse_fixed_struct (a, &aw, &ah, NULL, NULL);
-  gst_v4l2_src_parse_fixed_struct (b, &bw, &bh, NULL, NULL);
+  gst_v4l2_src_parse_fixed_struct (a, &aw, &ah, &a_fps_n, &a_fps_d);
+  gst_v4l2_src_parse_fixed_struct (b, &bw, &bh, &b_fps_n, &b_fps_d);
 
-  /* When both are smaller then pref, just append to the end */
-  if ((bw < pref->width || bh < pref->height)
-      && (aw < pref->width || ah < pref->height)) {
-    ret = 1;
-    goto done;
-  }
-
-  /* If a is smaller then pref and not b, then a goes after b */
-  if (aw < pref->width || ah < pref->height) {
-    ret = 1;
-    goto done;
-  }
-
-  /* If b is smaller then pref and not a, then a goes before b */
-  if (bw < pref->width || bh < pref->height) {
-    ret = -1;
-    goto done;
-  }
-
-  /* Both are larger or equal to the preference, prefer the smallest */
-  ad = MAX (1, aw - pref->width) * MAX (1, ah - pref->height);
-  bd = MAX (1, bw - pref->width) * MAX (1, bh - pref->height);
-
-  /* Adjust slightly in case width/height matched the preference */
-  if (aw == pref->width)
-    ad -= 1;
+  // Sort first the one with closest framerate to preference. Note that any
+  // framerate lower then 1 frame per second will be considered the same. In
+  // practice this should be fine considering that these framerate only exists
+  // for still picture, in which case the resolution is most likely the key.
+  a_distance = ABS ((a_fps_n / a_fps_d) - (pref->fps_n / pref->fps_d));
+  b_distance = ABS ((b_fps_n / b_fps_d) - (pref->fps_n / pref->fps_d));
+  if (a_distance != b_distance)
+    return a_distance - b_distance;
 
-  if (ah == pref->height)
-    ad -= 1;
+  // If same framerate, sort first the one with closest resolution to preference
+  a_distance = ABS (aw * ah - pref->width * pref->height);
+  b_distance = ABS (bw * bh - pref->width * pref->height);
 
-  if (bw == pref->width)
-    bd -= 1;
-
-  if (bh == pref->height)
-    bd -= 1;
-
-  /* If the choices are equivalent, maintain the order */
-  if (ad == bd)
+  /* If the distance are equivalent, maintain the order */
+  if (a_distance == b_distance)
     ret = 1;
   else
-    ret = ad - bd;
+    ret = a_distance - b_distance;
+
+  GST_TRACE ("Placing %" GST_PTR_FORMAT " %s %" GST_PTR_FORMAT,
+      caps_a, ret > 0 ? "after" : "before", caps_b);
 
-done:
-  GST_TRACE ("Placing %ix%i (%s) %s %ix%i (%s)", aw, ah,
-      gst_structure_get_string (a, "format"), ret > 0 ? "after" : "before", bw,
-      bh, gst_structure_get_string (b, "format"));
   return ret;
 }
 
@@ -585,6 +563,14 @@ gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps,
    * enumerate the possibilities */
   caps = gst_caps_normalize (caps);
 
+  /* try hard to avoid TRY_FMT since some UVC camera just crash when this
+   * is called at run-time. */
+  if (gst_v4l2_object_caps_is_subset (obj, caps)) {
+    fcaps = gst_v4l2_object_get_current_caps (obj);
+    GST_DEBUG_OBJECT (basesrc, "reuse current caps %" GST_PTR_FORMAT, fcaps);
+    goto out;
+  }
+
   for (i = 0; i < gst_caps_get_size (caps); ++i) {
     gst_v4l2_clear_error (&error);
     if (fcaps)
@@ -592,14 +578,6 @@ gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps,
 
     fcaps = gst_caps_copy_nth (caps, i);
 
-    /* try hard to avoid TRY_FMT since some UVC camera just crash when this
-     * is called at run-time. */
-    if (gst_v4l2_object_caps_is_subset (obj, fcaps)) {
-      gst_caps_unref (fcaps);
-      fcaps = gst_v4l2_object_get_current_caps (obj);
-      break;
-    }
-
     /* Just check if the format is acceptable, once we know
      * no buffers should be outstanding we try S_FMT.
      *
@@ -633,6 +611,7 @@ gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps,
     return NULL;
   }
 
+out:
   gst_caps_unref (caps);
 
   GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, fcaps);
@@ -708,7 +687,8 @@ gst_v4l2src_query_preferred_size (GstV4l2Src * v4l2src,
   GST_INFO_OBJECT (v4l2src, "Detect input %u as `%s`", in.index, in.name);
 
   /* Notify signal status using WARNING/INFO messages */
-  if (in.status & (V4L2_IN_ST_NO_POWER | V4L2_IN_ST_NO_SIGNAL)) {
+  if (in.status & (V4L2_IN_ST_NO_POWER | V4L2_IN_ST_NO_SIGNAL |
+          V4L2_IN_ST_NO_SYNC)) {
     if (!v4l2src->no_signal)
       /* note: taken from decklinksrc element */
       GST_ELEMENT_WARNING (v4l2src, RESOURCE, READ, ("Signal lost"),
@@ -1148,6 +1128,23 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition)
   return ret;
 }
 
+static gboolean
+gst_v4l2src_handle_resolution_change (GstV4l2Src * v4l2src)
+{
+  GST_INFO_OBJECT (v4l2src, "Resolution change detected.");
+
+  /* It is required to always cycle through streamoff, we also need to
+   * streamoff in order to allow locking a new DV_TIMING which will
+   * influence the output of TRY_FMT */
+  gst_v4l2src_stop (GST_BASE_SRC (v4l2src));
+
+  /* Force renegotiation */
+  v4l2src->renegotiation_adjust = v4l2src->offset + 1;
+  v4l2src->pending_set_fmt = TRUE;
+
+  return gst_base_src_negotiate (GST_BASE_SRC (v4l2src));
+}
+
 static GstFlowReturn
 gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
 {
@@ -1166,18 +1163,7 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
 
     if (G_UNLIKELY (ret != GST_FLOW_OK)) {
       if (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE) {
-        GST_INFO_OBJECT (v4l2src, "Resolution change detected.");
-
-        /* It is required to always cycle through streamoff, we also need to
-         * streamoff in order to allow locking a new DV_TIMING which will
-         * influence the output of TRY_FMT */
-        gst_v4l2src_stop (GST_BASE_SRC (src));
-
-        /* Force renegotiation */
-        v4l2src->renegotiation_adjust = v4l2src->offset + 1;
-        v4l2src->pending_set_fmt = TRUE;
-
-        if (!gst_base_src_negotiate (GST_BASE_SRC (src))) {
+        if (!gst_v4l2src_handle_resolution_change (v4l2src)) {
           ret = GST_FLOW_NOT_NEGOTIATED;
           goto error;
         }
@@ -1193,6 +1179,13 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
       ret = gst_v4l2_buffer_pool_process (obj_pool, buf, NULL);
       if (obj_pool)
         gst_object_unref (obj_pool);
+
+      if (G_UNLIKELY (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE)) {
+        if (!gst_v4l2src_handle_resolution_change (v4l2src)) {
+          ret = GST_FLOW_NOT_NEGOTIATED;
+          goto error;
+        }
+      }
     }
 
   } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER ||
diff --git a/sys/v4l2/gstv4l2transform.c b/sys/v4l2/gstv4l2transform.c
index e48529928315c8b9472550c79fd94a48245a0eca..38805489df1fae8838691e813b02c14f488e8aec 100644
--- a/sys/v4l2/gstv4l2transform.c
+++ b/sys/v4l2/gstv4l2transform.c
@@ -429,7 +429,8 @@ gst_v4l2_transform_fixate_caps (GstBaseTransform * trans,
   GstStructure *ins, *outs;
   const GValue *from_par, *to_par;
   GValue fpar = { 0, }, tpar = {
-  0,};
+    0,
+  };
 
   othercaps = gst_caps_truncate (othercaps);
   othercaps = gst_caps_make_writable (othercaps);
diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c
index 5d9d1e1e890daa0eb9d861234277488522ea5ffa..0b81ac662a25171d9b2a2cbc91ed8a483bcb7704 100644
--- a/sys/v4l2/gstv4l2videodec.c
+++ b/sys/v4l2/gstv4l2videodec.c
@@ -138,6 +138,10 @@ gst_v4l2_video_dec_open (GstVideoDecoder * decoder)
   if (gst_caps_is_empty (self->probed_sinkcaps))
     goto no_encoded_format;
 
+  self->supports_source_change =
+      gst_v4l2_object_subscribe_event (self->v4l2capture,
+      V4L2_EVENT_SOURCE_CHANGE);
+
   return TRUE;
 
 no_encoded_format:
@@ -185,7 +189,6 @@ gst_v4l2_video_dec_start (GstVideoDecoder * decoder)
 
   gst_v4l2_object_unlock (self->v4l2output);
   g_atomic_int_set (&self->active, TRUE);
-  g_atomic_int_set (&self->capture_configuration_change, FALSE);
   self->output_flow = GST_FLOW_OK;
 
   return TRUE;
@@ -256,13 +259,15 @@ static gboolean
 gst_v4l2_video_dec_set_format (GstVideoDecoder * decoder,
     GstVideoCodecState * state)
 {
+  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
   GstV4l2Error error = GST_V4L2_ERROR_INIT;
   gboolean ret = TRUE;
-  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
+  gboolean dyn_resolution = self->v4l2output->fmtdesc &&
+      (self->v4l2output->fmtdesc->flags & V4L2_FMT_FLAG_DYN_RESOLUTION);
 
   GST_DEBUG_OBJECT (self, "Setting format: %" GST_PTR_FORMAT, state->caps);
 
-  if (self->input_state) {
+  if (self->input_state && !dyn_resolution) {
     if (compatible_caps (self, state->caps)) {
       GST_DEBUG_OBJECT (self, "Compatible caps");
       goto done;
@@ -300,14 +305,9 @@ gst_v4l2_video_dec_set_format (GstVideoDecoder * decoder,
     self->output_flow = GST_FLOW_OK;
   }
 
-  ret = gst_v4l2_object_set_format (self->v4l2output, state->caps, &error);
-
-  gst_caps_replace (&self->probed_srccaps, NULL);
-  self->probed_srccaps = gst_v4l2_object_probe_caps (self->v4l2capture,
-      gst_v4l2_object_get_raw_caps ());
-
-  if (gst_caps_is_empty (self->probed_srccaps))
-    goto no_raw_format;
+  /* No V4L2_FMT_FLAG_DYN_RESOLUTION or no fmtdesc set yet */
+  if (!dyn_resolution)
+    ret = gst_v4l2_object_set_format (self->v4l2output, state->caps, &error);
 
   if (ret)
     self->input_state = gst_video_codec_state_ref (state);
@@ -316,12 +316,6 @@ gst_v4l2_video_dec_set_format (GstVideoDecoder * decoder,
 
 done:
   return ret;
-
-no_raw_format:
-  GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
-      (_("Decoder on device %s has no supported output format"),
-          self->v4l2output->videodev), (NULL));
-  return GST_FLOW_ERROR;
 }
 
 static gboolean
@@ -354,8 +348,46 @@ gst_v4l2_video_dec_flush (GstVideoDecoder * decoder)
 
   /* gst_v4l2_buffer_pool_flush() calls streamon the capture pool and must be
    * called after gst_v4l2_object_unlock_stop() stopped flushing the buffer
-   * pool. */
-  gst_v4l2_buffer_pool_flush (self->v4l2capture);
+   * pool. If the resolution has changed before we stopped the driver we must
+   * reallocate the capture pool. We simply discard the pool, and let the
+   * capture thread handle re-allocation.*/
+  if (gst_v4l2_buffer_pool_flush (self->v4l2capture) ==
+      GST_V4L2_FLOW_RESOLUTION_CHANGE || self->draining)
+    gst_v4l2_object_stop (self->v4l2capture);
+
+  return TRUE;
+}
+
+static gboolean
+gst_v4l2_video_remove_padding (GstCapsFeatures * features,
+    GstStructure * structure, gpointer user_data)
+{
+  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (user_data);
+  GstVideoAlignment *align = &self->v4l2capture->align;
+  GstVideoInfo *info = &self->v4l2capture->info;
+  int width, height;
+
+  if (!gst_structure_get_int (structure, "width", &width))
+    return TRUE;
+
+  if (!gst_structure_get_int (structure, "height", &height))
+    return TRUE;
+
+  if (align->padding_left != 0 || align->padding_top != 0 ||
+      height != info->height + align->padding_bottom)
+    return TRUE;
+
+  if (height == info->height + align->padding_bottom) {
+    /* Some drivers may round up width to the padded with */
+    if (width == info->width + align->padding_right)
+      gst_structure_set (structure,
+          "width", G_TYPE_INT, width - align->padding_right,
+          "height", G_TYPE_INT, height - align->padding_bottom, NULL);
+    /* Some drivers may keep visible width and only round up bytesperline */
+    else if (width == info->width)
+      gst_structure_set (structure,
+          "height", G_TYPE_INT, height - align->padding_bottom, NULL);
+  }
 
   return TRUE;
 }
@@ -364,19 +396,135 @@ static gboolean
 gst_v4l2_video_dec_negotiate (GstVideoDecoder * decoder)
 {
   GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
+  GstV4l2Error error = GST_V4L2_ERROR_INIT;
+  GstVideoInfo info;
+  GstVideoCodecState *output_state;
+  GstCaps *acquired_caps, *fixation_caps, *available_caps, *caps, *filter;
+  GstStructure *st;
+  gboolean active;
+  GstBufferPool *cpool;
+  gboolean ret;
 
   /* We don't allow renegotiation without careful disabling the pool */
-  {
-    GstBufferPool *cpool = gst_v4l2_object_get_buffer_pool (self->v4l2capture);
-    if (cpool) {
-      gboolean is_active = gst_buffer_pool_is_active (cpool);
-      gst_object_unref (cpool);
-      if (is_active)
-        return TRUE;
-    }
+  cpool = gst_v4l2_object_get_buffer_pool (self->v4l2capture);
+  if (cpool) {
+    gboolean is_active = gst_buffer_pool_is_active (cpool);
+    gst_object_unref (cpool);
+    if (is_active)
+      return TRUE;
+  }
+
+  /* init capture fps according to output */
+  self->v4l2capture->info.fps_d = self->v4l2output->info.fps_d;
+  self->v4l2capture->info.fps_n = self->v4l2output->info.fps_n;
+
+  /* For decoders G_FMT returns coded size, G_SELECTION returns visible size
+   * in the compose rectangle. gst_v4l2_object_acquire_format() checks both
+   * and returns the visible size as with/height and the coded size as
+   * padding. */
+  if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info))
+    goto not_negotiated;
+
+  /* gst_v4l2_object_acquire_format() does not set fps, copy from sink */
+  info.fps_n = self->v4l2output->info.fps_n;
+  info.fps_d = self->v4l2output->info.fps_d;
+
+  gst_caps_replace (&self->probed_srccaps, NULL);
+  self->probed_srccaps = gst_v4l2_object_probe_caps (self->v4l2capture,
+      gst_v4l2_object_get_raw_caps ());
+  /* Create caps from the acquired format, remove the format field */
+  acquired_caps = gst_video_info_to_caps (&info);
+  GST_DEBUG_OBJECT (self, "Acquired caps: %" GST_PTR_FORMAT, acquired_caps);
+  fixation_caps = gst_caps_copy (acquired_caps);
+  st = gst_caps_get_structure (fixation_caps, 0);
+  gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
+      NULL);
+
+  /* Probe currently available pixel formats */
+  available_caps = gst_caps_copy (self->probed_srccaps);
+  GST_DEBUG_OBJECT (self, "Available caps: %" GST_PTR_FORMAT, available_caps);
+
+  /* Replace coded size with visible size, we want to negotiate visible size
+   * with downstream, not coded size. */
+  gst_caps_map_in_place (available_caps, gst_v4l2_video_remove_padding, self);
+
+  filter = gst_caps_intersect_full (available_caps, fixation_caps,
+      GST_CAPS_INTERSECT_FIRST);
+  GST_DEBUG_OBJECT (self, "Filtered caps: %" GST_PTR_FORMAT, filter);
+  gst_caps_unref (fixation_caps);
+  gst_caps_unref (available_caps);
+  caps = gst_pad_peer_query_caps (decoder->srcpad, filter);
+  gst_caps_unref (filter);
+
+  GST_DEBUG_OBJECT (self, "Possible decoded caps: %" GST_PTR_FORMAT, caps);
+  if (gst_caps_is_empty (caps)) {
+    gst_caps_unref (caps);
+    goto not_negotiated;
   }
 
-  return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
+  /* Prefer the acquired caps over anything suggested downstream, this ensure
+   * that we preserves the bit depth, as we don't have any fancy fixation
+   * process */
+  if (gst_caps_is_subset (acquired_caps, caps))
+    goto use_acquired_caps;
+
+  /* Fixate pixel format */
+  caps = gst_caps_fixate (caps);
+
+  GST_DEBUG_OBJECT (self, "Chosen decoded caps: %" GST_PTR_FORMAT, caps);
+
+  /* Try to set negotiated format, on success replace acquired format */
+  if (gst_v4l2_object_set_format (self->v4l2capture, caps, &error))
+    gst_video_info_from_caps (&info, caps);
+  else
+    gst_v4l2_clear_error (&error);
+
+use_acquired_caps:
+  gst_caps_unref (acquired_caps);
+  gst_caps_unref (caps);
+
+  /* catch possible bogus driver that don't enumerate the format it actually
+   * returned from G_FMT */
+  if (!self->v4l2capture->fmtdesc)
+    goto not_negotiated;
+
+  output_state = gst_video_decoder_set_output_state (decoder,
+      info.finfo->format, info.width, info.height, self->input_state);
+
+  /* Copy the rest of the information, there might be more in the future */
+  output_state->info.interlace_mode = info.interlace_mode;
+  output_state->info.colorimetry = info.colorimetry;
+  gst_video_codec_state_unref (output_state);
+
+  ret = GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
+  if (!ret)
+    goto not_negotiated;
+
+  /* The pool may be created through gst_video_decoder_negotiate(), so must
+   * be kept after */
+  cpool = gst_v4l2_object_get_buffer_pool (self->v4l2capture);
+  gst_v4l2_buffer_pool_enable_resolution_change (GST_V4L2_BUFFER_POOL (cpool));
+
+  /* Ensure our internal pool is activated */
+  active = gst_buffer_pool_set_active (cpool, TRUE);
+  if (cpool)
+    gst_object_unref (cpool);
+  if (!active)
+    goto activate_failed;
+
+  return TRUE;
+
+not_negotiated:
+  GST_ERROR_OBJECT (self, "not negotiated");
+  gst_v4l2_error (self, &error);
+  gst_v4l2_object_stop (self->v4l2capture);
+  return FALSE;
+activate_failed:
+  GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
+      (_("Failed to allocate required memory.")),
+      ("Buffer pool activation failed"));
+  gst_v4l2_object_stop (self->v4l2capture);
+  return FALSE;
 }
 
 static gboolean
@@ -425,6 +573,9 @@ gst_v4l2_video_dec_finish (GstVideoDecoder * decoder)
 
   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
 
+  /* If we are in the middle of a source change, cancel it */
+  self->draining = FALSE;
+
   if (gst_v4l2_decoder_cmd (self->v4l2output, V4L2_DEC_CMD_STOP, 0)) {
     GstTask *task;
 
@@ -472,9 +623,9 @@ gst_v4l2_video_dec_finish (GstVideoDecoder * decoder)
 
   GST_DEBUG_OBJECT (decoder, "Done draining buffers");
 
-  /* Draining of the capture buffer has completed. 
+  /* Draining of the capture buffer has completed.
    * If any pending frames remain at this point there is a decoder error.
-   * This has been observed as a driver bug, where eos is sent too early.   
+   * This has been observed as a driver bug, where eos is sent too early.
    * These frames will never be rendered, so drop them now with a warning */
 
   pending_frames = gst_video_decoder_get_frames (decoder);
@@ -490,9 +641,11 @@ gst_v4l2_video_dec_finish (GstVideoDecoder * decoder)
       counter++;
       gst_video_decoder_drop_frame (decoder, frame);
     }
-    g_warning
-        ("%s: %i frames %u-%u left undrained after CMD_STOP, eos sent too early: bug in decoder -- please file a bug",
-        GST_ELEMENT_NAME (decoder), counter, first, last);
+    if (self->output_flow == GST_FLOW_OK) {
+      g_warning ("%s: %i frames %u-%u left undrained after CMD_STOP, "
+          "eos sent too early: bug in decoder -- please file a bug",
+          GST_ELEMENT_NAME (decoder), counter, first, last);
+    }
     if (pending_frames)
       g_list_free (pending_frames);
   }
@@ -529,155 +682,33 @@ check_system_frame_number_too_old (guint32 current, guint32 old)
   return FALSE;
 }
 
-static gboolean
-gst_v4l2_video_remove_padding (GstCapsFeatures * features,
-    GstStructure * structure, gpointer user_data)
-{
-  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (user_data);
-  GstVideoAlignment *align = &self->v4l2capture->align;
-  GstVideoInfo *info = &self->v4l2capture->info;
-  int width, height;
-
-  if (!gst_structure_get_int (structure, "width", &width))
-    return TRUE;
-
-  if (!gst_structure_get_int (structure, "height", &height))
-    return TRUE;
-
-  if (align->padding_left != 0 || align->padding_top != 0 ||
-      height != info->height + align->padding_bottom)
-    return TRUE;
-
-  if (height == info->height + align->padding_bottom) {
-    /* Some drivers may round up width to the padded with */
-    if (width == info->width + align->padding_right)
-      gst_structure_set (structure,
-          "width", G_TYPE_INT, width - align->padding_right,
-          "height", G_TYPE_INT, height - align->padding_bottom, NULL);
-    /* Some drivers may keep visible width and only round up bytesperline */
-    else if (width == info->width)
-      gst_structure_set (structure,
-          "height", G_TYPE_INT, height - align->padding_bottom, NULL);
-  }
-
-  return TRUE;
-}
-
+/* Only used initially to wait for a SRC_CH event
+ * called with decoder stream lock */
 static GstFlowReturn
-gst_v4l2_video_dec_setup_capture (GstVideoDecoder * decoder)
+gst_v4l2_video_dec_wait_for_src_ch (GstV4l2VideoDec * self)
 {
-  GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
-  GstV4l2Error error = GST_V4L2_ERROR_INIT;
-  GstVideoInfo info;
-  GstVideoCodecState *output_state;
-  GstCaps *acquired_caps, *available_caps, *caps, *filter;
-  GstStructure *st;
-  GstBufferPool *cpool;
-  gboolean active;
-
-  if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) {
-    /* init capture fps according to output */
-    self->v4l2capture->info.fps_d = self->v4l2output->info.fps_d;
-    self->v4l2capture->info.fps_n = self->v4l2output->info.fps_n;
-
-    /* For decoders G_FMT returns coded size, G_SELECTION returns visible size
-     * in the compose rectangle. gst_v4l2_object_acquire_format() checks both
-     * and returns the visible size as with/height and the coded size as
-     * padding. */
-    if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info))
-      goto not_negotiated;
-
-    /* gst_v4l2_object_acquire_format() does not set fps, copy from sink */
-    info.fps_n = self->v4l2output->info.fps_n;
-    info.fps_d = self->v4l2output->info.fps_d;
-
-    gst_v4l2_object_clear_format_list (self->v4l2capture);
-    gst_caps_replace (&self->probed_srccaps, NULL);
-    self->probed_srccaps = gst_v4l2_object_probe_caps (self->v4l2capture,
-        gst_v4l2_object_get_raw_caps ());
-    /* Create caps from the acquired format, remove the format field */
-    acquired_caps = gst_video_info_to_caps (&info);
-    GST_DEBUG_OBJECT (self, "Acquired caps: %" GST_PTR_FORMAT, acquired_caps);
-    st = gst_caps_get_structure (acquired_caps, 0);
-    gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
-        NULL);
-
-    /* Probe currently available pixel formats */
-    available_caps = gst_caps_copy (self->probed_srccaps);
-    GST_DEBUG_OBJECT (self, "Available caps: %" GST_PTR_FORMAT, available_caps);
-
-    /* Replace coded size with visible size, we want to negotiate visible size
-     * with downstream, not coded size. */
-    gst_caps_map_in_place (available_caps, gst_v4l2_video_remove_padding, self);
-
-    filter = gst_caps_intersect_full (available_caps, acquired_caps,
-        GST_CAPS_INTERSECT_FIRST);
-    GST_DEBUG_OBJECT (self, "Filtered caps: %" GST_PTR_FORMAT, filter);
-    gst_caps_unref (acquired_caps);
-    gst_caps_unref (available_caps);
-    caps = gst_pad_peer_query_caps (decoder->srcpad, filter);
-    gst_caps_unref (filter);
-
-    GST_DEBUG_OBJECT (self, "Possible decoded caps: %" GST_PTR_FORMAT, caps);
-    if (gst_caps_is_empty (caps)) {
-      gst_caps_unref (caps);
-      goto not_negotiated;
-    }
-
-    /* Fixate pixel format */
-    caps = gst_caps_fixate (caps);
-
-    GST_DEBUG_OBJECT (self, "Chosen decoded caps: %" GST_PTR_FORMAT, caps);
-
-    /* Try to set negotiated format, on success replace acquired format */
-    if (gst_v4l2_object_set_format (self->v4l2capture, caps, &error))
-      gst_video_info_from_caps (&info, caps);
-    else
-      gst_v4l2_clear_error (&error);
-    gst_caps_unref (caps);
+  GstFlowReturn flowret;
 
-    output_state = gst_video_decoder_set_output_state (decoder,
-        info.finfo->format, info.width, info.height, self->input_state);
+  if (!self->wait_for_source_change)
+    return GST_FLOW_OK;
 
-    /* Copy the rest of the information, there might be more in the future */
-    output_state->info.interlace_mode = info.interlace_mode;
-    output_state->info.colorimetry = info.colorimetry;
-    gst_video_codec_state_unref (output_state);
+  GST_DEBUG_OBJECT (self, "Waiting for source change event");
 
-    cpool = gst_v4l2_object_get_buffer_pool (self->v4l2capture);
-    gst_v4l2_buffer_pool_enable_resolution_change (GST_V4L2_BUFFER_POOL
-        (cpool));
+  GST_VIDEO_DECODER_STREAM_UNLOCK (GST_VIDEO_DECODER (self));
+  flowret = gst_v4l2_object_poll (self->v4l2capture, GST_CLOCK_TIME_NONE);
+  GST_VIDEO_DECODER_STREAM_LOCK (GST_VIDEO_DECODER (self));
 
-    if (!gst_video_decoder_negotiate (decoder)) {
-      if (cpool)
-        gst_object_unref (cpool);
-      if (GST_PAD_IS_FLUSHING (decoder->srcpad))
-        goto flushing;
-      else
-        goto not_negotiated;
-    }
-
-    /* Ensure our internal pool is activated */
-    active = gst_buffer_pool_set_active (cpool, TRUE);
-    if (cpool)
-      gst_object_unref (cpool);
-    if (!active)
-      goto activate_failed;
+  /* Fix the flow return value, as the poll is watching for buffer, but we are
+   * looking for the source change event */
+  if (flowret == GST_V4L2_FLOW_RESOLUTION_CHANGE) {
+    self->wait_for_source_change = FALSE;
+    flowret = GST_FLOW_OK;
+  } else if (flowret == GST_FLOW_OK) {
+    /* A buffer would be unexpected, in this case just terminate */
+    flowret = GST_V4L2_FLOW_LAST_BUFFER;
   }
 
-  return GST_FLOW_OK;
-
-not_negotiated:
-  GST_ERROR_OBJECT (self, "not negotiated");
-  gst_v4l2_error (self, &error);
-  return GST_FLOW_NOT_NEGOTIATED;
-activate_failed:
-  GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
-      (_("Failed to allocate required memory.")),
-      ("Buffer pool activation failed"));
-  return GST_FLOW_ERROR;
-flushing:
-  return GST_FLOW_FLUSHING;
+  return flowret;
 }
 
 static void
@@ -689,30 +720,47 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
   GstBuffer *buffer = NULL;
   GstFlowReturn ret;
 
+  GST_LOG_OBJECT (self, "Looping.");
+
   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
-  if (g_atomic_int_get (&self->capture_configuration_change)) {
-    gst_v4l2_object_stop (self->v4l2capture);
-    ret = gst_v4l2_video_dec_setup_capture (decoder);
+  if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) {
+    ret = gst_v4l2_video_dec_wait_for_src_ch (self);
     if (ret != GST_FLOW_OK) {
+      GST_INFO_OBJECT (decoder, "Polling for source change was interrupted");
       GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
+      goto beach;
+    }
 
-      /* if caps negotiation failed, avoid trying it repeatly */
-      if (ret == GST_FLOW_NOT_NEGOTIATED) {
-        GST_ERROR_OBJECT (decoder,
-            "capture configuration change fail, return negotiation fail");
+    GST_DEBUG_OBJECT (decoder, "Setup the capture queue");
+    if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) {
+      if (!gst_video_decoder_negotiate (decoder)) {
+        /* FIXME not super nice ? */
+        if (GST_PAD_IS_FLUSHING (decoder->sinkpad)
+            || GST_PAD_IS_FLUSHING (decoder->srcpad)) {
+          ret = GST_FLOW_FLUSHING;
+        } else {
+          ret = GST_FLOW_NOT_NEGOTIATED;
+          GST_ERROR_OBJECT (decoder, "Failed to setup capture queue");
+        }
+        GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
         goto beach;
-      } else {
-        return;
       }
+
+      /*
+       * In case we are flushing or stopping the element, ensure the active
+       * state is reflected onto the newly create pool.
+       */
+      if (!g_atomic_int_get (&self->active))
+        gst_v4l2_object_unlock (self->v4l2capture);
     }
-    g_atomic_int_set (&self->capture_configuration_change, FALSE);
+
+    /* just a safety, as introducing mistakes in negotiation seems rather
+     * easy.*/
+    g_return_if_fail (GST_V4L2_IS_ACTIVE (self->v4l2capture));
   }
   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
 
-  if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture)))
-    return;
-
-  GST_LOG_OBJECT (decoder, "Allocate output buffer");
+  GST_LOG_OBJECT (decoder, "Acquire output buffer");
 
   self->output_flow = GST_FLOW_OK;
 
@@ -732,12 +780,6 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
     ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
     g_object_unref (pool);
 
-    if (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE) {
-      GST_INFO_OBJECT (decoder, "Received resolution change");
-      g_atomic_int_set (&self->capture_configuration_change, TRUE);
-      return;
-    }
-
     if (ret != GST_FLOW_OK)
       goto beach;
 
@@ -750,12 +792,6 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
       if (cpool)
         gst_object_unref (cpool);
     }
-
-    if (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE) {
-      GST_INFO_OBJECT (decoder, "Received resolution change");
-      g_atomic_int_set (&self->capture_configuration_change, TRUE);
-      return;
-    }
   } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER);
 
   if (ret != GST_FLOW_OK)
@@ -775,9 +811,17 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
     gboolean warned = FALSE;
 
     /* Garbage collect old frames in case of codec bugs */
-    while ((oldest_frame = gst_video_decoder_get_oldest_frame (decoder)) &&
-        check_system_frame_number_too_old (frame->system_frame_number,
-            oldest_frame->system_frame_number)) {
+    while ((oldest_frame = gst_video_decoder_get_oldest_frame (decoder))) {
+      if (frame->system_frame_number > oldest_frame->system_frame_number &&
+          GST_VIDEO_CODEC_FRAME_IS_DECODE_ONLY (oldest_frame)) {
+        gst_video_decoder_finish_frame (decoder, oldest_frame);
+        oldest_frame = NULL;
+        continue;
+      }
+
+      if (G_LIKELY (!check_system_frame_number_too_old
+              (frame->system_frame_number, oldest_frame->system_frame_number)))
+        break;
       if (oldest_frame->system_frame_number > 0) {
         gst_video_decoder_drop_frame (decoder, oldest_frame);
         oldest_frame = NULL;
@@ -829,6 +873,26 @@ gst_v4l2_video_dec_loop (GstVideoDecoder * decoder)
   return;
 
 beach:
+  if (ret == GST_V4L2_FLOW_RESOLUTION_CHANGE) {
+    GST_VIDEO_DECODER_STREAM_LOCK (decoder);
+    self->draining = TRUE;
+    GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
+    GST_INFO_OBJECT (decoder, "Received resolution change");
+    return;
+  }
+
+  if (ret == GST_V4L2_FLOW_LAST_BUFFER) {
+    GST_VIDEO_DECODER_STREAM_LOCK (decoder);
+    if (self->draining) {
+      self->draining = FALSE;
+      gst_v4l2_object_stop (self->v4l2capture);
+      GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
+      return;
+    }
+
+    GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
+  }
+
   GST_DEBUG_OBJECT (decoder, "Leaving output thread: %s",
       gst_flow_get_name (ret));
 
@@ -844,7 +908,7 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
 {
   GstV4l2Error error = GST_V4L2_ERROR_INIT;
   GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder);
-  GstBufferPool *pool = gst_v4l2_object_get_buffer_pool (self->v4l2output);
+  GstBufferPool *pool = NULL;
   GstFlowReturn ret = GST_FLOW_OK;
   gboolean processed = FALSE;
   GstBuffer *tmp;
@@ -863,14 +927,7 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
       goto not_negotiated;
   }
 
-  if (!g_atomic_int_get (&self->capture_configuration_change)) {
-    ret = gst_v4l2_video_dec_setup_capture (decoder);
-    if (ret != GST_FLOW_OK) {
-      GST_ERROR_OBJECT (decoder, "setup capture fail\n");
-      goto not_negotiated;
-    }
-  }
-
+  pool = gst_v4l2_object_get_buffer_pool (self->v4l2output);
   if (G_UNLIKELY (!gst_buffer_pool_is_active (pool))) {
     GstBuffer *codec_data;
     GstStructure *config = gst_buffer_pool_get_config (pool);
@@ -912,6 +969,11 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
         goto activate_failed;
     }
 
+    /* Ensure to unlock capture, as it may be flushing due to previous
+     * unlock/stop calls */
+    gst_v4l2_object_unlock_stop (self->v4l2output);
+    gst_v4l2_object_unlock_stop (self->v4l2capture);
+
     if (!gst_buffer_pool_set_active (pool, TRUE))
       goto activate_failed;
 
@@ -924,6 +986,13 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
     GST_VIDEO_DECODER_STREAM_LOCK (decoder);
 
     gst_buffer_unref (codec_data);
+
+    /* Only wait for source change if the formats supports it */
+    if (!GST_V4L2_IS_ACTIVE (self->v4l2capture) &&
+        self->v4l2output->fmtdesc->flags & V4L2_FMT_FLAG_DYN_RESOLUTION) {
+      gst_v4l2_object_unlock_stop (self->v4l2capture);
+      self->wait_for_source_change = TRUE;
+    }
   }
 
   task_state = gst_pad_get_task_state (GST_VIDEO_DECODER_SRC_PAD (self));
@@ -941,6 +1010,7 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder,
     /* Start the processing task, when it quits, the task will disable input
      * processing to unlock input if draining, or prevent potential block */
     self->output_flow = GST_FLOW_FLUSHING;
+    self->draining = FALSE;
     if (!gst_pad_start_task (decoder->srcpad,
             (GstTaskFunction) gst_v4l2_video_dec_loop, self, NULL))
       goto start_task_failed;
@@ -1312,12 +1382,13 @@ G_STMT_START { \
     gint mpegversion = 0;
     gst_structure_get_int (s, "mpegversion", &mpegversion);
 
-    if (mpegversion == 2) {
-      SET_META ("MPEG2");
-      cdata->codec = gst_v4l2_mpeg2_get_codec ();
-    } else {
+    if (mpegversion == 4) {
       SET_META ("MPEG4");
       cdata->codec = gst_v4l2_mpeg4_get_codec ();
+    } else {
+      /* MPEG 2 decoders supports MPEG 1 format */
+      SET_META ("MPEG2");
+      cdata->codec = gst_v4l2_mpeg2_get_codec ();
     }
   } else if (gst_structure_has_name (s, "video/x-h263")) {
     SET_META ("H263");
@@ -1390,11 +1461,16 @@ gst_v4l2_video_dec_register (GstPlugin * plugin, const gchar * basename,
     cdata->device = g_strdup (device_path);
     cdata->sink_caps = gst_caps_new_empty ();
     gst_caps_append_structure (cdata->sink_caps, gst_structure_copy (s));
+    GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
+        GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
     cdata->src_caps = gst_caps_ref (src_caps);
     type_name = gst_v4l2_video_dec_set_metadata (s, cdata, basename);
 
     /* Skip over if we hit an unmapped type */
     if (!type_name) {
+      g_free (cdata->device);
+      gst_caps_unref (cdata->sink_caps);
+      gst_caps_unref (cdata->src_caps);
       g_free (cdata);
       continue;
     }
diff --git a/sys/v4l2/gstv4l2videodec.h b/sys/v4l2/gstv4l2videodec.h
index 5af08ac9354974b5f94b1dd29139803ea7667f00..fbbfb60b01d86e4825d4854d63932ca74e20541f 100644
--- a/sys/v4l2/gstv4l2videodec.h
+++ b/sys/v4l2/gstv4l2videodec.h
@@ -63,8 +63,12 @@ struct _GstV4l2VideoDec
   gboolean active;
   GstFlowReturn output_flow;
 
-  /* dynamic resolution change flag */
-  gboolean capture_configuration_change;
+  /* Source Change Events */
+  gboolean wait_for_source_change;
+  gboolean draining;
+
+  /* Capabilities */
+  gboolean supports_source_change;
 };
 
 struct _GstV4l2VideoDecClass
diff --git a/sys/v4l2/gstv4l2videoenc.c b/sys/v4l2/gstv4l2videoenc.c
index 1adb72054d147f72c1f0e81df79555984e993f38..b4317cfab245f7a68df9830c33bde0ef6b02e706 100644
--- a/sys/v4l2/gstv4l2videoenc.c
+++ b/sys/v4l2/gstv4l2videoenc.c
@@ -111,7 +111,7 @@ gst_v4l2_video_enc_open (GstVideoEncoder * encoder)
 {
   GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
   GstV4l2Error error = GST_V4L2_ERROR_INIT;
-  GstCaps *codec_caps;
+  GstCaps *tmp_caps, *codec_caps;
 
   GST_DEBUG_OBJECT (self, "Opening");
 
@@ -124,6 +124,15 @@ gst_v4l2_video_enc_open (GstVideoEncoder * encoder)
   self->probed_sinkcaps = gst_v4l2_object_probe_caps (self->v4l2output,
       gst_v4l2_object_get_raw_caps ());
 
+  /*
+   * Relax the framerate to always allow 0/1 regardless of what the driver
+   * wants. This improve compatibility of the encoder with sources which may
+   * not know the frame rate
+   */
+  tmp_caps = gst_caps_copy (self->probed_sinkcaps);
+  gst_caps_set_simple (tmp_caps, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
+  gst_caps_append (self->probed_sinkcaps, tmp_caps);
+
   if (gst_caps_is_empty (self->probed_sinkcaps))
     goto no_raw_format;
 
@@ -301,6 +310,34 @@ done:
   return ret;
 }
 
+static gboolean
+gst_v4l2_video_enc_flush (GstVideoEncoder * encoder)
+{
+  GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
+
+  GST_DEBUG_OBJECT (self, "Flushing");
+
+  /* Ensure the processing thread has stopped for the reverse playback
+   * iscount case */
+  if (g_atomic_int_get (&self->processing)) {
+    GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
+
+    gst_v4l2_object_unlock_stop (self->v4l2output);
+    gst_v4l2_object_unlock_stop (self->v4l2capture);
+    gst_pad_stop_task (encoder->srcpad);
+
+    GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
+
+  }
+
+  self->output_flow = GST_FLOW_OK;
+
+  gst_v4l2_object_unlock_stop (self->v4l2output);
+  gst_v4l2_object_unlock_stop (self->v4l2capture);
+
+  return TRUE;
+}
+
 static gboolean
 gst_v4l2_video_enc_set_format (GstVideoEncoder * encoder,
     GstVideoCodecState * state)
@@ -319,8 +356,8 @@ gst_v4l2_video_enc_set_format (GstVideoEncoder * encoder,
       return TRUE;
     }
 
-    if (gst_v4l2_video_enc_finish (encoder) != GST_FLOW_OK)
-      return FALSE;
+    gst_v4l2_video_enc_finish (encoder);
+    gst_v4l2_video_enc_flush (encoder);
 
     gst_v4l2_object_stop (self->v4l2output);
     gst_v4l2_object_stop (self->v4l2capture);
@@ -352,34 +389,6 @@ gst_v4l2_video_enc_set_format (GstVideoEncoder * encoder,
   return ret;
 }
 
-static gboolean
-gst_v4l2_video_enc_flush (GstVideoEncoder * encoder)
-{
-  GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
-
-  GST_DEBUG_OBJECT (self, "Flushing");
-
-  /* Ensure the processing thread has stopped for the reverse playback
-   * iscount case */
-  if (g_atomic_int_get (&self->processing)) {
-    GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
-
-    gst_v4l2_object_unlock_stop (self->v4l2output);
-    gst_v4l2_object_unlock_stop (self->v4l2capture);
-    gst_pad_stop_task (encoder->srcpad);
-
-    GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
-
-  }
-
-  self->output_flow = GST_FLOW_OK;
-
-  gst_v4l2_object_unlock_stop (self->v4l2output);
-  gst_v4l2_object_unlock_stop (self->v4l2capture);
-
-  return TRUE;
-}
-
 struct ProfileLevelCtx
 {
   GstV4l2VideoEnc *self;
@@ -517,6 +526,26 @@ negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s,
   return failed;
 }
 
+static GstCaps *
+gst_v4l2_video_enc_sink_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
+{
+  GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
+  GstCaps *probed_caps = NULL;
+  GstCaps *caps;
+
+  if (self->probed_sinkcaps)
+    probed_caps = gst_caps_ref (self->probed_sinkcaps);
+
+  caps = gst_video_encoder_proxy_getcaps (encoder, probed_caps, filter);
+
+  if (probed_caps)
+    gst_caps_unref (probed_caps);
+
+  GST_DEBUG_OBJECT (self, "Returning sink caps %" GST_PTR_FORMAT, caps);
+
+  return caps;
+}
+
 static gboolean
 gst_v4l2_video_enc_negotiate (GstVideoEncoder * encoder)
 {
@@ -635,31 +664,27 @@ static void
 gst_v4l2_video_enc_loop (GstVideoEncoder * encoder)
 {
   GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
+  GstBufferPool *pool = gst_v4l2_object_get_buffer_pool (self->v4l2capture);
+  GstV4l2BufferPool *cpool = GST_V4L2_BUFFER_POOL (pool);
   GstVideoCodecFrame *frame;
   GstBuffer *buffer = NULL;
   GstFlowReturn ret;
 
   GST_LOG_OBJECT (encoder, "Allocate output buffer");
 
-  buffer = gst_video_encoder_allocate_output_buffer (encoder,
-      self->v4l2capture->info.size);
-
-  if (NULL == buffer) {
-    ret = GST_FLOW_FLUSHING;
+  ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
+  if (ret != GST_FLOW_OK) {
+    if (cpool)
+      gst_object_unref (cpool);
     goto beach;
   }
 
   /* FIXME Check if buffer isn't the last one here */
 
   GST_LOG_OBJECT (encoder, "Process output buffer");
-  {
-    GstV4l2BufferPool *cpool =
-        GST_V4L2_BUFFER_POOL (gst_v4l2_object_get_buffer_pool
-        (self->v4l2capture));
-    ret = gst_v4l2_buffer_pool_process (cpool, &buffer, NULL);
-    if (cpool)
-      gst_object_unref (cpool);
-  }
+  ret = gst_v4l2_buffer_pool_process (cpool, &buffer, NULL);
+  if (cpool)
+    gst_object_unref (cpool);
   if (ret != GST_FLOW_OK)
     goto beach;
 
@@ -754,8 +779,6 @@ gst_v4l2_video_enc_handle_frame (GstVideoEncoder * encoder,
 
   task_state = gst_pad_get_task_state (GST_VIDEO_ENCODER_SRC_PAD (self));
   if (task_state == GST_TASK_STOPPED || task_state == GST_TASK_PAUSED) {
-    GstBufferPool *pool = gst_v4l2_object_get_buffer_pool (self->v4l2output);
-
     /* It is possible that the processing thread stopped due to an error or
      * when the last buffer has been met during the draining process. */
     if (self->output_flow != GST_FLOW_OK &&
@@ -764,14 +787,15 @@ gst_v4l2_video_enc_handle_frame (GstVideoEncoder * encoder,
       GST_DEBUG_OBJECT (self, "Processing loop stopped with error: %s, leaving",
           gst_flow_get_name (self->output_flow));
       ret = self->output_flow;
-      if (pool)
-        gst_object_unref (pool);
       goto drop;
     }
+  }
 
-    /* Ensure input internal pool is active */
-    if (!gst_buffer_pool_is_active (pool)) {
-      GstStructure *config = gst_buffer_pool_get_config (pool);
+  {
+    /* Ensure input internal output pool is active */
+    GstBufferPool *opool = gst_v4l2_object_get_buffer_pool (self->v4l2output);
+    if (!gst_buffer_pool_is_active (opool)) {
+      GstStructure *config = gst_buffer_pool_get_config (opool);
       guint min = MAX (self->v4l2output->min_buffers,
           GST_V4L2_MIN_BUFFERS (self->v4l2output));
 
@@ -779,21 +803,36 @@ gst_v4l2_video_enc_handle_frame (GstVideoEncoder * encoder,
           self->v4l2output->info.size, min, min);
 
       /* There is no reason to refuse this config */
-      if (!gst_buffer_pool_set_config (pool, config)) {
-        if (pool)
-          gst_object_unref (pool);
-        goto activate_failed;
+      if (!gst_buffer_pool_set_config (opool, config)) {
+        config = gst_buffer_pool_get_config (opool);
+
+        if (gst_buffer_pool_config_validate_params (config,
+                self->input_state->caps, self->v4l2output->info.size, min,
+                min)) {
+          gst_structure_free (config);
+          if (opool)
+            gst_object_unref (opool);
+          goto activate_failed;
+        }
+
+        if (!gst_buffer_pool_set_config (opool, config)) {
+          if (opool)
+            gst_object_unref (opool);
+          goto activate_failed;
+        }
       }
 
-      if (!gst_buffer_pool_set_active (pool, TRUE)) {
-        if (pool)
-          gst_object_unref (pool);
+      if (!gst_buffer_pool_set_active (opool, TRUE)) {
+        if (opool)
+          gst_object_unref (opool);
         goto activate_failed;
       }
-      if (pool)
-        gst_object_unref (pool);
     }
+    if (opool)
+      gst_object_unref (opool);
+  }
 
+  if (task_state == GST_TASK_STOPPED || task_state == GST_TASK_PAUSED) {
     {
       GstBufferPool *cpool =
           gst_v4l2_object_get_buffer_pool (self->v4l2capture);
@@ -817,6 +856,17 @@ gst_v4l2_video_enc_handle_frame (GstVideoEncoder * encoder,
   }
 
   if (frame->input_buffer) {
+    /* Process force keyframe event if it was passed */
+    if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
+      struct v4l2_control ctrl = { V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 1 };
+      if (self->v4l2output->ioctl (self->v4l2output->video_fd, VIDIOC_S_CTRL,
+              &ctrl) < 0)
+        GST_ELEMENT_WARNING (self, RESOURCE, FAILED,
+            (_("Failed to force keyframe.")),
+            ("VIDIOC_S_CTRL (V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME) failed: %s (%d)",
+                g_strerror (errno), errno));
+    }
+
     GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
     GST_LOG_OBJECT (encoder, "Passing buffer with frame number %u",
         frame->system_frame_number);
@@ -994,46 +1044,6 @@ gst_v4l2_video_enc_src_query (GstVideoEncoder * encoder, GstQuery * query)
   return ret;
 }
 
-static gboolean
-gst_v4l2_video_enc_sink_query (GstVideoEncoder * encoder, GstQuery * query)
-{
-  gboolean ret = TRUE;
-  GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
-
-  switch (GST_QUERY_TYPE (query)) {
-    case GST_QUERY_CAPS:{
-      GstCaps *filter, *result = NULL;
-      GstPad *pad = GST_VIDEO_ENCODER_SINK_PAD (encoder);
-
-      gst_query_parse_caps (query, &filter);
-
-      if (self->probed_sinkcaps)
-        result = gst_caps_ref (self->probed_sinkcaps);
-      else
-        result = gst_pad_get_pad_template_caps (pad);
-
-      if (filter) {
-        GstCaps *tmp = result;
-        result =
-            gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
-        gst_caps_unref (tmp);
-      }
-
-      GST_DEBUG_OBJECT (self, "Returning sink caps %" GST_PTR_FORMAT, result);
-
-      gst_query_set_caps_result (query, result);
-      gst_caps_unref (result);
-      break;
-    }
-
-    default:
-      ret = GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (encoder, query);
-      break;
-  }
-
-  return ret;
-}
-
 static gboolean
 gst_v4l2_video_enc_sink_event (GstVideoEncoder * encoder, GstEvent * event)
 {
@@ -1159,14 +1169,14 @@ gst_v4l2_video_enc_class_init (GstV4l2VideoEncClass * klass)
   video_encoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_flush);
   video_encoder_class->set_format =
       GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_set_format);
+  video_encoder_class->getcaps =
+      GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_sink_getcaps);
   video_encoder_class->negotiate =
       GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_negotiate);
   video_encoder_class->decide_allocation =
       GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_decide_allocation);
   video_encoder_class->propose_allocation =
       GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_propose_allocation);
-  video_encoder_class->sink_query =
-      GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_sink_query);
   video_encoder_class->src_query =
       GST_DEBUG_FUNCPTR (gst_v4l2_video_enc_src_query);
   video_encoder_class->sink_event =
@@ -1239,6 +1249,7 @@ gst_v4l2_video_enc_register (GstPlugin * plugin, GType type,
   GValue value = G_VALUE_INIT;
 
   filtered_caps = gst_caps_intersect (src_caps, codec_caps);
+  GST_MINI_OBJECT_FLAG_SET (filtered_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
 
   if (codec != NULL && video_fd != -1) {
     if (gst_v4l2_codec_probe_levels (codec, video_fd, &value)) {
@@ -1255,7 +1266,7 @@ gst_v4l2_video_enc_register (GstPlugin * plugin, GType type,
   cdata = g_new0 (GstV4l2VideoEncCData, 1);
   cdata->device = g_strdup (device_path);
   cdata->sink_caps = gst_caps_ref (sink_caps);
-  cdata->src_caps = gst_caps_ref (filtered_caps);
+  cdata->src_caps = filtered_caps;
   cdata->codec = codec;
 
   g_type_query (type, &type_query);
diff --git a/sys/v4l2/meson.build b/sys/v4l2/meson.build
index 6ece251b5ece63496068aed3a1805e5ddac328da..026d3920fce55a5e8576c44b0712437dff01331a 100644
--- a/sys/v4l2/meson.build
+++ b/sys/v4l2/meson.build
@@ -52,7 +52,7 @@ cdata.set('GST_V4L2_ENABLE_PROBE', get_option('v4l2-probe'))
 if have_v4l2
   message('building v4l2 plugin')
   cdata.set('HAVE_GST_V4L2', true)
-  gudev_dep = dependency('gudev-1.0', version : '>=147', required : get_option('v4l2-gudev'))
+  gudev_dep = dependency('gudev-1.0', version : '>=147', required : get_option('v4l2-gudev'), allow_fallback: true)
   cdata.set('HAVE_GUDEV', gudev_dep.found())
 
   # libv4l2 is only needed for converting some obscure formats
diff --git a/sys/v4l2/v4l2-utils.c b/sys/v4l2/v4l2-utils.c
index 260c5df1d86303c39d6471b5ad2181c384fc3e27..76b8e25a75470dedf9baa1e7e195b57385f602d2 100644
--- a/sys/v4l2/v4l2-utils.c
+++ b/sys/v4l2/v4l2-utils.c
@@ -46,7 +46,7 @@ gst_v4l2_iterator_new (void)
   static const gchar *subsystems[] = { "video4linux", NULL };
   struct _GstV4l2GUdevIterator *it;
 
-  it = g_slice_new0 (struct _GstV4l2GUdevIterator);
+  it = g_new0 (struct _GstV4l2GUdevIterator, 1);
 
   it->client = g_udev_client_new (subsystems);
   it->devices = g_udev_client_query_by_subsystem (it->client, "video4linux");
@@ -92,7 +92,7 @@ gst_v4l2_iterator_free (GstV4l2Iterator * _it)
   struct _GstV4l2GUdevIterator *it = (struct _GstV4l2GUdevIterator *) _it;
   g_list_free_full (it->devices, g_object_unref);
   gst_object_unref (it->client);
-  g_slice_free (struct _GstV4l2GUdevIterator, it);
+  g_free (it);
 }
 
 #else /* No GUDEV */
@@ -110,7 +110,7 @@ gst_v4l2_iterator_new (void)
 {
   struct _GstV4l2FsIterator *it;
 
-  it = g_slice_new0 (struct _GstV4l2FsIterator);
+  it = g_new0 (struct _GstV4l2FsIterator, 1);
   it->base_idx = 0;
   it->video_idx = -1;
   it->device = NULL;
@@ -160,7 +160,7 @@ gst_v4l2_iterator_free (GstV4l2Iterator * _it)
 {
   struct _GstV4l2FsIterator *it = (struct _GstV4l2FsIterator *) _it;
   g_free ((gchar *) it->parent.device_path);
-  g_slice_free (struct _GstV4l2FsIterator, it);
+  g_free (it);
 }
 
 #endif
diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c
index cb37ea781b96315922dd4b3e1c5c04c756a39855..b2165bc13bef8f2031bae8c62a0eb590ffef2d2c 100644
--- a/sys/v4l2/v4l2_calls.c
+++ b/sys/v4l2/v4l2_calls.c
@@ -177,6 +177,7 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
 
     if (input.type == V4L2_INPUT_TYPE_TUNER) {
       struct v4l2_tuner vtun;
+      memset (&vtun, 0, sizeof (vtun));
 
       v4l2channel->tuner = input.tuner;
       channel->flags |= GST_TUNER_CHANNEL_FREQUENCY;
@@ -307,6 +308,10 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object)
       } else {
         GST_WARNING_OBJECT (e, "Failed querying control %d on device '%s'. "
             "(%d - %s)", n, v4l2object->videodev, errno, strerror (errno));
+        if (n > (V4L2_CID_PRIVATE_BASE + V4L2_CID_MAX_CTRLS)) {
+          GST_DEBUG_OBJECT (e, "Finish control by reaching V4L2_CID_MAX_CTRLS");
+          break;
+        }
         continue;
       }
     }
@@ -1143,7 +1148,7 @@ input_failed:
 }
 
 gboolean
-gst_v4l2_query_input (GstV4l2Object * obj, struct v4l2_input * input)
+gst_v4l2_query_input (GstV4l2Object * obj, struct v4l2_input *input)
 {
   gint ret;
 
@@ -1274,7 +1279,7 @@ failed:
 }
 
 gboolean
-gst_v4l2_dequeue_event (GstV4l2Object * v4l2object, struct v4l2_event * event)
+gst_v4l2_dequeue_event (GstV4l2Object * v4l2object, struct v4l2_event *event)
 {
   gint ret;
 
@@ -1297,7 +1302,7 @@ gst_v4l2_dequeue_event (GstV4l2Object * v4l2object, struct v4l2_event * event)
 
 gboolean
 gst_v4l2_set_dv_timings (GstV4l2Object * v4l2object,
-    struct v4l2_dv_timings * timings)
+    struct v4l2_dv_timings *timings)
 {
   gint ret;
 
@@ -1317,7 +1322,7 @@ gst_v4l2_set_dv_timings (GstV4l2Object * v4l2object,
 
 gboolean
 gst_v4l2_query_dv_timings (GstV4l2Object * v4l2object,
-    struct v4l2_dv_timings * timings)
+    struct v4l2_dv_timings *timings)
 {
   gint ret;
 
diff --git a/sys/ximage/gst-ximage-navigation.c b/sys/ximage/gst-ximage-navigation.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d2fea1e4bf3b58e6cdc76f5370ddfbaa4d1e0b5
--- /dev/null
+++ b/sys/ximage/gst-ximage-navigation.c
@@ -0,0 +1,67 @@
+/* GStreamer
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "gst-ximage-navigation.h"
+#include <X11/extensions/XTest.h>
+
+// Based on xtestlib: https://www.x.org/releases/X11R7.5/doc/Xext/xtestlib.html
+
+void
+gst_ximage_navigation_mouse_move_pointer (Display * display, int x, int y)
+{
+  // If screen_number is -1, the current screen (that the pointer is on) is used
+  XTestFakeMotionEvent (display, -1, x, y, CurrentTime);
+  XSync (display, FALSE);
+  return;
+}
+
+void
+gst_ximage_navigation_mouse_push_button (Display * display,
+    unsigned int button, Bool is_press)
+{
+  /*
+     button values:
+     1 = left button
+     2 = middle button (pressing the scroll wheel)
+     3 = right button
+     4 = turn scroll wheel up
+     5 = turn scroll wheel down
+     6 = push scroll wheel left
+     7 = push scroll wheel right
+     8 = 4th button (aka browser backward button)
+     9 = 5th button (aka browser forward button)
+   */
+  XTestFakeButtonEvent (display, button, is_press, CurrentTime);
+  XSync (display, FALSE);
+  return;
+}
+
+void
+gst_ximage_navigation_key (Display * display, const char *keysym_name,
+    Bool is_press)
+{
+  // keysym_name: one of X11 keysym names defined in https://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt
+  unsigned int keysym, keycode;
+  keysym = (unsigned int) XStringToKeysym (keysym_name);
+  keycode = XKeysymToKeycode (display, keysym);
+  if (keycode == 0)             // undefined KeySym
+    return;
+  XTestFakeKeyEvent (display, keycode, is_press, CurrentTime);
+  XSync (display, FALSE);
+  return;
+}
diff --git a/sys/ximage/gst-ximage-navigation.h b/sys/ximage/gst-ximage-navigation.h
new file mode 100644
index 0000000000000000000000000000000000000000..842360ec50787aeb833280087ebd9756f0d15c7c
--- /dev/null
+++ b/sys/ximage/gst-ximage-navigation.h
@@ -0,0 +1,28 @@
+/* GStreamer
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_XIMAGE_NAVIGATION_H__
+#define __GST_XIMAGE_NAVIGATION_H__
+
+#include <ximageutil.h>
+
+void gst_ximage_navigation_mouse_move_pointer(Display * display, int x, int y);
+void gst_ximage_navigation_mouse_push_button(Display * display, unsigned int button, Bool is_press);
+void gst_ximage_navigation_key(Display * display, const char * keysym_name, Bool is_press);
+
+#endif
diff --git a/sys/ximage/gstximagesrc.c b/sys/ximage/gstximagesrc.c
index 3e379e18aee5aa1f834e2c26da4c45d3fc10e645..2f7bd5264112b8d06d4063c4a228e7164e148150 100644
--- a/sys/ximage/gstximagesrc.c
+++ b/sys/ximage/gstximagesrc.c
@@ -22,11 +22,18 @@
  * SECTION:element-ximagesrc
  * @title: ximagesrc
  *
- * This element captures your X Display and creates raw RGB video.  It uses
+ * This element captures your X Display and creates raw RGB video. It uses
  * the XDamage extension if available to only capture areas of the screen that
- * have changed since the last frame.  It uses the XFixes extension if
- * available to also capture your mouse pointer.  By default it will fixate to
- * 25 frames per second.
+ * have changed since the last frame. It uses the XFixes extension if
+ * available to also capture your mouse pointer. It supports handling of
+ * mouse and keyboard events. By default it will fixate to 25 frames per second.
+ * 
+ * Applications are expected to call `XinitThreads()` before any other threads
+ * are started. For use in gst-launch-1.0 or other GStreamer command line 
+ * applications it is also possible to set the GST_XINITTHREADS=1 environment 
+ * variable so that `XInitThreads()` gets called when the plugin is loaded. This 
+ * may be too late in other use case scenarios though, so applications should 
+ * not rely on that.
  *
  * ## Example pipelines
  * |[
@@ -39,6 +46,7 @@
 #include "config.h"
 #endif
 #include "gstximagesrc.h"
+#include "gst-ximage-navigation.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -55,6 +63,14 @@
 GST_DEBUG_CATEGORY_STATIC (gst_debug_ximage_src);
 #define GST_CAT_DEFAULT gst_debug_ximage_src
 
+#define MOUSE_SCROLL_UP_BUTTON 4
+#define MOUSE_SCROLL_DOWN_BUTTON 5
+
+#define KEYCODE_CTRL 0x25
+#define KEYCODE_SHIFT 0x32
+#define KEYCODE_ALT 0x40
+#define KEYCODE_META 0x85
+
 static GstStaticPadTemplate t =
 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/x-raw, "
@@ -75,6 +91,7 @@ enum
   PROP_REMOTE,
   PROP_XID,
   PROP_XNAME,
+  PROP_ENABLE_NAVIGATION_EVENTS
 };
 
 #define gst_ximage_src_parent_class parent_class
@@ -943,7 +960,6 @@ gst_ximage_src_set_property (GObject * object, guint prop_id,
 
   switch (prop_id) {
     case PROP_DISPLAY_NAME:
-
       g_free (src->display_name);
       src->display_name = g_value_dup_string (value);
       break;
@@ -987,6 +1003,9 @@ gst_ximage_src_set_property (GObject * object, guint prop_id,
       g_free (src->xname);
       src->xname = g_value_dup_string (value);
       break;
+    case PROP_ENABLE_NAVIGATION_EVENTS:
+      src->enable_navigation_events = g_value_get_boolean (value);
+      break;
     default:
       break;
   }
@@ -1004,7 +1023,6 @@ gst_ximage_src_get_property (GObject * object, guint prop_id,
         g_value_set_string (value, DisplayString (src->xcontext->disp));
       else
         g_value_set_string (value, src->display_name);
-
       break;
     case PROP_SHOW_POINTER:
       g_value_set_boolean (value, src->show_pointer);
@@ -1033,6 +1051,9 @@ gst_ximage_src_get_property (GObject * object, guint prop_id,
     case PROP_XNAME:
       g_value_set_string (value, src->xname);
       break;
+    case PROP_ENABLE_NAVIGATION_EVENTS:
+      g_value_set_boolean (value, src->enable_navigation_events);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1195,6 +1216,72 @@ gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
   return caps;
 }
 
+static gboolean
+gst_ximage_src_event (GstBaseSrc * base_src, GstEvent * event)
+{
+  gboolean ret = FALSE;
+#ifdef HAVE_XNAVIGATION
+  gboolean is_press = FALSE;
+  GstXImageSrc *src = GST_XIMAGE_SRC (base_src);
+  if (src->enable_navigation_events
+      && GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION) {
+    const gchar *key;
+    gint button;
+    gdouble x, y, delta_x, delta_y;
+
+    GST_DEBUG_OBJECT (src, "Processing event %" GST_PTR_FORMAT, event);
+    switch (gst_navigation_event_get_type (event)) {
+      case GST_NAVIGATION_EVENT_KEY_PRESS:
+        is_press = TRUE;        /* FALLTHROUGH */
+      case GST_NAVIGATION_EVENT_KEY_RELEASE:
+        if (gst_navigation_event_parse_key_event (event, &key)) {
+          gst_ximage_navigation_key (src->xcontext->disp, key, is_press);
+          ret = TRUE;
+        }
+        break;
+      case GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS:
+        is_press = TRUE;        /* FALLTHROUGH */
+      case GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE:
+        if (gst_navigation_event_parse_mouse_button_event (event, &button, &x,
+                &y)) {
+          gst_ximage_navigation_mouse_push_button (src->xcontext->disp, button,
+              is_press);
+          ret = TRUE;
+        }
+        break;
+      case GST_NAVIGATION_EVENT_MOUSE_MOVE:
+        if (gst_navigation_event_parse_mouse_move_event (event, &x, &y)) {
+          gst_ximage_navigation_mouse_move_pointer (src->xcontext->disp,
+              (int) x, (int) y);
+          ret = TRUE;
+        }
+        break;
+      case GST_NAVIGATION_EVENT_MOUSE_SCROLL:
+        if (gst_navigation_event_parse_mouse_scroll_event (event, &x, &y,
+                &delta_x, &delta_y)) {
+          int scroll_button =
+              (int) delta_y <
+              0 ? MOUSE_SCROLL_DOWN_BUTTON : MOUSE_SCROLL_UP_BUTTON;
+          gst_ximage_navigation_mouse_push_button (src->xcontext->disp,
+              scroll_button, TRUE);
+          gst_ximage_navigation_mouse_push_button (src->xcontext->disp,
+              scroll_button, FALSE);
+          ret = TRUE;
+        }
+        break;
+      default:
+        break;
+    }
+  }
+#endif
+  if (!ret) {
+    ret =
+        GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, event, (base_src,
+            event), FALSE);
+  }
+  return ret;
+}
+
 static void
 gst_ximage_src_class_init (GstXImageSrcClass * klass)
 {
@@ -1294,6 +1381,17 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass)
           "Window name to capture from", NULL,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstXImageSrc:enable-navigation-events:
+   *
+   * Enable navigation events
+   */
+  g_object_class_install_property (gc, PROP_ENABLE_NAVIGATION_EVENTS,
+      g_param_spec_boolean ("enable-navigation-events",
+          "Enable navigation events",
+          "When enabled, navigation events are handled", FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_set_static_metadata (ec, "Ximage video source",
       "Source/Video",
       "Creates a screenshot video stream",
@@ -1309,6 +1407,7 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass)
   bc->stop = gst_ximage_src_stop;
   bc->unlock = gst_ximage_src_unlock;
   push_class->create = gst_ximage_src_create;
+  bc->event = gst_ximage_src_event;
 }
 
 static void
@@ -1328,6 +1427,7 @@ gst_ximage_src_init (GstXImageSrc * ximagesrc)
   ximagesrc->endx_fit_to_screen = TRUE;
   ximagesrc->endy_fit_to_screen = TRUE;
   ximagesrc->remote = FALSE;
+  ximagesrc->enable_navigation_events = FALSE;
 }
 
 static gboolean
@@ -1335,6 +1435,9 @@ plugin_init (GstPlugin * plugin)
 {
   gboolean ret;
 
+  if (g_getenv ("GST_XINITTHREADS"))
+    XInitThreads ();
+
   GST_DEBUG_CATEGORY_INIT (gst_debug_ximage_src, "ximagesrc", 0,
       "ximagesrc element debug");
 
diff --git a/sys/ximage/gstximagesrc.h b/sys/ximage/gstximagesrc.h
index 09b88f46803c2213136a272550017a7703bb450f..20048217be2c61816830a58d43e3f927fac27d10 100644
--- a/sys/ximage/gstximagesrc.h
+++ b/sys/ximage/gstximagesrc.h
@@ -97,6 +97,9 @@ struct _GstXImageSrc
   /* whether to use remote friendly calls */
   gboolean remote;
 
+  /* enable navigation events */
+  gboolean enable_navigation_events;
+
 #ifdef HAVE_XFIXES
   int fixes_event_base;
   XFixesCursorImage *cursor_image;
diff --git a/sys/ximage/meson.build b/sys/ximage/meson.build
index 68cc263dd792277ef6dda445508b7e5f0038db14..a7021f109c3b478707927376825fb0bfdae4a1af 100644
--- a/sys/ximage/meson.build
+++ b/sys/ximage/meson.build
@@ -2,6 +2,7 @@ x11_dep = dependency('x11', required : get_option('ximagesrc'))
 
 if x11_dep.found()
   x_args = []
+  x_sources = files(['gstximagesrc.c', 'ximageutil.c'])
   xshm_dep = dependency('xext', required : get_option('ximagesrc-xshm'))
   # FIXME: should add a 'required' arg to cc.has_function() in Meson and use it here
   if xshm_dep.found() and cc.has_function('XShmAttach', dependencies: xshm_dep)
@@ -18,12 +19,19 @@ if x11_dep.found()
     x_args += ['-DHAVE_XDAMAGE']
   endif
 
+  xtst_dep = dependency('xtst', required : get_option('ximagesrc-navigation'))
+  if xtst_dep.found()
+    x_args += ['-DHAVE_XNAVIGATION']
+    x_sources += files(['gst-ximage-navigation.c'])
+  endif
+
   gstximagesrc = library('gstximagesrc',
-    'gstximagesrc.c', 'ximageutil.c',
+    x_sources,
     c_args : gst_plugins_good_args + x_args,
     include_directories : [configinc, libsinc],
     dependencies : [gstbase_dep, gstvideo_dep, x11_dep,
-                    xshm_dep, xfixes_dep, xdamage_dep],
+                    xshm_dep, xfixes_dep, xdamage_dep,
+                    xtst_dep],
     install : true,
     install_dir : plugins_install_dir,
   )
diff --git a/tests/check/elements/amrnbenc.c b/tests/check/elements/amrnbenc.c
new file mode 100644
index 0000000000000000000000000000000000000000..7dadf16e015ce450f05134839729887c3444e038
--- /dev/null
+++ b/tests/check/elements/amrnbenc.c
@@ -0,0 +1,144 @@
+/*
+ * GStreamer
+ *
+ * unit test for amrnbenc
+ *
+ * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+#define SRC_CAPS "audio/x-raw, format = (string)" GST_AUDIO_NE (S16) ", " \
+    "layout = (string) interleaved, channels = (int) 1, rate = (int) 8000"
+#define SINK_CAPS "audio/AMR"
+
+static GstPad *srcpad, *sinkpad;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (SINK_CAPS)
+    );
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (SRC_CAPS)
+    );
+
+static void
+buffer_unref (void *buffer, void *user_data)
+{
+  gst_buffer_unref (GST_BUFFER (buffer));
+}
+
+static GstElement *
+setup_amrnbenc (void)
+{
+  GstElement *amrnbenc;
+  GstCaps *caps;
+  GstBus *bus;
+
+  GST_DEBUG ("setup_amrnbenc");
+
+  amrnbenc = gst_check_setup_element ("amrnbenc");
+  srcpad = gst_check_setup_src_pad (amrnbenc, &srctemplate);
+  sinkpad = gst_check_setup_sink_pad (amrnbenc, &sinktemplate);
+  gst_pad_set_active (srcpad, TRUE);
+  gst_pad_set_active (sinkpad, TRUE);
+
+  bus = gst_bus_new ();
+  gst_element_set_bus (amrnbenc, bus);
+
+  fail_unless (gst_element_set_state (amrnbenc,
+          GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
+      "could not set to playing");
+
+  caps = gst_caps_from_string (SRC_CAPS);
+  gst_check_setup_events (srcpad, amrnbenc, caps, GST_FORMAT_TIME);
+  gst_caps_unref (caps);
+
+  buffers = NULL;
+  return amrnbenc;
+}
+
+static void
+cleanup_amrnbenc (GstElement * amrnbenc)
+{
+  GstBus *bus;
+
+  /* free encoded buffers */
+  g_list_foreach (buffers, buffer_unref, NULL);
+  g_list_free (buffers);
+  buffers = NULL;
+
+  bus = GST_ELEMENT_BUS (amrnbenc);
+  gst_bus_set_flushing (bus, TRUE);
+  gst_object_unref (bus);
+
+  GST_DEBUG ("cleanup_amrnbenc");
+  gst_pad_set_active (srcpad, FALSE);
+  gst_pad_set_active (sinkpad, FALSE);
+  gst_check_teardown_src_pad (amrnbenc);
+  gst_check_teardown_sink_pad (amrnbenc);
+  gst_check_teardown_element (amrnbenc);
+}
+
+/* push a random block of audio of the given size */
+static void
+push_data (gint size, GstFlowReturn expected_return)
+{
+  GstBuffer *buffer;
+  GstFlowReturn res;
+
+  buffer = gst_buffer_new_and_alloc (size);
+  /* make valgrind happier */
+  gst_buffer_memset (buffer, 0, 0, size);
+
+  res = gst_pad_push (srcpad, buffer);
+  fail_unless (res == expected_return,
+      "pushing audio returned %d (%s) not %d (%s)", res,
+      gst_flow_get_name (res), expected_return,
+      gst_flow_get_name (expected_return));
+}
+
+GST_START_TEST (test_enc)
+{
+  GstElement *amrnbenc;
+
+  amrnbenc = setup_amrnbenc ();
+  push_data (1000, GST_FLOW_OK);
+
+  cleanup_amrnbenc (amrnbenc);
+}
+
+GST_END_TEST;
+
+static Suite *
+amrnbenc_suite (void)
+{
+  Suite *s = suite_create ("amrnbenc");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_enc);
+  return s;
+}
+
+GST_CHECK_MAIN (amrnbenc);
diff --git a/tests/check/elements/dash_mpd.c b/tests/check/elements/dash_mpd.c
index bda2787df97a596d4dcfcaa77c7bae090f6601de..562765f4b813af6d49878743e1cfd557ce0cde07 100644
--- a/tests/check/elements/dash_mpd.c
+++ b/tests/check/elements/dash_mpd.c
@@ -106,6 +106,10 @@ duration_to_clocktime (guint year, guint month, guint day, guint hour,
           millisecond));
 }
 
+/* Setup streaming with default properties values */
+#define setup_streaming_simple(cl, node) \
+    gst_mpd_client2_setup_streaming(cl, node, 0, 0, 0, 0, 1);
+
 /*
  * Test to ensure a simple mpd file successfully parses.
  *
@@ -955,7 +959,7 @@ GST_START_TEST (dash_mpdparser_period_segmentTemplateWithPresentationTimeOffset)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
@@ -2989,13 +2993,13 @@ GST_START_TEST (dash_mpdparser_bitstreamSwitching_inheritance)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* setup streaming from the second adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 1);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* 2 active streams */
@@ -3203,7 +3207,7 @@ GST_START_TEST (dash_mpdparser_setup_streaming)
   fail_if (adapt_set == NULL);
 
   /* setup streaming from the adaptation set */
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   gst_mpd_client2_free (mpdclient);
@@ -3536,7 +3540,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* 1 active streams */
@@ -3546,7 +3550,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   /* setup streaming from the second adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 1);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* 2 active streams */
@@ -3556,7 +3560,7 @@ GST_START_TEST (dash_mpdparser_activeStream_selection)
   /* setup streaming from the third adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 2);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* 3 active streams */
@@ -3635,7 +3639,7 @@ GST_START_TEST (dash_mpdparser_activeStream_parameters)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* 1 active streams */
@@ -3727,7 +3731,7 @@ GST_START_TEST (dash_mpdparser_get_audio_languages)
   for (i = 0; i < adaptationSetsCount; i++) {
     adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, i);
     fail_if (adapt_set == NULL);
-    ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+    ret = setup_streaming_simple (mpdclient, adapt_set);
     assert_equals_int (ret, TRUE);
   }
   activeStreams = gst_mpd_client2_get_nb_active_stream (mpdclient);
@@ -3779,7 +3783,7 @@ setup_mpd_client (const gchar * xml)
   for (i = 0; i < adaptationSetsCount; i++) {
     adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, i);
     fail_if (adapt_set == NULL);
-    ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+    ret = setup_streaming_simple (mpdclient, adapt_set);
     assert_equals_int (ret, TRUE);
   }
   activeStreams = gst_mpd_client2_get_nb_active_stream (mpdclient);
@@ -4228,7 +4232,7 @@ GST_START_TEST (dash_mpdparser_get_streamPresentationOffset)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* test the stream presentation time offset */
@@ -4299,7 +4303,7 @@ GST_START_TEST (dash_mpdparser_segments)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -4434,7 +4438,7 @@ GST_START_TEST (dash_mpdparser_headers)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   /* get segment url and range from segment Initialization */
@@ -4510,7 +4514,7 @@ GST_START_TEST (dash_mpdparser_fragments)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
   fail_if (activeStream == NULL);
@@ -4663,7 +4667,7 @@ GST_START_TEST (dash_mpdparser_inherited_segmentURL)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -4750,7 +4754,7 @@ GST_START_TEST (dash_mpdparser_segment_list)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -4835,7 +4839,7 @@ GST_START_TEST (dash_mpdparser_segment_template)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -4956,7 +4960,7 @@ GST_START_TEST (dash_mpdparser_segment_timeline)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -5146,7 +5150,7 @@ GST_START_TEST (dash_mpdparser_multiple_inherited_segmentURL)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -5265,7 +5269,7 @@ GST_START_TEST (dash_mpdparser_multipleSegmentURL)
   /* setup streaming from the first adaptation set */
   adapt_set = (GstMPDAdaptationSetNode *) g_list_nth_data (adaptationSets, 0);
   fail_if (adapt_set == NULL);
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, TRUE);
 
   activeStream = gst_mpd_client2_get_active_stream_by_index (mpdclient, 0);
@@ -5725,7 +5729,7 @@ GST_START_TEST (dash_mpdparser_unmatched_segmentTimeline_segmentURL)
    * Should fail because the second S node does not have a  matching
    * SegmentURL node
    */
-  ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set);
+  ret = setup_streaming_simple (mpdclient, adapt_set);
   assert_equals_int (ret, FALSE);
 
   gst_mpd_client2_free (mpdclient);
@@ -5892,7 +5896,7 @@ GST_START_TEST (dash_mpdparser_maximum_segment_duration)
   for (iter = adapt_sets; iter; iter = g_list_next (iter)) {
     GstMPDAdaptationSetNode *adapt_set_node = iter->data;
 
-    ret = gst_mpd_client2_setup_streaming (mpdclient, adapt_set_node);
+    ret = setup_streaming_simple (mpdclient, adapt_set_node);
     assert_equals_int (ret, TRUE);
   }
   dur = gst_mpd_client2_get_maximum_segment_duration (mpdclient);
diff --git a/tests/check/elements/dtmf.c b/tests/check/elements/dtmf.c
index c2ae36d5098a4287f9e301da82b10d1ddd807d18..54025bbbdb2875fb9cb04de88ca8215ae3df1965 100644
--- a/tests/check/elements/dtmf.c
+++ b/tests/check/elements/dtmf.c
@@ -178,7 +178,8 @@ GST_START_TEST (test_rtpdtmfdepay)
   caps_out = gst_pad_get_current_caps (sink);
   expected_caps_out = gst_caps_new_simple ("audio/x-raw",
       "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
-      "rate", G_TYPE_INT, 1000, "channels", G_TYPE_INT, 1, NULL);
+      "rate", G_TYPE_INT, 1000, "channels", G_TYPE_INT, 1,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
   fail_unless (gst_caps_is_equal_fixed (caps_out, expected_caps_out));
   gst_caps_unref (expected_caps_out);
   gst_caps_unref (caps_out);
@@ -295,7 +296,7 @@ check_rtp_buffer (GstClockTime ts, GstClockTime duration, gboolean start,
 
   fail_unless (payload[0] == number);
   fail_unless ((payload[1] & 0x7F) == volume);
-  fail_unless (! !(payload[1] & 0x80) == end);
+  fail_unless (!!(payload[1] & 0x80) == end);
   fail_unless (GST_READ_UINT16_BE (payload + 2) == rtpduration);
 
   gst_rtp_buffer_unmap (&rtpbuffer);
@@ -559,6 +560,40 @@ GST_START_TEST (test_dtmfsrc_min_duration)
 
 GST_END_TEST;
 
+GST_START_TEST (test_rtpdtmfdepay_src_caps_fixated)
+{
+  // Loop a few times, additional test for rtpdtmfsrc leak on early shutdown
+  for (int i = 0; i < 50; ++i) {
+    GstElement *pipeline;
+    GstStructure *s;
+
+    pipeline =
+        gst_parse_launch ("rtpdtmfsrc ! rtpdtmfdepay ! audioconvert ! fakesink",
+        NULL);
+
+    fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+        GST_STATE_CHANGE_ASYNC);
+
+    // Send an event so there's some data flow and the pipeline prerolls
+    s = gst_structure_new ("dtmf-event",
+        "type", G_TYPE_INT, 1, "number", G_TYPE_INT, 3,
+        "method", G_TYPE_INT, 1, "volume", G_TYPE_INT, 8,
+        "start", G_TYPE_BOOLEAN, TRUE, NULL);
+
+    fail_unless (gst_element_send_event (pipeline,
+            gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s)));
+
+    // Wait for preroll
+    fail_unless_equals_int (gst_element_get_state (pipeline, NULL, NULL, -1),
+        GST_STATE_CHANGE_SUCCESS);
+
+    gst_element_set_state (pipeline, GST_STATE_NULL);
+    gst_object_unref (pipeline);
+  }
+}
+
+GST_END_TEST;
+
 static Suite *
 dtmf_suite (void)
 {
@@ -567,6 +602,7 @@ dtmf_suite (void)
 
   tc = tcase_create ("rtpdtmfdepay");
   tcase_add_test (tc, test_rtpdtmfdepay);
+  tcase_add_test (tc, test_rtpdtmfdepay_src_caps_fixated);
   suite_add_tcase (s, tc);
 
   tc = tcase_create ("rtpdtmfsrc");
diff --git a/tests/check/elements/flvmux.c b/tests/check/elements/flvmux.c
index 53a8e634e41a2c414b6c28a21701d8d812063399..6efa928fb09bacad93869de425749ebd7c1ec43d 100644
--- a/tests/check/elements/flvmux.c
+++ b/tests/check/elements/flvmux.c
@@ -533,7 +533,7 @@ GST_START_TEST (test_video_caps_late)
 
 
   /* push from flvmux to demux */
-  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 6));
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 7));
 
   /* verify we got 2x audio and 1x video buffers out of flvdemux */
   gst_buffer_unref (gst_harness_pull (a_sink));
@@ -630,7 +630,7 @@ GST_START_TEST (test_video_caps_change_streamable)
   gst_harness_crank_single_clock_wait (mux);
 
   /* push from flvmux to demux */
-  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 2));
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 3));
 
   /* should accept without the constraint */
   while ((event = gst_harness_try_pull_event (v_sink))) {
@@ -735,7 +735,7 @@ GST_START_TEST (test_audio_caps_change_streamable)
   gst_harness_crank_single_clock_wait (mux);
 
   /* push from flvmux to demux */
-  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 2));
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 3));
 
   /* should accept without the constraint */
   while ((event = gst_harness_try_pull_event (a_sink))) {
@@ -820,7 +820,7 @@ GST_START_TEST (test_video_caps_change_streamable_single)
               duration)));
 
   /* push from flvmux to demux */
-  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 2));
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 3));
 
   /* should accept without the constraint */
   while ((event = gst_harness_try_pull_event (v_sink))) {
@@ -906,7 +906,7 @@ GST_START_TEST (test_audio_caps_change_streamable_single)
               base_time + duration, duration)));
 
   /* push from flvmux to demux */
-  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 2));
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_sink_push_many (mux, 3));
 
   /* should accept without the constraint */
   while ((event = gst_harness_try_pull_event (a_sink))) {
diff --git a/tests/check/elements/hlsdemux_m3u8.c b/tests/check/elements/hlsdemux_m3u8.c
index 954d47927e74e4a8eabf24afa5941eb91871d6ec..8f2cbe00f3865ba1ba37daab0c046538ef8a1a19 100644
--- a/tests/check/elements/hlsdemux_m3u8.c
+++ b/tests/check/elements/hlsdemux_m3u8.c
@@ -232,13 +232,64 @@ main.mp4\n\
 main.mp4\n\
 #EXT-X-ENDLIST";
 
+static const gchar *LOW_LATENCY_PLAYLIST = "#EXTM3U\n\
+#EXT-X-VERSION:7\n\
+#EXT-X-TARGETDURATION:4\n\
+#EXT-X-PART-INF:PART-TARGET=2\n\
+#EXTINF:4.00008,\n\
+fileSequence268.mp4\n\
+#EXTINF:4.00008,\n\
+fileSequence269.mp4\n\
+#EXTINF:4.00008,\n\
+fileSequence270.mp4\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"filePart271.0.mp4\"\n\
+#EXT-X-PART:DURATION=2.00004,URI=\"filePart271.1.mp4\"\n\
+#EXTINF:4.00008,\n\
+fileSequence271.mp4\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"filePart272.0.mp4\"\n\
+#EXT-X-PART:DURATION=0.50001,URI=\"filePart272.1.mp4\"\n\
+#EXTINF:2.50005,\n\
+fileSequence272.mp4\n\
+#EXT-X-DISCONTINUITY\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"midRoll273.0.mp4\"\n\
+#EXT-X-PART:DURATION=2.00004,URI=\"midRoll273.1.mp4\"\n\
+#EXTINF:4.00008,\n\
+midRoll273.mp4\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"midRoll274.0.mp4\"\n\
+#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"midRoll274.1.mp4\"\n\
+#EXT-X-RENDITION-REPORT:URI=\"/1M/LL-HLS.m3u8\",LAST-MSN=274,LAST-PART=1";
+
+static const gchar *SKIP_PLAYLIST = "#EXTM3U\n\
+#EXT-X-VERSION:7\n\
+#EXT-X-TARGETDURATION:4\n\
+#EXT-X-PART-INF:PART-TARGET=2\n\
+#EXT-X-SKIP:SKIPPED-SEGMENTS=2,RECENTLY-REMOVED-DATERANGES=\"splice-6FFFFFF0\tsplice-6FFFFFF1\"\n\
+#EXTINF:4.00008,\n\
+fileSequence270.mp4\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"filePart271.0.mp4\"\n\
+#EXT-X-PART:DURATION=2.00004,URI=\"filePart271.1.mp4\"\n\
+#EXTINF:4.00008,\n\
+fileSequence271.mp4\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"filePart272.0.mp4\"\n\
+#EXT-X-PART:DURATION=0.50001,URI=\"filePart272.1.mp4\"\n\
+#EXTINF:2.50005,\n\
+fileSequence272.mp4\n\
+#EXT-X-DISCONTINUITY\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"midRoll273.0.mp4\"\n\
+#EXT-X-PART:DURATION=2.00004,URI=\"midRoll273.1.mp4\"\n\
+#EXTINF:4.00008,\n\
+midRoll273.mp4\n\
+#EXT-X-PART:DURATION=2.00004,INDEPENDENT=YES,URI=\"midRoll274.0.mp4\"\n\
+#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"midRoll274.1.mp4\"\n\
+#EXT-X-RENDITION-REPORT:URI=\"/1M/LL-HLS.m3u8\",LAST-MSN=274,LAST-PART=1";
+
 static GstHLSMediaPlaylist *
 load_m3u8 (const gchar * data)
 {
   GstHLSMediaPlaylist *playlist;
 
   playlist = gst_hls_media_playlist_parse (g_strdup (data),
-      "http://localhost/test.m3u8", NULL);
+      GST_CLOCK_TIME_NONE, "http://localhost/test.m3u8", NULL);
   fail_unless (playlist != NULL);
 
   return playlist;
@@ -529,7 +580,7 @@ GST_START_TEST (test_parse_invalid_playlist)
   GstHLSMediaPlaylist *pl;
 
   pl = gst_hls_media_playlist_parse (g_strdup ("#INVALID"),
-      "http://localhost/test.m3u8", NULL);
+      GST_CLOCK_TIME_NONE, "http://localhost/test.m3u8", NULL);
   fail_if (pl != NULL);
 }
 
@@ -648,11 +699,15 @@ GST_START_TEST (test_advance_fragment)
 {
   GstHLSMediaPlaylist *pl;
   GstM3U8MediaSegment *mf;
+  GstM3U8SeekResult seek_result;
 
   pl = load_m3u8 (BYTE_RANGES_PLAYLIST);
 
   /* Check the next fragment */
-  mf = gst_hls_media_playlist_get_starting_segment (pl);
+  fail_unless (gst_hls_media_playlist_get_starting_segment (pl,
+          &seek_result) == TRUE);
+
+  mf = seek_result.segment;
   fail_unless (mf != NULL);
   assert_equals_int (mf->discont, FALSE);
   assert_equals_string (mf->uri, "http://media.example.com/all.ts");
@@ -725,22 +780,27 @@ GST_START_TEST (test_get_stream_for_bitrate)
   GstHLSVariantStream *stream;
 
   master = load_master_playlist (VARIANT_PLAYLIST);
-  stream = gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 0, 0);
+  stream =
+      gst_hls_master_playlist_get_variant_for_bitrate (master, FALSE, 0, 0,
+      NULL);
 
   assert_equals_int (stream->bandwidth, 65000);
 
   stream =
-      gst_hls_master_playlist_get_variant_for_bitrate (master, NULL,
-      G_MAXINT32, 0);
+      gst_hls_master_playlist_get_variant_for_bitrate (master, FALSE,
+      G_MAXINT32, 0, NULL);
   assert_equals_int (stream->bandwidth, 768000);
   stream =
-      gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 300000, 0);
+      gst_hls_master_playlist_get_variant_for_bitrate (master, FALSE, 300000, 0,
+      NULL);
   assert_equals_int (stream->bandwidth, 256000);
   stream =
-      gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 500000, 0);
+      gst_hls_master_playlist_get_variant_for_bitrate (master, FALSE, 500000, 0,
+      NULL);
   assert_equals_int (stream->bandwidth, 256000);
   stream =
-      gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 255000, 0);
+      gst_hls_master_playlist_get_variant_for_bitrate (master, FALSE, 255000, 0,
+      NULL);
   assert_equals_int (stream->bandwidth, 128000);
 
   gst_hls_master_playlist_unref (master);
@@ -845,6 +905,68 @@ GST_START_TEST (test_map_tag)
 
 GST_END_TEST;
 
+GST_START_TEST (test_low_latency_playlist)
+{
+  GstHLSMediaPlaylist *pl;
+  GPtrArray *segments;
+  GstM3U8MediaSegment *seg;
+
+  /* Test low latency playlist features. EXT-X-PART-INF and EXT-X-PART
+   *
+   * There are 6 complete segments, and 1 dummy trailing segment containing
+   * only a partial segment
+   */
+
+  pl = load_m3u8 (LOW_LATENCY_PLAYLIST);
+  fail_unless (pl != NULL);
+
+  segments = pl->segments;
+
+  assert_equals_int (segments->len, 7);
+
+  for (gsize i = 0; i < segments->len; i++) {
+    GstM3U8MediaSegment *file = g_ptr_array_index (segments, i);
+
+    /* The first segments are full, with no partial entries */
+    if (i < 3) {
+      fail_unless (file->partial_segments == NULL);
+    } else if (i < 6) {
+      fail_unless (file->partial_segments != NULL);
+      /* 2 partial segments expected */
+      fail_unless (file->partial_segments->len == 2);
+    } else {
+      fail_unless (file->partial_segments != NULL);
+      /* 1 partial segment expected in the last segment */
+      fail_unless (file->partial_segments->len == 1);
+    }
+  }
+
+  /* The final segment only has one partial segment */
+  seg = g_ptr_array_index (segments, 6);
+  fail_unless (seg->partial_only == TRUE);
+
+  gst_hls_media_playlist_unref (pl);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_playlist_skip)
+{
+  GstHLSMediaPlaylist *pl;
+
+  pl = load_m3u8 (SKIP_PLAYLIST);
+  fail_unless (pl != NULL);
+
+  assert_equals_int (pl->skipped_segments, 2);
+  assert_equals_int (pl->num_removed_date_ranges, 2);
+  fail_unless (g_strcmp0 (pl->removed_date_ranges[0], "splice-6FFFFFF0") == 0);
+  fail_unless (g_strcmp0 (pl->removed_date_ranges[1], "splice-6FFFFFF1") == 0);
+
+  gst_hls_media_playlist_unref (pl);
+}
+
+GST_END_TEST;
+
 static Suite *
 hlsdemux_suite (void)
 {
@@ -879,6 +1001,8 @@ hlsdemux_suite (void)
   tcase_add_test (tc_m3u8, test_url_with_slash_query_param);
   tcase_add_test (tc_m3u8, test_stream_inf_tag);
   tcase_add_test (tc_m3u8, test_map_tag);
+  tcase_add_test (tc_m3u8, test_low_latency_playlist);
+  tcase_add_test (tc_m3u8, test_playlist_skip);
   return s;
 }
 
diff --git a/tests/check/elements/matroskademux.c b/tests/check/elements/matroskademux.c
index 5081c8494aca8991e54a96d797d4ca50edf90b2d..86fbc17df81ee11238782502c053bc2913d24799 100644
--- a/tests/check/elements/matroskademux.c
+++ b/tests/check/elements/matroskademux.c
@@ -1,5 +1,5 @@
 /* GStreamer unit test for matroskademux
- * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
+ * Copyright (C) 2015, 2023 Tim-Philipp Müller <tim@centricular.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -307,6 +307,183 @@ GST_START_TEST (test_toc_demux)
 
 GST_END_TEST;
 
+static void
+demux_pad_added_cb (GstElement * demux, GstPad * new_pad, gpointer user_data)
+{
+  GstElement *sink = GST_ELEMENT (user_data);
+
+  fail_unless (gst_element_link (demux, sink));
+}
+
+static const gint64 PINKNOISE_MKV_DURATION = 116099773;
+
+static void
+run_segment_looping_test (gint64 start, gint64 stop, gdouble rate)
+{
+  GstStateChangeReturn state_ret;
+  GstMessage *msg;
+  GstElement *src, *sink, *demux, *pipeline;
+  gboolean seek_ret;
+  GstBus *bus;
+  gchar *path;
+
+  g_assert (start >= 0);
+  g_assert (stop >= -1);
+  g_assert (rate > 0.0);
+
+  // only handle a rate for the middle segment case for now
+  g_assert (rate == 1.0 || stop != -1);
+
+  pipeline = gst_pipeline_new ("pipeline");
+  fail_unless (pipeline != NULL, "Failed to create pipeline!");
+
+  bus = gst_element_get_bus (pipeline);
+
+  src = gst_element_factory_make ("filesrc", "filesrc");
+  fail_unless (src != NULL, "Failed to create 'filesrc' element!");
+
+  demux = gst_element_factory_make ("matroskademux", "demux");
+  fail_unless (demux != NULL, "Failed to create matroskademux element");
+
+  sink = gst_element_factory_make ("fakesink", "fakesink");
+  fail_unless (sink != NULL, "Failed to create 'fakesink' element!");
+
+  gst_bin_add_many (GST_BIN (pipeline), src, demux, sink, NULL);
+
+  fail_unless (gst_element_link (src, demux));
+
+  g_signal_connect (demux, "pad-added", G_CALLBACK (demux_pad_added_cb), sink);
+
+  path = g_build_filename (GST_TEST_FILES_PATH, "pinknoise-vorbis.mkv", NULL);
+  GST_LOG ("reading file '%s'", path);
+  g_object_set (src, "location", path, NULL);
+
+  state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+  fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+  if (state_ret == GST_STATE_CHANGE_ASYNC) {
+    GST_LOG ("waiting for pipeline to reach PAUSED state");
+    state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+    fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+  }
+
+  GST_LOG ("PAUSED, let's play a little..");
+  state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+  GST_LOG ("Send FLUSHING seek with SEGMENT flag set.. "
+      "(start=%" G_GINT64_FORMAT ", stop=%" G_GINT64_FORMAT ")", start, stop);
+
+  if (stop == -1) {
+    seek_ret = gst_element_seek_simple (pipeline, GST_FORMAT_TIME,
+        GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT, start);
+  } else {
+    seek_ret = gst_element_seek (pipeline, rate, GST_FORMAT_TIME,
+        GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT,
+        GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_SET, stop);
+  }
+  fail_unless (seek_ret == TRUE);
+
+  GST_LOG ("Waiting for pipeline to preroll again after flushing seek..");
+  state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+  fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+
+  {
+    GstFormat fmt = GST_FORMAT_UNDEFINED;
+    gint64 segment_end = 0;
+
+    GST_LOG ("Waiting for SEGMENT_DONE message..");
+    msg = gst_bus_timed_pop_filtered (bus, -1, GST_MESSAGE_SEGMENT_DONE);
+    fail_unless (msg != NULL);
+
+    gst_message_parse_segment_done (msg, &fmt, &segment_end);
+    fail_unless_equals_int (fmt, GST_FORMAT_TIME);
+    if (stop == -1) {
+      fail_unless_equals_int64 (segment_end, PINKNOISE_MKV_DURATION);
+    } else {
+      fail_unless_equals_int64 (segment_end, stop);
+    }
+    gst_clear_message (&msg);
+  }
+
+  GST_LOG ("Send non-FLUSHING seek to start new segment loop.. "
+      "(start=%" G_GINT64_FORMAT ", stop=%" G_GINT64_FORMAT ")", start, stop);
+
+  /* No segment flag this time, so will be expecting an EOS at the end */
+  if (stop == -1) {
+    seek_ret = gst_element_seek_simple (pipeline, GST_FORMAT_TIME, 0, start);
+  } else {
+    seek_ret = gst_element_seek (pipeline, rate, GST_FORMAT_TIME, 0,
+        GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_SET, stop);
+  }
+  fail_unless (seek_ret == TRUE);
+
+  GST_LOG ("Waiting for EOS message..");
+  msg = gst_bus_timed_pop_filtered (bus, -1, GST_MESSAGE_EOS);
+  fail_unless (msg != NULL);
+  gst_clear_message (&msg);
+
+  {
+    GstPad *pad = gst_element_get_static_pad (sink, "sink");
+    GstEvent *ev = gst_pad_get_sticky_event (pad, GST_EVENT_SEGMENT, 0);
+    const GstSegment *segment = NULL;
+
+    gst_event_parse_segment (ev, &segment);
+
+    GST_INFO ("segment %" GST_SEGMENT_FORMAT, segment);
+
+    fail_unless_equals_int64 (segment->start, start);
+    if (stop == -1) {
+      fail_unless_equals_int64 (segment->stop, PINKNOISE_MKV_DURATION);
+      fail_unless_equals_int64 (segment->duration, PINKNOISE_MKV_DURATION);
+      fail_unless_equals_int64 (segment->base, PINKNOISE_MKV_DURATION);
+    } else {
+      fail_unless_equals_int64 (segment->stop, stop);
+      fail_unless_equals_int64 (segment->duration, PINKNOISE_MKV_DURATION);
+      fail_unless_equals_int64 (segment->base, (stop - start) / rate);
+    }
+
+    gst_clear_event (&ev);
+    gst_clear_object (&pad);
+  }
+
+  fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
+      GST_STATE_CHANGE_SUCCESS);
+  gst_object_unref (pipeline);
+  gst_clear_object (&bus);
+
+  g_free (path);
+}
+
+/* Make sure segment seeks behave as expected, and the segment base offset
+ * is increased correctly by the clip duration rather than the last timestamp
+ * position. */
+GST_START_TEST (test_segment_looping)
+{
+  run_segment_looping_test (0, -1, 1.0);
+}
+
+GST_END_TEST;
+
+/* If we do a segment seek on a middle segment of the clip, we expect the
+ * base offset of the next segment to be the duration of our selected segment,
+ * not the duration of the entire clip, since we only played that much then. */
+GST_START_TEST (test_segment_looping_middle_segment)
+{
+  run_segment_looping_test (50 * GST_MSECOND, 100 * GST_MSECOND, 1.0);
+}
+
+GST_END_TEST;
+
+/* For positive non-1.0 rates the base offset of the next segment should be
+ * scaled accordingly, since it's in running time not stream time. */
+GST_START_TEST (test_segment_looping_middle_segment_with_rate)
+{
+  run_segment_looping_test (50 * GST_MSECOND, 100 * GST_MSECOND, 2.0);
+}
+
+GST_END_TEST;
+
 static Suite *
 matroskademux_suite (void)
 {
@@ -316,6 +493,9 @@ matroskademux_suite (void)
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_sub_terminator);
   tcase_add_test (tc_chain, test_toc_demux);
+  tcase_add_test (tc_chain, test_segment_looping);
+  tcase_add_test (tc_chain, test_segment_looping_middle_segment);
+  tcase_add_test (tc_chain, test_segment_looping_middle_segment_with_rate);
 
   return s;
 }
diff --git a/tests/check/elements/matroskamux.c b/tests/check/elements/matroskamux.c
index fa53c7ad6878b53489227f9a877679e73977a12f..c6ac0ef8a65ff62caa78d969d0b888188eef8f81 100644
--- a/tests/check/elements/matroskamux.c
+++ b/tests/check/elements/matroskamux.c
@@ -59,8 +59,8 @@ seekable_sinkpad_query (GstPad * pad, GstObject * parent, GstQuery * query)
 
 #define compare_buffer_to_data(buffer, data, data_size)             \
 G_STMT_START {                                                      \
-fail_unless_equals_int (data_size, gst_buffer_get_size (buffer));   \
-fail_unless (gst_buffer_memcmp (buffer, 0, data, data_size) == 0);  \
+  fail_unless_equals_int (data_size, gst_buffer_get_size (buffer)); \
+  fail_unless (gst_buffer_memcmp (buffer, 0, data, data_size) == 0);\
 } G_STMT_END
 
 static void
@@ -227,7 +227,7 @@ GST_END_TEST;
 
 GST_START_TEST (test_block_group_v2)
 {
-  guint8 data0_v2[] = { 0xa3, 0x85, 0x81, 0x00, 0x01, 0x00 };
+  guint8 data0_v2[] = { 0xa3, 0x85, 0x81, 0x00, 0x01, 0x80 };
 
   test_block_group_with_version (2, data0_v2, sizeof (data0_v2));
 }
@@ -307,10 +307,10 @@ GST_START_TEST (test_timecodescale)
 {
   GstBuffer *inbuffer, *outbuffer;
   guint8 data_h0[] = {
-    0xa3, 0x85, 0x81, 0x00, 0x00, 0x00,
+    0xa3, 0x85, 0x81, 0x00, 0x00, 0x80,
   };
   guint8 data_h1[] = {
-    0xa3, 0x85, 0x81, 0x00, 0x01, 0x00,
+    0xa3, 0x85, 0x81, 0x00, 0x01, 0x80,
   };
 
   GstHarness *h = setup_matroskamux_harness (AC3_CAPS_STRING);
diff --git a/tests/check/elements/qtdemux.c b/tests/check/elements/qtdemux.c
index 4a14c45c062997d131ce2ef974093432a2131d31..4850ee88216e677773b32210ce43aeaea23d230e 100644
--- a/tests/check/elements/qtdemux.c
+++ b/tests/check/elements/qtdemux.c
@@ -19,10 +19,98 @@
  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
-#include "qtdemux.h"
+#include <glib/gstdio.h>
 #include <glib/gprintf.h>
-#include <gst/check/gstharness.h>
+
+#include <gio/gio.h>
+
+#include <gst/check/check.h>
+#include <gst/app/app.h>
+#include <gst/audio/audio.h>
+
+#define TEST_FILE_PREFIX GST_TEST_FILES_PATH G_DIR_SEPARATOR_S
+
+/* Fragments taken from http://dash.akamaized.net/dash264/TestCases/5c/nomor/4_1a.mpd
+ *
+ * Audio stream (aac)
+ * Header + first Fragments
+ */
+/* http://dash.akamaized.net/dash264/TestCases/5c/nomor/BBB_32k_init.mp4 */
+#define BBB_FILE_I TEST_FILE_PREFIX "qtdemux-test-BBB_32k_init.mp4"
+static guint8 *BBB_32k_init_mp4;
+static const guint BBB_32k_init_mp4_len = 776;
+
+/* http://dash.akamaized.net/dash264/TestCases/5c/nomor/BBB_32k_1.mp4 */
+#define BBB_FILE_1 TEST_FILE_PREFIX "qtdemux-test-BBB_32k_1.mp4"
+static guint8 *BBB_32k_1_mp4;
+static const guint BBB_32k_1_mp4_len = 8423;
+
+/* Fragments taken from http://www.bok.net/dash/tears_of_steel/cleartext/stream.mpd
+ *
+ * Audio stream (aac)
+ * Header + first fragment
+ */
+/* http://www.bok.net/dash/tears_of_steel/cleartext/audio/en/init.mp4 */
+#define INIT_FILE TEST_FILE_PREFIX "qtdemux-test-audio-init.mp4"
+static guint8 *init_mp4;
+const guint init_mp4_len = 624;
+
+/* http://www.bok.net/dash/tears_of_steel/cleartext/audio/en/seg-1.m4f */
+#define SEG1_FILE TEST_FILE_PREFIX "qtdemux-test-audio-seg1.m4f"
+static guint8 *seg_1_m4f;
+const guint seg_1_m4f_len = 49554;
+const guint seg_1_moof_size = 1120;
+const guint seg_1_sample_0_offset = 1128;
+
+static const guint seg_1_sample_sizes[] = {
+  371, 372, 477, 530, 489, 462, 441, 421, 420, 410, 402, 398, 381, 381, 386,
+  386, 369, 370, 362, 346, 357, 355, 376, 336, 341, 358, 350, 362, 333, 415,
+  386, 364, 344, 386, 358, 365, 404, 342, 361, 366, 361, 350, 390, 348, 366,
+  359, 357, 360, 349, 356, 365, 393, 353, 385, 381, 348, 345, 414, 372, 369,
+  401, 391, 333, 339, 423, 343, 445, 425, 422, 415, 406, 389, 395, 375, 356,
+  442, 432, 391, 385, 339, 277, 293, 316, 327, 309, 389, 359, 427, 326, 420,
+  407, 316, 362, 419, 349, 387, 326, 328, 367, 344, 425, 329, 379, 403, 314,
+  397, 368, 389, 380, 373, 342, 343, 368, 436, 359, 352, 361, 366, 350, 419,
+  331, 426, 401, 382, 326, 411, 364, 338, 345
+};
+
+/* in timescale */
+static const GstClockTime seg_1_sample_duration = 1024;
+static const guint32 seg_1_timescale = 44100;
+
+static gboolean
+load_file (const gchar * fn, guint8 ** p_data, guint expected_len)
+{
+  gsize read_len = 0;
+
+  if (!g_file_get_contents (fn, (gchar **) p_data, &read_len, NULL))
+    return FALSE;
+
+  g_assert_cmpuint (read_len, ==, expected_len);
+  return TRUE;
+}
+
+static void
+load_files (void)
+{
+  g_assert (load_file (INIT_FILE, &init_mp4, init_mp4_len));
+  g_assert (load_file (SEG1_FILE, &seg_1_m4f, seg_1_m4f_len));
+  g_assert (load_file (BBB_FILE_I, &BBB_32k_init_mp4, BBB_32k_init_mp4_len));
+  g_assert (load_file (BBB_FILE_1, &BBB_32k_1_mp4, BBB_32k_1_mp4_len));
+}
+
+static void
+unload_files (void)
+{
+  g_clear_pointer (&init_mp4, (GDestroyNotify) g_free);
+  g_clear_pointer (&seg_1_m4f, (GDestroyNotify) g_free);
+  g_clear_pointer (&BBB_32k_init_mp4, (GDestroyNotify) g_free);
+  g_clear_pointer (&BBB_32k_1_mp4, (GDestroyNotify) g_free);
+}
 
 typedef struct
 {
@@ -198,6 +286,8 @@ GST_START_TEST (test_qtdemux_input_gap)
    *     of 0.
    */
 
+  load_files ();
+
   qtdemux = gst_element_factory_make ("qtdemux", NULL);
   gst_element_set_state (qtdemux, GST_STATE_PLAYING);
   sinkpad = gst_element_get_static_pad (qtdemux, "sink");
@@ -260,6 +350,8 @@ GST_START_TEST (test_qtdemux_input_gap)
   gst_object_unref (sinkpad);
   gst_element_set_state (qtdemux, GST_STATE_NULL);
   gst_object_unref (qtdemux);
+
+  unload_files ();
 }
 
 GST_END_TEST;
@@ -393,6 +485,8 @@ GST_START_TEST (test_qtdemux_duplicated_moov)
   data.expected_num_srcpad = 1;
   data.total_step = G_N_ELEMENTS (expected);
 
+  load_files ();
+
   /* The goal of this test is to check that qtdemux can properly handle
    * duplicated moov without redundant events and pad exposing
    *
@@ -468,6 +562,8 @@ GST_START_TEST (test_qtdemux_duplicated_moov)
   gst_object_unref (data.sinkpad);
   gst_element_set_state (qtdemux, GST_STATE_NULL);
   gst_object_unref (qtdemux);
+
+  unload_files ();
 }
 
 GST_END_TEST;
@@ -519,6 +615,8 @@ GST_START_TEST (test_qtdemux_stream_change)
   data.expected_num_srcpad = 4;
   data.total_step = G_N_ELEMENTS (expected);
 
+  load_files ();
+
   /* The goal of this test is to check that qtdemux can properly handle
    * stream change regardless of track-id change.
    * This test is simulating DASH bitrate switching (for both playbin and plabyin3)
@@ -736,6 +834,8 @@ GST_START_TEST (test_qtdemux_stream_change)
   gst_object_unref (data.sinkpad);
   gst_element_set_state (qtdemux, GST_STATE_NULL);
   gst_object_unref (qtdemux);
+
+  unload_files ();
 }
 
 GST_END_TEST;
@@ -764,6 +864,8 @@ GST_START_TEST (test_qtdemux_pad_names)
   GstCaps *caps;
   GstCaps *mediacaps;
 
+  load_files ();
+
   /* The goal of this test is to check that qtdemux can create proper
    * pad names with encrypted stream caps in mss mode.
    *
@@ -884,6 +986,755 @@ GST_START_TEST (test_qtdemux_pad_names)
   gst_element_set_state (qtdemux_a, GST_STATE_NULL);
   gst_object_unref (qtdemux_a);
   g_free (expected_audio_pad_name);
+
+  unload_files ();
+}
+
+GST_END_TEST;
+
+typedef struct
+{
+  GstPad *sinkpad;
+  guint sample_cnt;
+  guint expected_sample_cnt;
+} MssModeTestData;
+
+static GstPadProbeReturn
+qtdemux_probe_for_mss_mode (GstPad * pad, GstPadProbeInfo * info,
+    MssModeTestData * data)
+{
+  data->sample_cnt++;
+  GST_LOG ("samples received: %u", data->sample_cnt);
+  return GST_PAD_PROBE_OK;
+}
+
+static void
+qtdemux_pad_added_cb_in_mss_mode (GstElement * element, GstPad * pad,
+    MssModeTestData * data)
+{
+  GST_DEBUG_OBJECT (pad, "New pad added");
+
+  if (!data->sinkpad) {
+    GstPad *sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+
+    gst_pad_set_event_function (sinkpad, _sink_event);
+    gst_pad_set_chain_function (sinkpad, _sink_chain);
+
+    gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_BUFFER,
+        (GstPadProbeCallback) qtdemux_probe_for_mss_mode, data, NULL);
+    gst_pad_set_active (sinkpad, TRUE);
+    data->sinkpad = sinkpad;
+    gst_pad_link (pad, sinkpad);
+  }
+}
+
+/* Fragment taken from
+ * http://amssamples.streaming.mediaservices.windows.net/b6822ec8-5c2b-4ae0-a851-fd46a78294e9/ElephantsDream.ism/QualityLevels(53644)/Fragments(AAC_und_ch2_56kbps=0)
+ */
+#define MSS_FRAGMENT TEST_FILE_PREFIX "mss-fragment.m4f"
+static guint8 *mss_fragment;
+static const guint mss_fragment_len = 14400;
+
+GST_START_TEST (test_qtdemux_compensate_data_offset)
+{
+  /* Same fragment as above, but with modified trun box data offset field
+   * from 871 to 791 to mimic an mss fragment with data offset smaller
+   * than the moof size. */
+  const guint mss_fragment_wrong_data_offset_len = 14400;
+  guint8 *mss_fragment_wrong_data_offset;
+  GstElement *qtdemux;
+  GstPad *sinkpad;
+  GstBuffer *inbuf;
+  GstSegment segment;
+  GstEvent *event;
+  GstCaps *caps;
+  GstCaps *mediacaps;
+  MssModeTestData data = { 0, };
+
+  data.expected_sample_cnt = 87;
+
+  g_assert (load_file (MSS_FRAGMENT, &mss_fragment, mss_fragment_len));
+
+  /* Change trun box data offset field from 871 to 791 to mimic an MSS fragment
+   * with data offset smaller than the moof size. */
+  mss_fragment_wrong_data_offset = g_memdup2 (mss_fragment, mss_fragment_len);
+  g_assert (GST_READ_UINT32_BE (&mss_fragment_wrong_data_offset[64]) == 871);
+  GST_WRITE_UINT32_BE (&mss_fragment_wrong_data_offset[64], 791);
+
+  /* The goal of this test is to check that qtdemux can compensate
+   * wrong data offset in trun boxes and allow proper parsing of samples
+   * in mss mode.
+   */
+
+  qtdemux = gst_element_factory_make ("qtdemux", NULL);
+  gst_element_set_state (qtdemux, GST_STATE_PLAYING);
+  sinkpad = gst_element_get_static_pad (qtdemux, "sink");
+
+  /* We'll want to know when the source pad is added */
+  g_signal_connect (qtdemux, "pad-added", (GCallback)
+      qtdemux_pad_added_cb_in_mss_mode, &data);
+
+  /* Send the initial STREAM_START and segment (TIME) event */
+  event = gst_event_new_stream_start ("TEST");
+  GST_DEBUG ("Pushing stream-start event");
+  fail_unless (gst_pad_send_event (sinkpad, event) == TRUE);
+
+  /* Send CAPS event* */
+  mediacaps = gst_caps_new_simple ("audio/mpeg",
+      "mpegversion", G_TYPE_INT, 4,
+      "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 48000, NULL);
+  caps =
+      gst_caps_new_simple ("video/quicktime", "variant", G_TYPE_STRING,
+      "mss-fragmented", "timescale", G_TYPE_UINT64,
+      G_GUINT64_CONSTANT (10000000), "media-caps", GST_TYPE_CAPS, mediacaps,
+      NULL);
+
+  /* Send segment event* */
+  event = gst_event_new_caps (caps);
+  GST_DEBUG ("Pushing caps event");
+  fail_unless (gst_pad_send_event (sinkpad, event) == TRUE);
+  gst_caps_unref (mediacaps);
+  gst_caps_unref (caps);
+
+  gst_segment_init (&segment, GST_FORMAT_TIME);
+  event = gst_event_new_segment (&segment);
+  GST_DEBUG ("Pushing segment event");
+  fail_unless (gst_pad_send_event (sinkpad, event) == TRUE);
+
+  /* Send the first fragment */
+  /* NOTE: mss streams don't have moov */
+  inbuf = gst_buffer_new_and_alloc (mss_fragment_wrong_data_offset_len);
+  gst_buffer_fill (inbuf, 0, mss_fragment_wrong_data_offset,
+      mss_fragment_wrong_data_offset_len);
+  GST_BUFFER_PTS (inbuf) = 0;
+  GST_BUFFER_OFFSET (inbuf) = 0;
+  GST_BUFFER_FLAG_SET (inbuf, GST_BUFFER_FLAG_DISCONT);
+  GST_DEBUG ("Pushing fragment");
+  fail_unless (gst_pad_chain (sinkpad, inbuf) == GST_FLOW_OK);
+  fail_if (data.sinkpad == NULL);
+
+  /* If data offset has been compensated samples will be pushed as normal */
+  fail_unless (data.sample_cnt == data.expected_sample_cnt);
+
+  gst_object_unref (sinkpad);
+  gst_pad_set_active (data.sinkpad, FALSE);
+  gst_object_unref (data.sinkpad);
+  gst_element_set_state (qtdemux, GST_STATE_NULL);
+  gst_object_unref (qtdemux);
+
+  g_free (mss_fragment_wrong_data_offset);
+  g_clear_pointer (&mss_fragment, (GDestroyNotify) g_free);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_qtdemux_mss_fragment)
+{
+  GstElement *qtdemux;
+  GstPad *sinkpad;
+  GstBuffer *inbuf;
+  GstSegment segment;
+  GstEvent *event;
+  GstCaps *caps;
+  GstCaps *mediacaps;
+  MssModeTestData data = { 0, };
+
+  data.expected_sample_cnt = 87;
+
+  g_assert (load_file (MSS_FRAGMENT, &mss_fragment, mss_fragment_len));
+
+  /* The goal of this test is to check that qtdemux can handle a normal
+   * mss fragment.
+   */
+
+  qtdemux = gst_element_factory_make ("qtdemux", NULL);
+  gst_element_set_state (qtdemux, GST_STATE_PLAYING);
+  sinkpad = gst_element_get_static_pad (qtdemux, "sink");
+
+  /* We'll want to know when the source pad is added */
+  g_signal_connect (qtdemux, "pad-added", (GCallback)
+      qtdemux_pad_added_cb_in_mss_mode, &data);
+
+  /* Send the initial STREAM_START and segment (TIME) event */
+  event = gst_event_new_stream_start ("TEST");
+  GST_DEBUG ("Pushing stream-start event");
+  fail_unless (gst_pad_send_event (sinkpad, event) == TRUE);
+
+  /* Send CAPS event* */
+  mediacaps = gst_caps_new_simple ("audio/mpeg",
+      "mpegversion", G_TYPE_INT, 4,
+      "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 48000, NULL);
+  caps =
+      gst_caps_new_simple ("video/quicktime", "variant", G_TYPE_STRING,
+      "mss-fragmented", "timescale", G_TYPE_UINT64,
+      G_GUINT64_CONSTANT (10000000), "media-caps", GST_TYPE_CAPS, mediacaps,
+      NULL);
+
+  /* Send segment event* */
+  event = gst_event_new_caps (caps);
+  GST_DEBUG ("Pushing caps event");
+  fail_unless (gst_pad_send_event (sinkpad, event) == TRUE);
+  gst_caps_unref (mediacaps);
+  gst_caps_unref (caps);
+
+  gst_segment_init (&segment, GST_FORMAT_TIME);
+  event = gst_event_new_segment (&segment);
+  GST_DEBUG ("Pushing segment event");
+  fail_unless (gst_pad_send_event (sinkpad, event) == TRUE);
+
+  /* Send the first fragment */
+  /* NOTE: mss streams don't have moov */
+  inbuf = gst_buffer_new_and_alloc (mss_fragment_len);
+  gst_buffer_fill (inbuf, 0, mss_fragment, mss_fragment_len);
+  GST_BUFFER_PTS (inbuf) = 0;
+  GST_BUFFER_OFFSET (inbuf) = 0;
+  GST_BUFFER_FLAG_SET (inbuf, GST_BUFFER_FLAG_DISCONT);
+  GST_DEBUG ("Pushing fragment");
+  fail_unless (gst_pad_chain (sinkpad, inbuf) == GST_FLOW_OK);
+  fail_if (data.sinkpad == NULL);
+
+  fail_unless (data.sample_cnt == data.expected_sample_cnt);
+
+  gst_object_unref (sinkpad);
+  gst_pad_set_active (data.sinkpad, FALSE);
+  gst_object_unref (data.sinkpad);
+  gst_element_set_state (qtdemux, GST_STATE_NULL);
+  gst_object_unref (qtdemux);
+
+  g_clear_pointer (&mss_fragment, (GDestroyNotify) g_free);
+}
+
+GST_END_TEST;
+
+typedef struct
+{
+  const gchar *filename;
+  /* Total number of AAC frames, including any and all dummy/empty/padding frames. */
+  guint num_aac_frames;
+  /* In AAC, this is 1024 in the vast majority of the cases.
+   * AAC can also use 960 samples per frame, but this is rare. */
+  guint num_samples_per_frame;
+  /* How many padding samples to expect at the beginning and the end.
+   * The amount of padding samples can exceed the size of a frame.
+   * This means that the first and last N frame(s) can actually be
+   * fully made of padding samples and thus need to be thrown away. */
+  guint num_start_padding_samples;
+  guint num_end_padding_samples;
+  guint sample_rate;
+  /* Some encoders produce data whose last frame uses a different
+   * (smaller) stts value to handle the padding at the end. Data
+   * produced by such encoders will not get a clipmeta added at the
+   * end. When using test data produced by such an encoder, this
+   * must be set to FALSE, otherwise it must be set to TRUE.
+   * Notably, anything that produces an iTunSMPB tag (iTunes itself
+   * as well as newer Nero encoders for example) will cause such
+   * a clipmeta to be added. */
+  gboolean expect_clipmeta_at_end;
+
+  /* Total number of samples available, with / without padding
+   * samples factored in. */
+  guint64 num_samples_with_padding;
+  guint64 num_samples_without_padding;
+
+  /* The index of the first / last frame that contains valid samples.
+   * Indices start with 0. Valid range is [0 , (num_aac_frames-1)].
+   * In virtually all cases, when the AAC data was encoded with iTunes,
+   * the first and last valid frames will be partially clipped. */
+  guint first_frame_with_valid_samples;
+  guint last_frame_with_valid_samples;
+
+  guint64 num_samples_in_first_valid_frame;
+  guint64 num_samples_in_last_valid_frame;
+
+  GstClockTime total_duration_without_padding;
+
+  GstElement *appsink;
+} GaplessTestInfo;
+
+static void
+precalculate_gapless_test_factors (GaplessTestInfo * info)
+{
+  info->num_samples_with_padding = info->num_aac_frames *
+      info->num_samples_per_frame;
+  info->num_samples_without_padding = info->num_samples_with_padding -
+      info->num_start_padding_samples - info->num_end_padding_samples;
+
+  info->first_frame_with_valid_samples = info->num_start_padding_samples /
+      info->num_samples_per_frame;
+  info->last_frame_with_valid_samples = (info->num_samples_with_padding -
+      info->num_end_padding_samples) / info->num_samples_per_frame;
+
+  info->num_samples_in_first_valid_frame =
+      (info->first_frame_with_valid_samples + 1) * info->num_samples_per_frame -
+      info->num_start_padding_samples;
+  info->num_samples_in_last_valid_frame =
+      (info->num_samples_with_padding - info->num_end_padding_samples) -
+      info->last_frame_with_valid_samples * info->num_samples_per_frame;
+
+  /* The total actual playtime duration. */
+  info->total_duration_without_padding =
+      gst_util_uint64_scale_int (info->num_samples_without_padding, GST_SECOND,
+      info->sample_rate);
+
+  GST_DEBUG ("num_samples_with_padding %" G_GUINT64_FORMAT
+      " num_samples_without_padding %" G_GUINT64_FORMAT
+      " first_frame_with_valid_samples %u"
+      " last_frame_with_valid_samples %u"
+      " num_samples_in_first_valid_frame %" G_GUINT64_FORMAT
+      " num_samples_in_last_valid_frame %" G_GUINT64_FORMAT
+      " total_duration_without_padding %" G_GUINT64_FORMAT,
+      info->num_samples_with_padding, info->num_samples_without_padding,
+      info->first_frame_with_valid_samples, info->last_frame_with_valid_samples,
+      info->num_samples_in_first_valid_frame,
+      info->num_samples_in_last_valid_frame,
+      info->total_duration_without_padding);
+}
+
+static void
+setup_gapless_itunes_test_info (GaplessTestInfo * info)
+{
+  info->filename =
+      "sine-1kHztone-48kHzrate-mono-s32le-200000samples-itunes.m4a";
+  info->num_aac_frames = 198;
+  info->num_samples_per_frame = 1024;
+  info->sample_rate = 48000;
+  info->expect_clipmeta_at_end = TRUE;
+
+  info->num_start_padding_samples = 2112;
+  info->num_end_padding_samples = 640;
+
+  precalculate_gapless_test_factors (info);
+}
+
+static void
+setup_gapless_nero_with_itunsmpb_test_info (GaplessTestInfo * info)
+{
+  info->filename =
+      "sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-with-itunsmpb.m4a";
+  info->num_aac_frames = 198;
+  info->num_samples_per_frame = 1024;
+  info->sample_rate = 48000;
+  info->expect_clipmeta_at_end = TRUE;
+
+  info->num_start_padding_samples = 2624;
+  info->num_end_padding_samples = 128;
+
+  precalculate_gapless_test_factors (info);
+}
+
+static void
+setup_gapless_nero_without_itunsmpb_test_info (GaplessTestInfo * info)
+{
+  info->filename =
+      "sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-without-itunsmpb.m4a";
+  info->num_aac_frames = 198;
+  info->num_samples_per_frame = 1024;
+  info->sample_rate = 48000;
+  /* Older Nero AAC encoders produce a different stts value for the
+   * last frame to skip padding data. In this file, all frames except
+   * the last one use an stts value of 1024, while the last value
+   * uses an stts value of 896. Consequently, the logic inside qtdemux
+   * won't deem it necessary to add an audioclipmeta - there are no
+   * padding samples to clip. */
+  info->expect_clipmeta_at_end = FALSE;
+
+  info->num_start_padding_samples = 2624;
+  info->num_end_padding_samples = 128;
+
+  precalculate_gapless_test_factors (info);
+}
+
+static void
+check_parsed_aac_frame (GaplessTestInfo * info, guint frame_num)
+{
+  GstClockTime expected_pts = GST_CLOCK_TIME_NONE;
+  GstClockTime expected_duration = GST_CLOCK_TIME_NONE;
+  GstClockTimeDiff ts_delta;
+  guint64 expected_sample_offset;
+  guint64 expected_num_samples;
+  gboolean expect_audioclipmeta = FALSE;
+  guint64 expected_audioclipmeta_start = 0;
+  guint64 expected_audioclipmeta_end = 0;
+  GstSample *sample;
+  GstBuffer *buffer;
+  GstAudioClippingMeta *audioclip_meta;
+
+  if (frame_num < info->first_frame_with_valid_samples) {
+    /* Frame is at the beginning and is fully clipped. */
+    expected_sample_offset = 0;
+    expected_num_samples = 0;
+
+    expected_audioclipmeta_start = info->num_samples_per_frame;
+    expected_audioclipmeta_end = 0;
+  } else if (frame_num == info->first_frame_with_valid_samples) {
+    /* Frame is at the beginning and is partially clipped. */
+
+    expected_sample_offset = 0;
+    expected_num_samples = info->num_samples_in_first_valid_frame;
+
+    expected_audioclipmeta_start = info->num_samples_per_frame -
+        info->num_samples_in_first_valid_frame;
+    expected_audioclipmeta_end = 0;
+  } else if (frame_num < info->last_frame_with_valid_samples) {
+    /* Regular, unclipped frame. */
+
+    expected_sample_offset = info->num_samples_in_first_valid_frame +
+        info->num_samples_per_frame * (frame_num -
+        info->first_frame_with_valid_samples - 1);
+    expected_num_samples = info->num_samples_per_frame;
+  } else if (frame_num == info->last_frame_with_valid_samples) {
+    /* The first frame at the end with padding samples. This one will have
+     * the last few valid samples, followed by the first padding samples. */
+
+    expected_sample_offset = info->num_samples_in_first_valid_frame +
+        info->num_samples_per_frame * (frame_num -
+        info->first_frame_with_valid_samples - 1);
+    expected_num_samples = info->num_samples_in_last_valid_frame;
+
+    if (info->expect_clipmeta_at_end) {
+      expect_audioclipmeta = TRUE;
+      expected_audioclipmeta_start = 0;
+      expected_audioclipmeta_end =
+          info->num_samples_per_frame - expected_num_samples;
+    }
+  } else {
+    /* A fully clipped frame at the end of the stream. */
+
+    expected_sample_offset = info->num_samples_in_first_valid_frame +
+        info->num_samples_without_padding;
+    expected_num_samples = 0;
+
+    if (info->expect_clipmeta_at_end) {
+      expect_audioclipmeta = TRUE;
+      expected_audioclipmeta_start = 0;
+      expected_audioclipmeta_end = info->num_samples_per_frame;
+    }
+  }
+
+  /* Pull the frame from appsink so we can check it. */
+
+  sample = gst_app_sink_pull_sample (GST_APP_SINK (info->appsink));
+  fail_if (sample == NULL);
+  fail_unless (GST_IS_SAMPLE (sample));
+
+  expected_pts = gst_util_uint64_scale_int (expected_sample_offset,
+      GST_SECOND, info->sample_rate);
+  expected_duration = gst_util_uint64_scale_int (expected_num_samples,
+      GST_SECOND, info->sample_rate);
+
+  buffer = gst_sample_get_buffer (sample);
+  fail_if (buffer == NULL);
+
+  /* Verify the sample's PTS and duration. Allow for 1 nanosecond difference
+   * to account for rounding errors in sample <-> timestamp conversions. */
+  ts_delta = GST_CLOCK_DIFF (GST_BUFFER_PTS (buffer), expected_pts);
+  fail_unless (ABS (ts_delta) <= 1);
+  ts_delta = GST_CLOCK_DIFF (GST_BUFFER_DURATION (buffer), expected_duration);
+  fail_unless (ABS (ts_delta) <= 1);
+  /* Check if there's audio clip metadata, and verify it if it exists. */
+  if (expect_audioclipmeta) {
+    audioclip_meta = gst_buffer_get_audio_clipping_meta (buffer);
+    fail_if (audioclip_meta == NULL);
+    fail_unless_equals_uint64 (audioclip_meta->start,
+        expected_audioclipmeta_start);
+    fail_unless_equals_uint64 (audioclip_meta->end, expected_audioclipmeta_end);
+  }
+
+  gst_sample_unref (sample);
+}
+
+static void
+qtdemux_pad_added_cb_for_gapless (GstElement * demux, GstPad * pad,
+    GaplessTestInfo * info)
+{
+  GstPad *appsink_pad;
+  GstPadLinkReturn ret;
+
+  appsink_pad = gst_element_get_static_pad (info->appsink, "sink");
+
+  if (gst_pad_is_linked (appsink_pad))
+    goto finish;
+
+  ret = gst_pad_link (pad, appsink_pad);
+  if (GST_PAD_LINK_FAILED (ret)) {
+    GST_ERROR ("Could not link qtdemux and appsink: %s",
+        gst_pad_link_get_name (ret));
+  }
+
+finish:
+  gst_object_unref (GST_OBJECT (appsink_pad));
+}
+
+static void
+switch_state_with_async_wait (GstElement * pipeline, GstState state)
+{
+  GstStateChangeReturn state_ret;
+
+  state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+  if (state_ret == GST_STATE_CHANGE_ASYNC) {
+    GST_LOG ("waiting for pipeline to reach %s state",
+        gst_element_state_get_name (state));
+    state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+    fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+  }
+}
+
+static void
+perform_gapless_test (GaplessTestInfo * info)
+{
+  GstElement *source, *demux, *appsink, *pipeline;
+  guint frame_num;
+
+  pipeline = gst_pipeline_new (NULL);
+  source = gst_element_factory_make ("filesrc", NULL);
+  demux = gst_element_factory_make ("qtdemux", NULL);
+  appsink = gst_element_factory_make ("appsink", NULL);
+
+  info->appsink = appsink;
+
+  g_signal_connect (demux, "pad-added", (GCallback)
+      qtdemux_pad_added_cb_for_gapless, info);
+
+  gst_bin_add_many (GST_BIN (pipeline), source, demux, appsink, NULL);
+  gst_element_link (source, demux);
+
+  {
+    char *full_filename =
+        g_build_filename (GST_TEST_FILES_PATH, info->filename, NULL);
+    g_object_set (G_OBJECT (source), "location", full_filename, NULL);
+    g_free (full_filename);
+  }
+
+  g_object_set (G_OBJECT (appsink), "sync", FALSE, NULL);
+
+  switch_state_with_async_wait (pipeline, GST_STATE_PLAYING);
+
+  /* Verify all frames from the test signal. */
+  for (frame_num = 0; frame_num < info->num_aac_frames; ++frame_num)
+    check_parsed_aac_frame (info, frame_num);
+
+  /* Check what duration is returned by a query. This duration must exclude
+   * the padding samples. */
+  {
+    GstQuery *query;
+    gint64 duration;
+    GstFormat format;
+
+    query = gst_query_new_duration (GST_FORMAT_TIME);
+    fail_unless (gst_element_query (pipeline, query));
+
+    gst_query_parse_duration (query, &format, &duration);
+    fail_unless_equals_int (format, GST_FORMAT_TIME);
+    fail_unless_equals_uint64 ((guint64) duration,
+        info->total_duration_without_padding);
+
+    gst_query_unref (query);
+  }
+
+  /* Seek tests: Here we seek to a certain position that corresponds to a
+   * certain frame. Then we check if we indeed got that frame. */
+
+  /* Seek back to the first frame. This will _not_ be the first valid frame.
+   * Instead, it will be a frame that gets only decoded and has duration
+   * zero. Other zero-duration frames may follow, until the first frame
+   * with valid data is encountered. This means that when the user seeks
+   * to position 0, downstream will subsequently get a number of buffers
+   * with PTS 0, and all of those buffers except the last will have a
+   * duration of 0. */
+  {
+    switch_state_with_async_wait (pipeline, GST_STATE_PAUSED);
+    gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0);
+    switch_state_with_async_wait (pipeline, GST_STATE_PLAYING);
+
+    check_parsed_aac_frame (info, 0);
+  }
+
+  /* Now move to the frame past the very first one that contained valid samples.
+   * This very first frame will usually be clipped, and be output as the last
+   * buffer at PTS 0 (see above). */
+  {
+    GstClockTime position;
+
+    position =
+        gst_util_uint64_scale_int (info->num_samples_in_first_valid_frame,
+        GST_SECOND, info->sample_rate);
+
+    switch_state_with_async_wait (pipeline, GST_STATE_PAUSED);
+    gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+        position);
+    switch_state_with_async_wait (pipeline, GST_STATE_PLAYING);
+
+    check_parsed_aac_frame (info, info->first_frame_with_valid_samples + 1);
+  }
+
+  /* Seek to the last frame with valid samples (= the first frame with padding
+   * samples at the end of the stream). */
+  {
+    GstClockTime position;
+
+    position =
+        gst_util_uint64_scale_int (info->num_samples_in_first_valid_frame +
+        info->num_samples_without_padding - info->num_samples_per_frame,
+        GST_SECOND, info->sample_rate);
+
+    switch_state_with_async_wait (pipeline, GST_STATE_PAUSED);
+    gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+        position);
+    switch_state_with_async_wait (pipeline, GST_STATE_PLAYING);
+
+    check_parsed_aac_frame (info, info->last_frame_with_valid_samples);
+  }
+
+  gst_element_set_state (pipeline, GST_STATE_NULL);
+  gst_object_unref (pipeline);
+}
+
+GST_START_TEST (test_qtdemux_gapless_itunes_data)
+{
+  GaplessTestInfo info;
+  setup_gapless_itunes_test_info (&info);
+  perform_gapless_test (&info);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_qtdemux_gapless_nero_data_with_itunsmpb)
+{
+  GaplessTestInfo info;
+  setup_gapless_nero_with_itunsmpb_test_info (&info);
+  perform_gapless_test (&info);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_qtdemux_gapless_nero_data_without_itunsmpb)
+{
+  GaplessTestInfo info;
+  setup_gapless_nero_without_itunsmpb_test_info (&info);
+  perform_gapless_test (&info);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_qtdemux_editlist)
+{
+  const gsize editlist_mp4_size = 5322593;
+  guint8 *editlist_mp4 = NULL;
+  GstElement *src, *sink, *pipe;
+  GstSample *sample;
+  guint frame_count = 0;
+
+  {
+    GZlibDecompressor *decompress;
+    GConverterResult decomp_res;
+    gsize bytes_read, gz_size, mp4_size;
+    guint8 *gz_gz = NULL;
+    guint8 gz[8705];
+
+    /* read .mp4.gz.gz */
+    g_assert (load_file (TEST_FILE_PREFIX "editlists.mp4.gz.gz", &gz_gz, 3597));
+
+    /* mp4.gz.gz -> mp4.gz */
+    decompress = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
+    decomp_res = g_converter_convert (G_CONVERTER (decompress), gz_gz, 3597,
+        gz, 8705, G_CONVERTER_INPUT_AT_END, &bytes_read, &gz_size, NULL);
+    fail_unless_equals_int (decomp_res, G_CONVERTER_FINISHED);
+    fail_unless_equals_int (bytes_read, 3597);
+    fail_unless_equals_int (gz_size, 8705);
+    g_object_unref (decompress);
+    g_clear_pointer (&gz_gz, (GDestroyNotify) g_free);
+
+    editlist_mp4 = g_malloc0 (editlist_mp4_size);
+
+    /* mp4.gz -> mp4 */
+    decompress = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
+    decomp_res = g_converter_convert (G_CONVERTER (decompress), gz, 8705,
+        editlist_mp4, editlist_mp4_size, G_CONVERTER_INPUT_AT_END, &bytes_read,
+        &mp4_size, NULL);
+    fail_unless_equals_int (decomp_res, G_CONVERTER_FINISHED);
+    fail_unless_equals_int (bytes_read, 8705);
+    fail_unless_equals_int (mp4_size, editlist_mp4_size);
+    g_object_unref (decompress);
+  }
+
+  fail_unless_equals_int (editlist_mp4[28 + 4], 'm');
+  fail_unless_equals_int (editlist_mp4[28 + 5], 'd');
+  fail_unless_equals_int (editlist_mp4[28 + 6], 'a');
+  fail_unless_equals_int (editlist_mp4[28 + 7], 't');
+
+  pipe = gst_parse_launch ("dataurisrc name=src ! qtdemux name=d "
+      "d.video_0 ! appsink name=sink", NULL);
+
+  fail_unless (pipe != NULL);
+
+  src = gst_bin_get_by_name (GST_BIN (pipe), "src");
+  fail_unless (src != NULL);
+
+  sink = gst_bin_get_by_name (GST_BIN (pipe), "sink");
+  fail_unless (sink != NULL);
+
+  /* Convert to data: URI so we can use dataurisrc. Bit silly of course,
+   * should have a memsrc or somesuch, but does the job for now */
+  {
+    gsize s_alloc_len = 32 + (editlist_mp4_size / 3 + 1) * 4 + 4;
+    gchar *s = g_malloc0 (s_alloc_len);
+    gsize s_len = 0;
+    gsize base64_size;
+    gint state = 0;
+    gint save = 0;
+
+    s_len = g_strlcat (s, "data:video/quicktime;base64,", s_alloc_len);
+
+    base64_size =
+        g_base64_encode_step (editlist_mp4, editlist_mp4_size, FALSE, s + s_len,
+        &state, &save);
+    s_len += base64_size;
+    base64_size = g_base64_encode_close (FALSE, s + s_len, &state, &save);
+    s_len += base64_size;
+    g_clear_pointer (&editlist_mp4, (GDestroyNotify) g_free);
+
+    {
+      GValue v = G_VALUE_INIT;
+
+      /* Avoids at least one of the two string copies */
+      g_value_init (&v, G_TYPE_STRING);
+      g_value_take_string (&v, s);
+      g_object_set_property (G_OBJECT (src), "uri", &v);
+      g_value_reset (&v);
+    }
+  }
+
+  g_object_set (sink, "sync", FALSE, NULL);
+
+  gst_element_set_state (pipe, GST_STATE_PLAYING);
+
+  /* wait for preroll */
+  {
+    GstMessage *msg;
+
+    GST_LOG ("waiting for preroll");
+    msg =
+        gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe), -1,
+        GST_MESSAGE_ASYNC_DONE);
+
+    gst_message_unref (msg);
+  }
+
+  /* pull video frames out of qtdemux */
+  while ((sample = gst_app_sink_pull_sample (GST_APP_SINK (sink)))) {
+    ++frame_count;
+    gst_sample_unref (sample);
+  }
+
+  fail_unless_equals_int (frame_count, 361);
+
+  gst_element_set_state (pipe, GST_STATE_NULL);
+
+  gst_clear_object (&src);
+  gst_clear_object (&sink);
+  gst_clear_object (&pipe);
 }
 
 GST_END_TEST;
@@ -901,6 +1752,12 @@ qtdemux_suite (void)
   tcase_add_test (tc_chain, test_qtdemux_duplicated_moov);
   tcase_add_test (tc_chain, test_qtdemux_stream_change);
   tcase_add_test (tc_chain, test_qtdemux_pad_names);
+  tcase_add_test (tc_chain, test_qtdemux_compensate_data_offset);
+  tcase_add_test (tc_chain, test_qtdemux_mss_fragment);
+  tcase_add_test (tc_chain, test_qtdemux_gapless_itunes_data);
+  tcase_add_test (tc_chain, test_qtdemux_gapless_nero_data_with_itunsmpb);
+  tcase_add_test (tc_chain, test_qtdemux_gapless_nero_data_without_itunsmpb);
+  tcase_add_test (tc_chain, test_qtdemux_editlist);
 
   return s;
 }
diff --git a/tests/check/elements/qtdemux.h b/tests/check/elements/qtdemux.h
deleted file mode 100644
index 85977ed4cbe6b95d0b7f3d7aed1c5c24306ca703..0000000000000000000000000000000000000000
--- a/tests/check/elements/qtdemux.h
+++ /dev/null
@@ -1,5033 +0,0 @@
-/* GStreamer
- *
- * unit test for qtdemux
- *
- * Copyright (C) <2016> Edward Hervey <edward@centricular.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib/gstdio.h>
-
-#include <gst/check/gstcheck.h>
-
-/* Fragments taken from http://www.bok.net/dash/tears_of_steel/cleartext/stream.mpd
- *
- * Audio stream (aac)
- * Header + first fragment
- */
-
-/* http://www.bok.net/dash/tears_of_steel/cleartext/audio/en/init.mp4 */
-static guint8 init_mp4[] = {
-  0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d,
-  0x00, 0x00, 0x02, 0x00, 0x69, 0x73, 0x6f, 0x6d, 0x69, 0x73, 0x6f, 0x32,
-  0x61, 0x76, 0x63, 0x31, 0x6d, 0x70, 0x34, 0x31, 0x00, 0x00, 0x02, 0x50,
-  0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c, 0x6d, 0x76, 0x68, 0x64,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x03, 0xe8, 0x00, 0x0b, 0x33, 0xd7, 0x00, 0x01, 0x00, 0x00,
-  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0xa4, 0x74, 0x72, 0x61, 0x6b,
-  0x00, 0x00, 0x00, 0x5c, 0x74, 0x6b, 0x68, 0x64, 0x00, 0x00, 0x00, 0x07,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x33, 0xbc, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40,
-  0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20, 0x6d, 0x64, 0x68, 0x64,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0xac, 0x44, 0x00, 0x00, 0x00, 0x00, 0x15, 0xc7, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x35, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x73, 0x6f, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x65, 0x6e, 0x74,
-  0x6f, 0x34, 0x20, 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x48, 0x61, 0x6e,
-  0x64, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x6d, 0x69, 0x6e,
-  0x66, 0x00, 0x00, 0x00, 0x10, 0x73, 0x6d, 0x68, 0x64, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e,
-  0x66, 0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c,
-  0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa7, 0x73, 0x74, 0x62,
-  0x6c, 0x00, 0x00, 0x00, 0x5b, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4b, 0x6d, 0x70, 0x34,
-  0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
-  0x00, 0xac, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x65, 0x73, 0x64,
-  0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x19, 0x00, 0x00, 0x00, 0x04, 0x11,
-  0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x01, 0x00, 0x01, 0xf4,
-  0x01, 0x05, 0x02, 0x12, 0x10, 0x06, 0x01, 0x02, 0x00, 0x00, 0x00, 0x14,
-  0x73, 0x74, 0x73, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x73, 0x63,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
-  0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6d, 0x76, 0x65, 0x78,
-  0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x0b, 0x33, 0xd7, 0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x65, 0x78,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-guint init_mp4_len = 624;
-
-/* http://www.bok.net/dash/tears_of_steel/cleartext/audio/en/seg-1.m4f */
-static const guint8 seg_1_m4f[] = {
-  0x00, 0x00, 0x04, 0x60, 0x6d, 0x6f, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x10,
-  0x6d, 0x66, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-  0x00, 0x00, 0x04, 0x48, 0x74, 0x72, 0x61, 0x66, 0x00, 0x00, 0x00, 0x10,
-  0x74, 0x66, 0x68, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-  0x00, 0x00, 0x00, 0x14, 0x74, 0x66, 0x64, 0x74, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1c,
-  0x74, 0x72, 0x75, 0x6e, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x81,
-  0x00, 0x00, 0x04, 0x68, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x73,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xdd, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x12,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xe9, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xce, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xb9,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xa5, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xa4, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x9a,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x92, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x8e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x7d,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x7d, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x82, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x82,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x72, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x6a,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x63,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x55,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x6a,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x4d, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x9f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x82,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x82,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x94,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x69, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x6e,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x69, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x86,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x6e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x67,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x5d,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x89,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x81, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x7d,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x59, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x9e,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x91,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x87, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x4d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x53,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xa7, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x57, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xbd,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xa9, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xa6, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x9f,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x96, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x8b,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xba,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x87, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x81,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x25,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x35,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x67, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xab,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xa4, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x97,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x6a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xa3,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x5d, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x83, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x46,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x6f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x58,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xa9, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x7b,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x93, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x8d,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x7c,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x75, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x57,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xb4, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x67,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x69, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x6e,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0xa3, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x4b,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x91, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x7e,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x9b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x6c,
-  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x52, 0x00, 0x00, 0x04, 0x00,
-  0x00, 0x00, 0x01, 0x59, 0x00, 0x00, 0xbd, 0x32, 0x6d, 0x64, 0x61, 0x74,
-  0x21, 0x11, 0x45, 0x00, 0x14, 0x50, 0x01, 0x46, 0xff, 0xf1, 0x0a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5d, 0xe9, 0x82, 0x14, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xbc, 0x21,
-  0x1a, 0x93, 0xa9, 0x04, 0x3e, 0xa3, 0x59, 0x41, 0x67, 0xa6, 0x1c, 0x02,
-  0x81, 0x4a, 0x3d, 0x3e, 0x5d, 0x30, 0x1f, 0x5b, 0x59, 0x41, 0x49, 0xde,
-  0x22, 0x1e, 0x81, 0x0b, 0x3f, 0xf6, 0xff, 0xf1, 0x0a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5d, 0xe6, 0xc2, 0x14, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
-  0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xbc, 0x21,
-  0x1a, 0x93, 0x85, 0x9a, 0x92, 0xc6, 0x41, 0x30, 0x91, 0x8c, 0x45, 0x08,
-  0xa4, 0xc5, 0x45, 0x20, 0xcc, 0xd6, 0x22, 0x84, 0xa1, 0x02, 0x53, 0x1a,
-  0xc8, 0x9a, 0x50, 0x21, 0x89, 0xfe, 0x70, 0x24, 0x76, 0x64, 0x5c, 0xee,
-  0x1e, 0x76, 0x20, 0x03, 0x90, 0x52, 0x88, 0x20, 0xb8, 0xf0, 0x58, 0xf0,
-  0x1f, 0xd8, 0xeb, 0x14, 0x9b, 0x1f, 0xdf, 0xf1, 0xe8, 0xce, 0xa8, 0xf3,
-  0x93, 0xa7, 0x34, 0x9a, 0x26, 0x92, 0x9b, 0xb7, 0xbe, 0x40, 0xcb, 0xf0,
-  0xcc, 0x8f, 0x36, 0x98, 0x48, 0x18, 0xe6, 0x17, 0x61, 0x56, 0x23, 0x0e,
-  0x33, 0x2a, 0xc2, 0x32, 0x01, 0x57, 0xdb, 0x6e, 0x05, 0x22, 0x56, 0xcd,
-  0x69, 0x9b, 0xb6, 0x2e, 0x12, 0xbf, 0x4a, 0x76, 0x24, 0x60, 0xc0, 0xe8,
-  0xdd, 0x2b, 0x57, 0x4d, 0x2e, 0xd3, 0x18, 0x4e, 0x10, 0x64, 0xcb, 0xb6,
-  0x74, 0xb0, 0x9b, 0xd4, 0xd9, 0x83, 0x11, 0xb6, 0x14, 0xb8, 0xee, 0xc0,
-  0xde, 0x35, 0xc5, 0xb4, 0x1e, 0x52, 0xa2, 0xa6, 0x74, 0x07, 0x25, 0x9d,
-  0xa7, 0x52, 0xa4, 0xc6, 0x45, 0x03, 0xad, 0xb4, 0x03, 0xd4, 0x0b, 0xc6,
-  0x12, 0x4b, 0xdf, 0x5d, 0x64, 0xea, 0xea, 0xda, 0x84, 0x00, 0x6f, 0x46,
-  0xe4, 0x39, 0x16, 0xf2, 0xca, 0x8f, 0xd0, 0x29, 0x0d, 0xee, 0x47, 0x32,
-  0xbe, 0x51, 0x6b, 0xef, 0x59, 0x15, 0xe5, 0x13, 0x0b, 0x77, 0x92, 0x15,
-  0x7f, 0x04, 0x5d, 0xea, 0xca, 0xc0, 0x40, 0x39, 0xa5, 0x12, 0x15, 0x7d,
-  0xea, 0x12, 0xe7, 0x25, 0xf2, 0x94, 0xb1, 0x2e, 0x7d, 0x57, 0xdc, 0x99,
-  0xae, 0xa2, 0xea, 0x33, 0xba, 0xa1, 0xcb, 0xc1, 0x54, 0x3b, 0x06, 0xe3,
-  0x34, 0x50, 0xba, 0x89, 0xec, 0xaa, 0xec, 0x47, 0xe6, 0xdc, 0x3b, 0xfa,
-  0x1a, 0xe1, 0xe9, 0xc7, 0x46, 0x40, 0x1c, 0xa1, 0x89, 0x98, 0x23, 0x84,
-  0x6e, 0xe8, 0xba, 0x8c, 0xcb, 0x01, 0x48, 0xa8, 0x28, 0x16, 0xa8, 0x6b,
-  0x22, 0x69, 0x40, 0x86, 0x20, 0x79, 0x83, 0x21, 0x7b, 0x6f, 0x6d, 0xb4,
-  0xda, 0x6e, 0x31, 0x2a, 0x26, 0x12, 0xa5, 0xb7, 0x4a, 0x91, 0x30, 0x55,
-  0xb4, 0xe6, 0x99, 0x6a, 0xa6, 0x12, 0x1f, 0x72, 0xdb, 0x65, 0xdc, 0xbe,
-  0x17, 0x15, 0x6b, 0xc0, 0x9e, 0x55, 0x20, 0x8c, 0xf5, 0x27, 0x3c, 0x09,
-  0x42, 0xcc, 0x78, 0x8d, 0x24, 0x37, 0xb9, 0x45, 0xdd, 0x14, 0xa5, 0x99,
-  0xde, 0x15, 0xa0, 0x5a, 0x3e, 0xfe, 0x8c, 0xd7, 0x2a, 0x14, 0x52, 0xef,
-  0x6e, 0x87, 0x9c, 0x22, 0x9d, 0x08, 0x65, 0x59, 0xb5, 0x60, 0x78, 0xfd,
-  0xc3, 0x2e, 0x6a, 0x88, 0x13, 0xc2, 0x4f, 0x72, 0x6c, 0x92, 0x02, 0x4c,
-  0x88, 0xd4, 0xc4, 0x80, 0xc1, 0x48, 0x56, 0x4c, 0x39, 0x98, 0x5d, 0x9d,
-  0x31, 0x35, 0x81, 0x47, 0x72, 0x4d, 0xe5, 0x77, 0x54, 0xf8, 0x88, 0xe4,
-  0x07, 0xad, 0xa1, 0xa6, 0xa1, 0xe9, 0x69, 0x16, 0xad, 0x33, 0x88, 0x86,
-  0xee, 0x18, 0xea, 0x37, 0xca, 0x8d, 0xd8, 0x5f, 0x95, 0x04, 0xc3, 0x55,
-  0x76, 0x45, 0xf7, 0xd5, 0x9c, 0x32, 0x4e, 0x80, 0x80, 0xd0, 0x9c, 0x41,
-  0xa7, 0x54, 0xbc, 0x36, 0x90, 0xb3, 0xcf, 0xc0, 0x70, 0x46, 0x45, 0x34,
-  0xca, 0x93, 0x3c, 0x6b, 0xa6, 0xed, 0x40, 0x8a, 0x98, 0x29, 0x12, 0x78,
-  0xd9, 0x6c, 0x37, 0x04, 0xad, 0xf3, 0x09, 0x6e, 0x6e, 0xce, 0xe9, 0x1c,
-  0x29, 0x05, 0x06, 0x08, 0x63, 0x7e, 0x43, 0x3a, 0x2e, 0x2e, 0xef, 0x85,
-  0xb7, 0x89, 0xc0, 0xf5, 0xf5, 0x72, 0x90, 0x1c, 0x21, 0x1a, 0x88, 0x07,
-  0xff, 0xff, 0xff, 0xfe, 0xef, 0x66, 0xa3, 0x31, 0x11, 0x6c, 0x44, 0x13,
-  0x12, 0x88, 0xe1, 0x0e, 0x18, 0xe3, 0x70, 0xa0, 0x14, 0x2a, 0xe9, 0x28,
-  0x82, 0x44, 0xe4, 0x68, 0x38, 0x21, 0x08, 0x2c, 0xa4, 0xd9, 0x1e, 0xc4,
-  0x79, 0x28, 0x10, 0x2e, 0xe4, 0x78, 0x52, 0x24, 0x21, 0x11, 0x83, 0xf6,
-  0xba, 0xd5, 0xa8, 0xe4, 0xbc, 0xf1, 0x3c, 0x65, 0x25, 0x7a, 0xb4, 0x5c,
-  0x89, 0xce, 0x14, 0xb2, 0x8e, 0x93, 0x52, 0xf3, 0x82, 0x4c, 0x4d, 0x42,
-  0x68, 0xb5, 0xc8, 0x15, 0xea, 0xd1, 0x1d, 0xa6, 0xf3, 0x83, 0xf4, 0x1f,
-  0x18, 0x97, 0xb7, 0x98, 0xfd, 0x9c, 0x13, 0x5c, 0x90, 0xe1, 0x7d, 0x88,
-  0xaf, 0x4f, 0x16, 0x8e, 0x7e, 0xfe, 0x99, 0x74, 0x80, 0x25, 0xea, 0x96,
-  0x13, 0x20, 0x3d, 0xdf, 0x79, 0x3e, 0x93, 0xdb, 0x74, 0x9a, 0x11, 0x4e,
-  0xe9, 0xd8, 0x40, 0x1a, 0x1c, 0x0c, 0x04, 0x44, 0x8e, 0x1e, 0x86, 0x34,
-  0x42, 0x68, 0x68, 0x0e, 0xf9, 0xc2, 0x12, 0x4b, 0x0e, 0x57, 0x10, 0xd4,
-  0x80, 0xc2, 0xd0, 0xe8, 0x8c, 0xc2, 0xb2, 0x97, 0x99, 0xdd, 0xcd, 0x08,
-  0xaa, 0x6b, 0x46, 0xab, 0x6c, 0xaf, 0x97, 0x4b, 0x6f, 0x8b, 0x62, 0x81,
-  0x25, 0xed, 0x44, 0x90, 0x2b, 0x71, 0xdb, 0x12, 0x4c, 0x6d, 0x08, 0xb5,
-  0x8b, 0x4b, 0x12, 0x92, 0x28, 0x83, 0xbb, 0xb4, 0x54, 0x2d, 0x17, 0xbc,
-  0x25, 0x67, 0xb5, 0xf2, 0x06, 0x20, 0x8d, 0x47, 0x7d, 0x5d, 0x23, 0x89,
-  0xa8, 0x9d, 0x94, 0x6e, 0x3c, 0xc9, 0x46, 0x12, 0x60, 0x46, 0x2c, 0x4c,
-  0xb6, 0xeb, 0x4b, 0x51, 0xf6, 0xd2, 0x4d, 0x7c, 0x7d, 0x07, 0x49, 0xb5,
-  0x30, 0xc0, 0xc3, 0x53, 0x62, 0x1f, 0xf4, 0xa2, 0xb6, 0x76, 0xd2, 0x35,
-  0xd1, 0xaa, 0xb1, 0xf5, 0x9a, 0x8c, 0x83, 0xde, 0x6c, 0xa9, 0x35, 0xbc,
-  0xcf, 0x2d, 0xad, 0x2b, 0xf1, 0xf7, 0x79, 0xff, 0x48, 0x1f, 0xb1, 0xfa,
-  0x0e, 0x2e, 0x07, 0x57, 0x7c, 0xed, 0x76, 0x70, 0xf9, 0x7a, 0xfc, 0x9e,
-  0x17, 0x9c, 0x01, 0xdb, 0xd0, 0xc6, 0x46, 0xb0, 0x50, 0x6c, 0x14, 0x1b,
-  0x0a, 0x88, 0xe1, 0xc0, 0xa9, 0x9a, 0x32, 0x2a, 0xae, 0xaa, 0xd5, 0x50,
-  0x46, 0x5c, 0xc2, 0x25, 0x5b, 0x05, 0x67, 0xd8, 0xd7, 0xcf, 0xa9, 0x49,
-  0xa6, 0x6b, 0x90, 0x2f, 0xb8, 0xa7, 0x16, 0x1c, 0xae, 0x05, 0xbd, 0x14,
-  0x6a, 0xb2, 0x64, 0x7c, 0xd8, 0x1a, 0xff, 0x67, 0xc4, 0xb4, 0x4f, 0x3f,
-  0x49, 0xe4, 0xdc, 0x02, 0x68, 0x90, 0x15, 0x62, 0xda, 0x17, 0xaf, 0x24,
-  0x19, 0xbb, 0x8c, 0x5d, 0x18, 0x7c, 0x9d, 0x63, 0x85, 0xfa, 0xad, 0xba,
-  0x10, 0xb6, 0x05, 0x64, 0x00, 0x4f, 0x8f, 0x5c, 0xcd, 0xb8, 0x4a, 0xd6,
-  0x8e, 0x2e, 0x02, 0x3b, 0xee, 0xe6, 0x43, 0xca, 0xca, 0x28, 0x2d, 0x24,
-  0x47, 0x11, 0xda, 0x1a, 0x03, 0xdd, 0xf2, 0xbd, 0x3b, 0x89, 0x8d, 0x54,
-  0x8e, 0xb3, 0xb1, 0x21, 0x8d, 0xfa, 0xad, 0xa6, 0x58, 0x5d, 0x13, 0x39,
-  0x3d, 0xc6, 0xfe, 0xd3, 0xec, 0xf2, 0x55, 0xf9, 0x9b, 0x25, 0x22, 0x06,
-  0x80, 0xbc, 0x93, 0x77, 0x7b, 0x46, 0xb3, 0x33, 0xa3, 0x7a, 0xf2, 0x83,
-  0xa4, 0x17, 0x2b, 0x14, 0x5a, 0x74, 0xc4, 0x62, 0xf4, 0xba, 0x78, 0xa6,
-  0xb6, 0xbf, 0x62, 0x03, 0xed, 0xc2, 0x75, 0xa7, 0xb7, 0x8c, 0x8f, 0x02,
-  0x14, 0x71, 0x81, 0xaa, 0xb7, 0xaa, 0xb0, 0x79, 0x4e, 0x1f, 0x3d, 0x47,
-  0x55, 0xaa, 0x41, 0x48, 0x42, 0xc9, 0x5b, 0xeb, 0xba, 0x7d, 0x3d, 0x4d,
-  0x35, 0x71, 0x7a, 0xd6, 0x4e, 0x2c, 0xaf, 0x04, 0x43, 0xc8, 0x63, 0x1e,
-  0xf1, 0xa0, 0x1b, 0x57, 0x07, 0x95, 0xa6, 0xc3, 0xe3, 0x2e, 0xa6, 0x6e,
-  0x41, 0x5a, 0xae, 0x3c, 0xce, 0xb0, 0xbf, 0xa3, 0x71, 0xe8, 0xbe, 0xf3,
-  0xef, 0x7d, 0x5f, 0x1f, 0xa3, 0xa3, 0xb4, 0xf3, 0x00, 0x70, 0x21, 0x1a,
-  0x93, 0x9d, 0x89, 0x90, 0x9a, 0x61, 0x50, 0xdc, 0x20, 0xa5, 0x46, 0x10,
-  0xc8, 0x4d, 0xe8, 0x05, 0x40, 0x4b, 0xa0, 0x12, 0x99, 0x8b, 0x27, 0x05,
-  0xc4, 0x96, 0x5f, 0xc3, 0x7a, 0x86, 0xcc, 0xed, 0x25, 0x8a, 0x2a, 0x90,
-  0x1e, 0x99, 0xd0, 0x46, 0x2c, 0x1f, 0x2f, 0x53, 0xec, 0x7d, 0x03, 0xd7,
-  0xc7, 0x6f, 0xc9, 0x64, 0x1c, 0x8a, 0xfd, 0xb5, 0x2b, 0x63, 0x89, 0xf9,
-  0x3e, 0x16, 0xc0, 0xb0, 0xb7, 0xbc, 0x3b, 0xa4, 0xda, 0x6e, 0xab, 0x46,
-  0x7c, 0xf7, 0x92, 0x5f, 0xf0, 0xda, 0xe6, 0xfc, 0x56, 0x1a, 0x24, 0xf0,
-  0xca, 0xf7, 0xa5, 0xa5, 0x7b, 0x70, 0x70, 0xaf, 0x8b, 0xcd, 0x18, 0xed,
-  0xc3, 0x7a, 0x4a, 0x26, 0x7d, 0xfb, 0x22, 0x94, 0x92, 0xd9, 0xcd, 0x98,
-  0xd3, 0x3a, 0x1e, 0x43, 0x74, 0x2b, 0xfd, 0x07, 0x8d, 0x2b, 0xad, 0xfe,
-  0xe9, 0xae, 0xa4, 0x1d, 0xa1, 0x2f, 0xa7, 0xb3, 0x59, 0xf4, 0x5c, 0x6e,
-  0x3e, 0xf8, 0x82, 0x3b, 0x49, 0xeb, 0xa9, 0xf5, 0xf7, 0xb7, 0x83, 0x05,
-  0xae, 0x75, 0x54, 0x01, 0x71, 0xd4, 0xa5, 0x00, 0x8a, 0x86, 0x81, 0x7b,
-  0xd0, 0x07, 0x77, 0xba, 0x44, 0xc4, 0x94, 0x3e, 0x6f, 0x5d, 0x2a, 0x75,
-  0x2a, 0x5b, 0x39, 0x4c, 0x68, 0x32, 0x65, 0x82, 0x6e, 0x9c, 0xff, 0x35,
-  0xd0, 0x78, 0x73, 0xb3, 0xb7, 0x7f, 0xa2, 0xc3, 0x90, 0xcc, 0x83, 0x90,
-  0x78, 0x69, 0xd5, 0xf2, 0xbb, 0x9d, 0x75, 0x4f, 0x1e, 0xe3, 0xb0, 0x7b,
-  0x80, 0xef, 0x62, 0x92, 0x32, 0xae, 0x02, 0xda, 0x92, 0x9c, 0x6b, 0x1b,
-  0x7f, 0x0e, 0xc7, 0x4a, 0xc3, 0x4f, 0x5b, 0x08, 0xd2, 0xac, 0x22, 0x7b,
-  0xa5, 0x10, 0x90, 0xa9, 0x55, 0x2b, 0x86, 0xfc, 0x3f, 0xc4, 0xf9, 0x5f,
-  0x7f, 0xde, 0x72, 0xbf, 0x9f, 0xda, 0x00, 0x74, 0xb1, 0x32, 0x92, 0xcc,
-  0x4a, 0x1c, 0x84, 0x10, 0xe7, 0x40, 0xa3, 0x2c, 0x03, 0x7a, 0x00, 0xb0,
-  0x2e, 0xa1, 0x69, 0x3a, 0x69, 0x6a, 0x52, 0x02, 0x42, 0x9e, 0xda, 0x67,
-  0xf2, 0xe2, 0x11, 0x3d, 0x27, 0x65, 0x21, 0x70, 0x61, 0x92, 0xfe, 0x33,
-  0xc3, 0x9f, 0x87, 0x86, 0xef, 0x12, 0x35, 0xc8, 0x6b, 0x21, 0xde, 0x72,
-  0xb4, 0xe4, 0xff, 0x87, 0x52, 0x2a, 0xa4, 0x83, 0x99, 0xd4, 0x6c, 0x5f,
-  0xac, 0x67, 0xb5, 0x27, 0x2e, 0xed, 0x58, 0x08, 0x00, 0xaf, 0xc0, 0x6b,
-  0x8a, 0xd7, 0xb7, 0x81, 0xbc, 0x5b, 0x4a, 0x27, 0xce, 0x15, 0x70, 0xac,
-  0x9f, 0xf2, 0xba, 0x39, 0xd4, 0x9d, 0xe7, 0xb8, 0xc4, 0xd9, 0x37, 0x5e,
-  0x9b, 0xaa, 0xf0, 0xf6, 0x51, 0x9b, 0x50, 0xf1, 0xb7, 0x27, 0xbd, 0xa7,
-  0x75, 0x9f, 0x92, 0x10, 0x3c, 0xa3, 0x7c, 0x38, 0x2e, 0x5f, 0x99, 0xfc,
-  0xbd, 0x3f, 0x03, 0x5a, 0xcd, 0xd7, 0xfe, 0xc0, 0x3e, 0x36, 0x8d, 0x73,
-  0xdc, 0xf6, 0x82, 0x0d, 0x72, 0x42, 0xb4, 0xd3, 0x38, 0xba, 0xa9, 0x16,
-  0x0c, 0x8c, 0xaf, 0x95, 0x02, 0xbb, 0xe5, 0x54, 0x7b, 0xdc, 0x5d, 0xc9,
-  0xd4, 0xdd, 0x12, 0xd6, 0xba, 0x89, 0x6c, 0xb5, 0xfb, 0x6f, 0x9c, 0xab,
-  0x38, 0x7e, 0xda, 0x54, 0x1d, 0xbc, 0x67, 0xeb, 0xf2, 0xfd, 0xad, 0x44,
-  0xd2, 0x9a, 0x86, 0x6a, 0x0c, 0x36, 0x00, 0xcd, 0x18, 0xee, 0x90, 0x5e,
-  0x05, 0x32, 0x0c, 0x9b, 0x35, 0x78, 0x85, 0x62, 0x22, 0x65, 0x06, 0x17,
-  0x04, 0xf0, 0x89, 0x79, 0xd3, 0x41, 0x9b, 0xc7, 0x9f, 0xbd, 0x2b, 0x67,
-  0x75, 0x7f, 0x06, 0xf9, 0xa5, 0x6d, 0x9c, 0xff, 0x07, 0xc3, 0xe7, 0x7f,
-  0x1b, 0xf3, 0x2d, 0x2f, 0xa7, 0x00, 0xe0, 0x21, 0x1a, 0x93, 0xa5, 0x21,
-  0x90, 0x85, 0x22, 0xa2, 0x58, 0x50, 0x41, 0x09, 0xac, 0xcd, 0x64, 0x54,
-  0x02, 0xb1, 0x7b, 0x0b, 0x50, 0x52, 0xc8, 0x9f, 0xb0, 0x98, 0x3f, 0xd7,
-  0xe3, 0x79, 0x76, 0xd1, 0x9b, 0xde, 0x98, 0x38, 0x60, 0x7b, 0xa8, 0xf1,
-  0x09, 0x11, 0x36, 0x8a, 0x4a, 0xe2, 0xb3, 0xa5, 0x29, 0x75, 0x66, 0x93,
-  0xc1, 0xa4, 0xcd, 0x6d, 0x0c, 0xd9, 0x02, 0x35, 0x43, 0xf5, 0x61, 0xb6,
-  0xa4, 0x76, 0x29, 0x06, 0x71, 0x70, 0x8f, 0xbf, 0xe8, 0x70, 0xc9, 0x95,
-  0x9e, 0xfc, 0xf8, 0xc2, 0x13, 0xe3, 0x49, 0x95, 0x61, 0x1b, 0x79, 0x24,
-  0xe0, 0x58, 0x80, 0x8a, 0x80, 0xd1, 0x59, 0xa7, 0x91, 0x02, 0x11, 0xd3,
-  0x5d, 0x2a, 0x80, 0xaf, 0xdf, 0x90, 0xe8, 0xe4, 0xbc, 0x47, 0xbd, 0x28,
-  0x82, 0x02, 0xd4, 0xbd, 0xce, 0xad, 0x08, 0xf0, 0x0b, 0x4f, 0xdc, 0x39,
-  0x50, 0x15, 0xb7, 0x6a, 0xe5, 0x4f, 0x94, 0x7e, 0x7a, 0x41, 0xd9, 0xe2,
-  0x1d, 0x2b, 0xd7, 0x80, 0xcf, 0x8a, 0x28, 0xbd, 0x6c, 0x97, 0xc8, 0xe3,
-  0x56, 0x7e, 0xab, 0xb6, 0x09, 0xd7, 0xb6, 0x9b, 0xe9, 0xcc, 0x59, 0x73,
-  0x95, 0x5e, 0xa4, 0x2d, 0xea, 0x70, 0x19, 0xdd, 0x66, 0x87, 0xa0, 0xab,
-  0x1a, 0xd1, 0x39, 0x70, 0x26, 0xb7, 0x3a, 0x27, 0x87, 0x70, 0x3b, 0x77,
-  0x47, 0xca, 0x8e, 0x38, 0xb8, 0x95, 0xc7, 0xb6, 0x9b, 0x8a, 0x26, 0x68,
-  0x8f, 0xbb, 0xdf, 0x19, 0x18, 0xc8, 0x77, 0xca, 0x94, 0x2c, 0x94, 0x6e,
-  0xa5, 0x38, 0xde, 0xd6, 0x6f, 0x75, 0x5b, 0x59, 0xbb, 0xd8, 0xf5, 0x69,
-  0x54, 0xd0, 0xd8, 0xc7, 0x57, 0x8b, 0xb1, 0xe7, 0x67, 0xbd, 0xf9, 0x03,
-  0xba, 0x60, 0x8a, 0x87, 0x82, 0xb8, 0x4e, 0x00, 0x29, 0x50, 0x01, 0x58,
-  0x20, 0x94, 0x90, 0x50, 0x5f, 0xe7, 0xfa, 0x2f, 0xeb, 0xdf, 0x51, 0x07,
-  0xfe, 0xb7, 0xd7, 0x73, 0x91, 0x1e, 0x8f, 0xa6, 0x3f, 0xb8, 0xe9, 0xa5,
-  0x31, 0x1f, 0x0f, 0xc1, 0x1c, 0xf2, 0x6f, 0xf6, 0x28, 0xb5, 0x8c, 0x9c,
-  0x80, 0x3a, 0x1e, 0x87, 0xf6, 0x7e, 0x21, 0x75, 0x80, 0x43, 0xb4, 0x28,
-  0xfc, 0x07, 0x8f, 0x10, 0x02, 0x00, 0x45, 0x6d, 0xcd, 0x7e, 0x2b, 0xad,
-  0xcf, 0xb1, 0x95, 0xdd, 0xe5, 0xb3, 0x7a, 0x4b, 0xff, 0xcc, 0xa7, 0xaa,
-  0x51, 0x4a, 0xab, 0x9b, 0x1b, 0x3c, 0x6b, 0xa5, 0x58, 0xb0, 0xad, 0x2b,
-  0x62, 0x6d, 0x7e, 0x98, 0x42, 0x1a, 0x84, 0xcf, 0xf4, 0x71, 0x55, 0x89,
-  0x48, 0xab, 0xea, 0x00, 0x00, 0xa2, 0x10, 0x6a, 0x8d, 0xe8, 0xed, 0x39,
-  0x70, 0x66, 0xd4, 0x1b, 0xcd, 0x77, 0x30, 0x3f, 0x9c, 0x75, 0x7f, 0xd6,
-  0xfb, 0x66, 0x28, 0x16, 0xbf, 0x1e, 0x8a, 0x2b, 0x44, 0x48, 0x57, 0xa1,
-  0x67, 0xbf, 0x80, 0x77, 0xd7, 0x1c, 0x63, 0x9d, 0x61, 0x30, 0x86, 0x9d,
-  0x18, 0x5f, 0x1c, 0xf2, 0xc2, 0xfd, 0x34, 0xa1, 0x8d, 0x1a, 0xfd, 0x56,
-  0x14, 0x63, 0x82, 0xa1, 0xed, 0x5c, 0xd9, 0x45, 0x05, 0x86, 0xca, 0x81,
-  0x1c, 0x9c, 0x8d, 0x1d, 0x67, 0xbd, 0x51, 0xa0, 0xa7, 0x64, 0x0d, 0xf9,
-  0x44, 0x6d, 0xa8, 0x62, 0x22, 0x37, 0x52, 0xd2, 0xd6, 0x33, 0xd2, 0xc5,
-  0xd2, 0xb7, 0x33, 0x7a, 0x76, 0x36, 0xee, 0x37, 0x63, 0x9f, 0x33, 0x1f,
-  0x2e, 0xaf, 0xc6, 0xeb, 0xf7, 0x76, 0x5e, 0x1f, 0x2f, 0x9b, 0xcc, 0x01,
-  0xc0, 0x21, 0x1a, 0x93, 0x95, 0x15, 0x94, 0x8d, 0x22, 0x21, 0x60, 0x82,
-  0x14, 0x6f, 0x5b, 0x8d, 0xc5, 0xd3, 0x76, 0xa5, 0x00, 0x02, 0x11, 0x51,
-  0xf6, 0x1a, 0x05, 0x0a, 0x96, 0xb8, 0xfb, 0x3b, 0xf7, 0x3c, 0xf9, 0xb3,
-  0xc7, 0x4f, 0xbe, 0x9c, 0xb4, 0x2d, 0xbf, 0x3e, 0x35, 0xc4, 0x0e, 0x54,
-  0x68, 0xb0, 0x64, 0xc6, 0xa9, 0xcd, 0x0e, 0xda, 0x09, 0x50, 0x53, 0xe6,
-  0x67, 0xcb, 0x46, 0xbe, 0x2d, 0x48, 0x2a, 0x06, 0xb1, 0x1f, 0x64, 0x45,
-  0x25, 0x22, 0x8e, 0x29, 0xcd, 0x60, 0x7a, 0x8c, 0xb4, 0xfa, 0xe5, 0x55,
-  0x82, 0x99, 0x37, 0xb1, 0xc2, 0x2b, 0xb9, 0x42, 0x57, 0xde, 0x8b, 0xde,
-  0x1d, 0x66, 0x5d, 0xf5, 0xe7, 0x91, 0x78, 0x63, 0x31, 0x3f, 0x7a, 0x2f,
-  0xf5, 0xbd, 0x56, 0xf8, 0xf6, 0x99, 0xbf, 0x5a, 0xf0, 0x15, 0xb6, 0xbc,
-  0x47, 0xc9, 0xce, 0x84, 0x65, 0x57, 0x57, 0x7a, 0x94, 0x64, 0x3b, 0x31,
-  0x5d, 0xd5, 0x81, 0x9f, 0x87, 0xf2, 0x79, 0x7d, 0x3e, 0x79, 0x85, 0x84,
-  0x93, 0xd1, 0x2f, 0xc1, 0xd1, 0xfb, 0x0e, 0x8c, 0x0c, 0xdd, 0x91, 0x57,
-  0xf8, 0x8e, 0x8d, 0xab, 0xf4, 0x5d, 0x03, 0xa3, 0x77, 0x39, 0xb6, 0xd6,
-  0xe8, 0x86, 0x43, 0x18, 0xf0, 0xad, 0x50, 0x01, 0x97, 0xa2, 0xde, 0xc8,
-  0x98, 0x0a, 0x8b, 0x6d, 0x47, 0x12, 0x92, 0xe2, 0x8e, 0xc1, 0xcb, 0x6b,
-  0x8f, 0x05, 0x3a, 0x88, 0xb2, 0xe8, 0x4f, 0x6a, 0xd9, 0x20, 0xf7, 0xee,
-  0x3e, 0x15, 0x6b, 0xa3, 0xbd, 0x8d, 0xbf, 0x14, 0x82, 0x8d, 0xa6, 0xc6,
-  0xe0, 0xbe, 0x53, 0x1a, 0x3d, 0xfc, 0x9f, 0x67, 0x8a, 0x39, 0x01, 0xc9,
-  0x8a, 0x91, 0x23, 0x22, 0x18, 0x50, 0x37, 0x0b, 0x22, 0xb2, 0x5d, 0x29,
-  0x56, 0x14, 0x2a, 0x80, 0x10, 0x42, 0x50, 0xc7, 0xf2, 0x4e, 0x5b, 0xe9,
-  0xcc, 0xab, 0xc3, 0x9a, 0xe9, 0xee, 0x0b, 0x93, 0x59, 0xc5, 0xd8, 0x4d,
-  0x72, 0xdd, 0x83, 0xb3, 0x0b, 0xd7, 0xdb, 0x9d, 0x1a, 0x75, 0x6c, 0xfc,
-  0x39, 0x78, 0x8c, 0x96, 0x32, 0x68, 0xc1, 0x0a, 0x00, 0x21, 0x72, 0x16,
-  0x04, 0x7b, 0xf6, 0x55, 0x46, 0x85, 0x78, 0x06, 0xd4, 0x83, 0x51, 0xe3,
-  0xa4, 0xdb, 0x50, 0x96, 0x3e, 0x16, 0x0c, 0x17, 0xe0, 0x94, 0xdd, 0x68,
-  0xe1, 0xf8, 0xe1, 0x87, 0x8f, 0x22, 0xb3, 0x65, 0x57, 0xa9, 0x0a, 0x22,
-  0x5c, 0x64, 0xc9, 0xa3, 0xc9, 0x6b, 0xb5, 0x61, 0x31, 0xb8, 0xf0, 0x6a,
-  0x2a, 0xbd, 0x13, 0x2f, 0xe7, 0xdd, 0x18, 0x0c, 0x6a, 0x9f, 0x71, 0x79,
-  0x00, 0x11, 0x56, 0x31, 0x0d, 0xee, 0xb6, 0x34, 0xd5, 0x81, 0xe3, 0xe8,
-  0x5b, 0xd8, 0x20, 0x1b, 0x3d, 0xe6, 0x53, 0x1a, 0xa2, 0xb3, 0x49, 0x65,
-  0x35, 0x95, 0x8f, 0x64, 0xc1, 0x72, 0xee, 0x9d, 0xff, 0x3a, 0x2d, 0xdf,
-  0xb7, 0x17, 0xeb, 0xdf, 0x85, 0x0e, 0x75, 0x7b, 0x12, 0xe5, 0xe0, 0x9f,
-  0x11, 0xa3, 0x16, 0x7e, 0x9f, 0x1d, 0x72, 0x47, 0x3c, 0xae, 0x06, 0x40,
-  0x81, 0x63, 0x85, 0x04, 0x0d, 0x1e, 0x4b, 0x7d, 0x3e, 0xa6, 0x9a, 0xc6,
-  0x45, 0x5f, 0x93, 0xde, 0xf7, 0x0b, 0xc3, 0xaf, 0xd1, 0x78, 0x7e, 0xe4,
-  0xab, 0xea, 0xf8, 0x11, 0xe1, 0x76, 0xda, 0xde, 0xd0, 0x07, 0x21, 0x1a,
-  0x88, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xed, 0x65, 0xa3, 0x41, 0x49, 0xe8,
-  0x68, 0x23, 0x85, 0x2a, 0xcc, 0xde, 0xa4, 0x0a, 0xd8, 0x02, 0x00, 0x12,
-  0x50, 0x30, 0xff, 0x46, 0x8c, 0x66, 0xd8, 0xeb, 0xe2, 0x27, 0xd1, 0x59,
-  0x7d, 0xd7, 0xc6, 0x1d, 0x26, 0x43, 0x34, 0xca, 0xf0, 0x0a, 0xed, 0x71,
-  0xbe, 0x82, 0x03, 0x18, 0xe2, 0x88, 0xb4, 0xd3, 0x2c, 0x68, 0x1e, 0x2b,
-  0xd4, 0xc6, 0xe7, 0xb5, 0x65, 0x00, 0x65, 0xbc, 0xb5, 0x87, 0xe5, 0xf1,
-  0x16, 0x70, 0xf4, 0x25, 0xf3, 0x3c, 0x46, 0x72, 0x4e, 0x0b, 0x73, 0x10,
-  0x20, 0x16, 0xc7, 0x7d, 0xbe, 0x98, 0x7b, 0x1b, 0xa5, 0x25, 0x7a, 0xfa,
-  0x77, 0xe4, 0x87, 0x20, 0xe9, 0x4e, 0x48, 0xf1, 0x3b, 0x42, 0xcb, 0x57,
-  0xc1, 0x8b, 0xa4, 0xcc, 0xab, 0xba, 0x7f, 0x5e, 0xfb, 0x81, 0x93, 0xe4,
-  0xa8, 0xb5, 0xb0, 0xcc, 0x4e, 0x5b, 0xa2, 0xd7, 0xb7, 0x38, 0x06, 0x9a,
-  0x64, 0x9d, 0xfa, 0x15, 0x33, 0x1f, 0x46, 0x52, 0xd1, 0x11, 0x0a, 0x02,
-  0xbe, 0x74, 0xad, 0x3a, 0x02, 0x78, 0x05, 0xfa, 0x5c, 0xe4, 0xa7, 0x97,
-  0x4b, 0x26, 0x01, 0x33, 0x09, 0xa0, 0xc8, 0xfe, 0xac, 0xba, 0x8e, 0x5b,
-  0x83, 0x03, 0xa1, 0x46, 0xf8, 0xcc, 0x29, 0xd1, 0xcb, 0xb0, 0x86, 0xd0,
-  0xb8, 0x5f, 0x46, 0xef, 0xa1, 0x41, 0x8d, 0xab, 0x56, 0x82, 0x46, 0x2a,
-  0xa1, 0x44, 0x44, 0x0b, 0x7a, 0x16, 0x50, 0x7f, 0x50, 0xfb, 0x33, 0x84,
-  0xfb, 0xee, 0x9e, 0x8c, 0xf6, 0x49, 0xf1, 0x56, 0x4e, 0x95, 0x85, 0x82,
-  0x7e, 0x7e, 0x3f, 0x99, 0xe8, 0xfd, 0x2f, 0x57, 0x5c, 0xc0, 0x0f, 0x3e,
-  0xb6, 0x1a, 0x3c, 0x8a, 0x83, 0x61, 0x40, 0x8c, 0x3b, 0xba, 0xa9, 0x4a,
-  0x92, 0xba, 0xc8, 0x0d, 0xc0, 0x00, 0x15, 0x68, 0xe4, 0x39, 0x27, 0x27,
-  0x44, 0xa8, 0xa0, 0x2a, 0x36, 0xaa, 0x39, 0x1f, 0xb7, 0xac, 0x52, 0x03,
-  0x40, 0x13, 0x63, 0x1e, 0x2d, 0x45, 0x82, 0x7a, 0x26, 0x8b, 0x9a, 0x08,
-  0xa5, 0x7b, 0xb6, 0x39, 0x4e, 0x96, 0x56, 0xa8, 0x65, 0x13, 0xf7, 0xc5,
-  0xf4, 0x4a, 0x38, 0xad, 0x4d, 0x24, 0x9d, 0x6f, 0x73, 0x23, 0x61, 0x74,
-  0xde, 0x0b, 0x2f, 0x5c, 0xb3, 0x3b, 0x36, 0xf6, 0xda, 0x9b, 0xf3, 0xe9,
-  0xf8, 0xad, 0xce, 0x4e, 0xc5, 0xc9, 0x63, 0xec, 0xd1, 0xd2, 0xdb, 0xf1,
-  0xc2, 0xe6, 0x14, 0x9d, 0x3d, 0xda, 0x78, 0x5a, 0x19, 0x6a, 0xe4, 0xed,
-  0x69, 0xf5, 0x5d, 0x1f, 0x56, 0xbe, 0x7c, 0x91, 0x28, 0xde, 0x0f, 0x54,
-  0xf7, 0xd0, 0x6a, 0x1a, 0x11, 0x02, 0x06, 0xe8, 0x15, 0x8e, 0x23, 0x4f,
-  0xc5, 0x33, 0x58, 0x6c, 0xa3, 0x21, 0x82, 0x87, 0xc1, 0xc3, 0x2f, 0x1b,
-  0x13, 0x3d, 0x86, 0x2d, 0xec, 0xe2, 0x37, 0x65, 0xe9, 0x2d, 0x93, 0xdc,
-  0x5d, 0x3b, 0x7b, 0x9a, 0xf6, 0x98, 0xc4, 0x22, 0x9d, 0x24, 0x10, 0x99,
-  0x93, 0x0d, 0x5a, 0x51, 0xf7, 0x97, 0xb1, 0x62, 0xa5, 0x8a, 0x92, 0x50,
-  0xb7, 0xf7, 0xaf, 0x1e, 0x5f, 0xdb, 0xbf, 0xb4, 0xfc, 0xc8, 0x70, 0x21,
-  0x1a, 0x88, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xf3, 0x65, 0xa5, 0xa0, 0x88,
-  0xea, 0x44, 0x21, 0x09, 0x05, 0x04, 0x10, 0xb3, 0x88, 0x6e, 0x56, 0xa0,
-  0x06, 0xc0, 0x09, 0x42, 0x55, 0xa5, 0x7b, 0x89, 0xb7, 0x8b, 0xe1, 0xd3,
-  0x20, 0x3b, 0x16, 0x0e, 0x93, 0xef, 0x7b, 0xaa, 0xe3, 0xfa, 0x9f, 0xca,
-  0x90, 0xc5, 0x6c, 0x5b, 0x6e, 0x4d, 0x0d, 0xd7, 0x76, 0xa4, 0x69, 0x23,
-  0x92, 0x56, 0x65, 0x4b, 0xe9, 0x6a, 0x0a, 0x36, 0x29, 0x65, 0x87, 0xcf,
-  0xbb, 0x00, 0x06, 0x0c, 0x4f, 0x52, 0xde, 0xf4, 0x4b, 0x67, 0xcb, 0x25,
-  0xfc, 0xcd, 0xae, 0xbb, 0x37, 0x19, 0x90, 0x2b, 0x03, 0x69, 0x6f, 0xb2,
-  0xa1, 0x2c, 0x63, 0x11, 0x2d, 0xef, 0x24, 0x9b, 0x61, 0xca, 0x24, 0x88,
-  0xd2, 0xe4, 0x83, 0x61, 0x62, 0xa6, 0x53, 0x03, 0x75, 0x12, 0xb7, 0x30,
-  0xea, 0x4d, 0x2a, 0xa6, 0x71, 0x67, 0x6a, 0x26, 0x88, 0x6f, 0x48, 0x72,
-  0x4b, 0x25, 0x8a, 0xc8, 0x9d, 0xc5, 0x0c, 0x5d, 0xd5, 0xd2, 0x6f, 0xc8,
-  0x72, 0x96, 0x1c, 0x4f, 0xd6, 0xc9, 0x54, 0x68, 0x2f, 0x99, 0x33, 0x99,
-  0xb0, 0xcb, 0xb2, 0x02, 0x1a, 0xf4, 0x0c, 0xce, 0x88, 0xee, 0x6e, 0xef,
-  0x65, 0xe0, 0x41, 0x02, 0x2c, 0x01, 0x35, 0xa2, 0x62, 0x9e, 0x4c, 0x32,
-  0x24, 0x46, 0x61, 0x3b, 0xd6, 0xf3, 0x8b, 0x89, 0x64, 0x4b, 0xf2, 0xa9,
-  0x00, 0xf3, 0xa9, 0x3d, 0xc6, 0x19, 0x9c, 0x23, 0x4a, 0xdf, 0xc1, 0x55,
-  0x46, 0xd8, 0x9b, 0xd8, 0x2f, 0xdb, 0x46, 0xbb, 0x4a, 0x92, 0x75, 0x40,
-  0xa2, 0x3b, 0x2d, 0xeb, 0x72, 0x17, 0xdf, 0x17, 0xc6, 0x20, 0xf1, 0xeb,
-  0x46, 0x11, 0x11, 0x70, 0x37, 0x0e, 0xd5, 0x01, 0x53, 0x8a, 0x83, 0x65,
-  0x45, 0x00, 0x40, 0x90, 0x04, 0xa7, 0x8d, 0x2f, 0xfb, 0xbf, 0xac, 0xec,
-  0x5a, 0x01, 0x2c, 0xd1, 0xf9, 0x9a, 0x8b, 0x25, 0x4a, 0x3e, 0x7a, 0xba,
-  0x74, 0x73, 0xb0, 0x4f, 0x09, 0xe3, 0xb3, 0xa6, 0x3c, 0x8c, 0x38, 0x4c,
-  0x7d, 0x48, 0xdc, 0x32, 0x05, 0xeb, 0x27, 0xcc, 0xc6, 0x03, 0xcb, 0x84,
-  0x50, 0xf3, 0x75, 0xe0, 0xea, 0x34, 0xd9, 0x41, 0x0b, 0x91, 0x94, 0xd9,
-  0xe7, 0x55, 0xd3, 0x46, 0x29, 0x88, 0x6d, 0x72, 0x56, 0x3d, 0xdc, 0x3a,
-  0x0b, 0x8a, 0x98, 0x45, 0xb5, 0x17, 0x45, 0x0d, 0x73, 0xe5, 0xf5, 0x02,
-  0x91, 0x58, 0x62, 0xd3, 0xc0, 0xd4, 0xe8, 0x5c, 0xf4, 0x2a, 0xe9, 0x89,
-  0xf3, 0x68, 0x95, 0xcb, 0x13, 0xa1, 0xe4, 0xce, 0x78, 0x89, 0x12, 0x47,
-  0xfe, 0xcf, 0xb1, 0x5f, 0xf2, 0x52, 0xf8, 0xde, 0xef, 0xd2, 0x7f, 0x75,
-  0xb9, 0xf0, 0x08, 0x40, 0x34, 0x0e, 0xe4, 0xf1, 0x1a, 0x42, 0x83, 0xbd,
-  0x5e, 0xfc, 0x49, 0x37, 0x11, 0x46, 0x9a, 0x6c, 0x3d, 0xd2, 0x9a, 0x17,
-  0x9a, 0x73, 0x57, 0xb6, 0xf5, 0x53, 0x89, 0x67, 0xc2, 0xb2, 0x6a, 0xf4,
-  0xaf, 0xf0, 0x08, 0xae, 0xd8, 0x6d, 0xac, 0x72, 0xac, 0xef, 0xe2, 0x8b,
-  0xf3, 0x79, 0x7c, 0x1f, 0x53, 0xd6, 0xfa, 0xbf, 0x12, 0x00, 0x38, 0x21,
-  0x1a, 0x88, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0xed, 0x6d, 0x61, 0xa1, 0x89,
-  0xac, 0x48, 0x12, 0x84, 0x01, 0x51, 0x95, 0x2a, 0x03, 0x77, 0x00, 0x08,
-  0x24, 0xd8, 0x93, 0xc1, 0x2a, 0x88, 0x91, 0x54, 0x42, 0x28, 0x64, 0xe2,
-  0x10, 0x4c, 0x3a, 0x8e, 0x26, 0x04, 0x1e, 0xe8, 0xb3, 0xc2, 0x3e, 0x0e,
-  0x7e, 0x6d, 0xfd, 0x1d, 0x6b, 0x4c, 0x69, 0xef, 0xfe, 0x46, 0x53, 0x7c,
-  0x3a, 0xd2, 0xc6, 0xd4, 0xb8, 0x38, 0x61, 0x06, 0xa3, 0x5e, 0x7e, 0x0a,
-  0x0b, 0xd1, 0x34, 0x5e, 0x96, 0x8c, 0x28, 0x12, 0x5a, 0xc8, 0x20, 0xa4,
-  0xc6, 0x2b, 0x00, 0x8f, 0x23, 0xb6, 0x06, 0xe7, 0xe3, 0x80, 0x91, 0x88,
-  0xdf, 0x30, 0xa5, 0x8d, 0x9a, 0x55, 0x82, 0xf0, 0x31, 0x8d, 0x47, 0x0b,
-  0x16, 0xbf, 0x45, 0x92, 0x94, 0xc6, 0x98, 0x21, 0xf3, 0xd5, 0x07, 0xd9,
-  0xcc, 0x71, 0xe3, 0x27, 0x5a, 0x5a, 0xb1, 0xf1, 0x81, 0xfc, 0x2a, 0x07,
-  0xfd, 0x45, 0xf8, 0x65, 0x39, 0x8e, 0x88, 0x54, 0xff, 0x7d, 0x74, 0x45,
-  0x0f, 0x8b, 0x35, 0x21, 0xf4, 0x59, 0xed, 0xfb, 0xa6, 0x76, 0x7c, 0x8d,
-  0x95, 0x14, 0xe9, 0x90, 0x8a, 0xf3, 0x68, 0x27, 0x80, 0x35, 0x30, 0xa2,
-  0x4c, 0x8c, 0x1a, 0x10, 0xea, 0xc8, 0x8c, 0xc8, 0xe5, 0xb6, 0x22, 0x1e,
-  0xe6, 0xb1, 0x4b, 0x10, 0x9c, 0xa4, 0xdf, 0xcc, 0x03, 0xe0, 0xd1, 0x3e,
-  0xfb, 0x65, 0x3d, 0x0d, 0x20, 0x78, 0x95, 0x2e, 0x8c, 0x83, 0x32, 0xef,
-  0xcd, 0x83, 0x09, 0x14, 0x22, 0x97, 0x8d, 0x4b, 0x21, 0x1d, 0x6e, 0x8e,
-  0xf3, 0x35, 0x13, 0xa1, 0x46, 0x95, 0x86, 0x7f, 0x7f, 0x87, 0x1f, 0x4e,
-  0x73, 0x00, 0x0f, 0x3e, 0xc5, 0x29, 0x45, 0x06, 0xc1, 0x81, 0xb8, 0x76,
-  0x00, 0xa4, 0x54, 0x00, 0x01, 0x00, 0x58, 0x36, 0x74, 0x34, 0xd6, 0xb4,
-  0xa2, 0x28, 0x1f, 0x4c, 0xdd, 0x72, 0xce, 0xae, 0xb0, 0x74, 0x76, 0xec,
-  0x6b, 0x50, 0xef, 0xa5, 0x99, 0x11, 0x64, 0x3e, 0x4f, 0xae, 0x77, 0x7b,
-  0xfd, 0xf1, 0xbe, 0x9e, 0xa6, 0x68, 0x4f, 0xb4, 0x4a, 0x2c, 0x3b, 0xcf,
-  0x0d, 0xd8, 0xa8, 0xe2, 0x88, 0x83, 0x14, 0x4b, 0x30, 0xc6, 0x97, 0x7b,
-  0x62, 0xb8, 0x03, 0x21, 0x58, 0x5d, 0x73, 0x8b, 0x71, 0x01, 0xb5, 0x76,
-  0x23, 0xe6, 0xbc, 0x75, 0xaf, 0xa1, 0xce, 0x37, 0xea, 0xbd, 0xe1, 0x4c,
-  0xf5, 0x42, 0x0d, 0x71, 0x5b, 0xc2, 0xc6, 0x3d, 0xd9, 0xc5, 0x1a, 0x93,
-  0x73, 0xfd, 0x9f, 0xd8, 0x33, 0xe2, 0x25, 0x10, 0x7b, 0xb4, 0xbd, 0x60,
-  0xf7, 0x19, 0x31, 0xca, 0x86, 0x97, 0x71, 0xaa, 0x6b, 0x89, 0x45, 0x85,
-  0x96, 0xd5, 0xed, 0x41, 0x30, 0x80, 0xa2, 0x42, 0x88, 0xbb, 0xc9, 0xd5,
-  0xed, 0xcb, 0x48, 0x64, 0x60, 0x4e, 0xa6, 0xfd, 0xa3, 0xf2, 0xf6, 0x13,
-  0x74, 0x8d, 0xe7, 0x41, 0x51, 0x8f, 0x75, 0x8d, 0xaf, 0xd9, 0x16, 0xb1,
-  0xa7, 0x57, 0xc0, 0xfa, 0xb9, 0xf0, 0xfc, 0xfe, 0xeb, 0x8d, 0xb4, 0x03,
-  0x07, 0x21, 0x1a, 0x8c, 0x00, 0x01, 0xff, 0xff, 0xfe, 0xed, 0x6f, 0x62,
-  0x8a, 0x10, 0x84, 0x34, 0x14, 0x11, 0x42, 0x62, 0xaa, 0xf3, 0x81, 0x88,
-  0x28, 0x50, 0x20, 0x04, 0x91, 0x83, 0x32, 0x31, 0x11, 0x30, 0x28, 0x78,
-  0xda, 0x4b, 0xfa, 0xb6, 0x71, 0x89, 0xa8, 0x1e, 0x93, 0x61, 0xcd, 0x9c,
-  0x53, 0x72, 0x73, 0x79, 0xf6, 0x1f, 0x24, 0x58, 0xf3, 0x5b, 0x65, 0xa7,
-  0x9b, 0x23, 0xc8, 0x72, 0xb9, 0x8e, 0x88, 0x11, 0x65, 0x47, 0xa7, 0x8f,
-  0x88, 0x68, 0xf9, 0x15, 0xc4, 0x95, 0x26, 0xcd, 0x0d, 0xa8, 0x5f, 0x16,
-  0xd8, 0xec, 0xc6, 0xa7, 0x38, 0x0b, 0x0c, 0x0b, 0x23, 0x0a, 0x9b, 0xa2,
-  0xcf, 0xc7, 0x0d, 0x3d, 0x37, 0x8f, 0x7a, 0x7d, 0x57, 0xd5, 0x59, 0x36,
-  0x62, 0x84, 0xba, 0xde, 0xf2, 0x64, 0x77, 0xe7, 0xa6, 0x95, 0x4d, 0x73,
-  0x3c, 0x07, 0x9a, 0xb9, 0xe4, 0x66, 0x21, 0xc1, 0x1e, 0x71, 0x13, 0x60,
-  0x7b, 0x24, 0xad, 0x4b, 0x61, 0x47, 0x40, 0xf4, 0xea, 0xf2, 0xc6, 0xcd,
-  0x05, 0x5c, 0x42, 0x9e, 0x6e, 0xbe, 0x1a, 0x4b, 0xab, 0x90, 0xb5, 0x29,
-  0x87, 0x51, 0x9e, 0x69, 0x77, 0xed, 0x68, 0x32, 0xb6, 0xbe, 0xa3, 0x93,
-  0x9a, 0x67, 0xc2, 0xd3, 0xc7, 0x17, 0x84, 0xf0, 0x61, 0x88, 0x74, 0xd6,
-  0x51, 0x90, 0x5b, 0x40, 0x84, 0x36, 0x67, 0x40, 0xa4, 0xcf, 0x6e, 0xdd,
-  0x36, 0x5b, 0xa8, 0xde, 0xa4, 0x5c, 0xfb, 0xcf, 0xf7, 0xe5, 0x26, 0x84,
-  0x5d, 0x46, 0xba, 0x64, 0x8d, 0x83, 0x00, 0xa9, 0xba, 0xd2, 0xd8, 0xbc,
-  0x8d, 0xc6, 0xbc, 0xdd, 0xad, 0x59, 0xfb, 0x52, 0x7f, 0xed, 0x21, 0xcd,
-  0xdb, 0xb1, 0xd9, 0xfc, 0xfd, 0x3d, 0x78, 0xf9, 0x7b, 0x78, 0x00, 0x3b,
-  0xb0, 0x7e, 0x25, 0x14, 0x14, 0x11, 0x42, 0xe6, 0x29, 0x25, 0x54, 0xab,
-  0xaa, 0x58, 0x30, 0x08, 0xa1, 0x11, 0x14, 0x29, 0xbd, 0xe3, 0x7a, 0x5a,
-  0x13, 0x99, 0xaf, 0x0e, 0xc7, 0x3a, 0x69, 0x40, 0x65, 0x02, 0xb3, 0x41,
-  0xda, 0xbf, 0x51, 0xb9, 0x27, 0xd0, 0xc0, 0x30, 0x67, 0x30, 0x38, 0xc8,
-  0x0f, 0x0a, 0x04, 0x3c, 0x95, 0x0f, 0x91, 0x96, 0x28, 0x7e, 0x5f, 0x54,
-  0x49, 0x13, 0x20, 0xd3, 0xd0, 0x48, 0xbb, 0xae, 0x12, 0x02, 0x95, 0xa1,
-  0xc1, 0x5a, 0x67, 0xd9, 0x4b, 0x79, 0x50, 0x28, 0x53, 0xc9, 0x01, 0x0b,
-  0xae, 0xfb, 0xf0, 0x5d, 0xa1, 0x67, 0xa3, 0xc2, 0x47, 0x36, 0x9f, 0x77,
-  0xa8, 0x42, 0x4a, 0xd4, 0xed, 0xdc, 0x6d, 0x2c, 0x70, 0xc8, 0x19, 0xc2,
-  0x42, 0xde, 0x04, 0xd8, 0x84, 0x11, 0x78, 0xf1, 0xda, 0x9e, 0xba, 0x4e,
-  0x49, 0xcc, 0x89, 0x00, 0xdc, 0x34, 0x8a, 0x10, 0xa6, 0xca, 0x12, 0x05,
-  0x71, 0x58, 0x44, 0x88, 0x53, 0x4f, 0xbf, 0x8e, 0x81, 0xa3, 0x1a, 0x46,
-  0xaa, 0xfd, 0xf1, 0xd9, 0xc8, 0x04, 0x8d, 0x76, 0x69, 0x1f, 0x77, 0xa3,
-  0x9f, 0xbf, 0xf0, 0xf4, 0xe6, 0x40, 0x38, 0x21, 0x1a, 0x88, 0x00, 0x3f,
-  0xff, 0xff, 0xfe, 0xff, 0x6c, 0x81, 0xa1, 0x0a, 0x08, 0x26, 0x14, 0x09,
-  0x42, 0xd0, 0xb6, 0x5a, 0xe6, 0xf4, 0x01, 0x80, 0x40, 0x2a, 0xea, 0xe8,
-  0x2d, 0x8e, 0x2a, 0xfa, 0xae, 0x01, 0x1b, 0x36, 0xc3, 0xed, 0xd6, 0x65,
-  0x40, 0xc2, 0xb9, 0x27, 0x8c, 0xf2, 0x36, 0x49, 0xc3, 0xf5, 0xa6, 0x19,
-  0x7e, 0xb8, 0xf4, 0x85, 0x31, 0x23, 0x9f, 0x7e, 0x2f, 0xaa, 0xe1, 0xe1,
-  0x07, 0x40, 0x52, 0xd1, 0x28, 0xc8, 0x07, 0xfe, 0xc6, 0x5c, 0x7f, 0x34,
-  0x70, 0xba, 0x74, 0xec, 0x87, 0xe1, 0x89, 0x74, 0x09, 0x8f, 0xe8, 0x7e,
-  0xfa, 0xfc, 0x4d, 0xfc, 0x66, 0x02, 0xf6, 0xb2, 0x62, 0x86, 0x38, 0xc9,
-  0x8c, 0x03, 0x23, 0x02, 0x48, 0x04, 0x12, 0x56, 0x12, 0x74, 0x20, 0xd9,
-  0x0e, 0xb3, 0xbd, 0x6a, 0x29, 0x08, 0xf1, 0xf6, 0xc8, 0x9a, 0x28, 0xfc,
-  0xde, 0x14, 0x60, 0xbe, 0x66, 0x39, 0x64, 0x6a, 0x38, 0xdd, 0xd1, 0x1e,
-  0xba, 0xd7, 0x6a, 0xf1, 0x7a, 0xa9, 0x5f, 0xb3, 0x6f, 0x34, 0x2a, 0x2f,
-  0x27, 0x75, 0xf0, 0xee, 0x69, 0x2c, 0xc8, 0xfb, 0xd2, 0x1a, 0xf1, 0x7e,
-  0xda, 0xf5, 0xeb, 0x30, 0xe3, 0xf9, 0xcc, 0x13, 0x8a, 0xb5, 0xcb, 0x9d,
-  0x4c, 0x90, 0x2e, 0x31, 0xb2, 0x61, 0x2a, 0x3d, 0xdd, 0x9c, 0xfb, 0xeb,
-  0xc6, 0x81, 0xa1, 0xa9, 0x92, 0xeb, 0x6c, 0x8c, 0xa9, 0x6c, 0xce, 0xb9,
-  0x2b, 0x02, 0x14, 0x43, 0x86, 0x22, 0x28, 0x14, 0x9c, 0x49, 0x79, 0x72,
-  0x9e, 0xb2, 0x91, 0xa0, 0x4a, 0x64, 0x9d, 0xba, 0x25, 0xd6, 0x7a, 0xbd,
-  0x1f, 0x97, 0xcf, 0xe1, 0xc8, 0x01, 0xe3, 0xda, 0xa6, 0x28, 0xa0, 0xa0,
-  0x6e, 0x1d, 0x80, 0x4a, 0x88, 0x28, 0x00, 0x00, 0x44, 0x60, 0x56, 0x9e,
-  0xee, 0xc5, 0x55, 0xcd, 0x29, 0x82, 0x55, 0x78, 0xed, 0xd5, 0xb3, 0x29,
-  0x09, 0x3a, 0xd7, 0x0c, 0x06, 0x72, 0x15, 0x0f, 0x9e, 0x08, 0xbc, 0xe7,
-  0xb5, 0xd3, 0x46, 0x84, 0x42, 0x73, 0xa2, 0xda, 0x31, 0xa9, 0x27, 0x6f,
-  0x4b, 0xf6, 0xb3, 0xac, 0xc3, 0x9e, 0xb4, 0xd5, 0xf8, 0xbb, 0x9f, 0xbd,
-  0x91, 0x22, 0xb5, 0xfd, 0xba, 0x91, 0xc1, 0x6e, 0xb0, 0xd5, 0x70, 0x38,
-  0x62, 0x3b, 0x06, 0x20, 0xdc, 0x62, 0xcd, 0x1f, 0x67, 0xd9, 0xf1, 0xf2,
-  0xfa, 0x00, 0xb2, 0x31, 0x02, 0x1f, 0x3e, 0xdf, 0x97, 0x25, 0xa6, 0xe1,
-  0x51, 0xba, 0x37, 0x4c, 0x12, 0x1d, 0x4c, 0x3a, 0xbe, 0x41, 0xc4, 0x3b,
-  0x24, 0x9f, 0x5f, 0xf9, 0xaa, 0x7b, 0xdd, 0x5c, 0xa8, 0x46, 0x77, 0xa1,
-  0x4a, 0xf4, 0x24, 0xd1, 0xac, 0x46, 0x4c, 0xdb, 0xeb, 0x82, 0x98, 0x33,
-  0x33, 0x8d, 0x62, 0x40, 0x98, 0xc1, 0x3a, 0xe3, 0x11, 0xac, 0x2e, 0x97,
-  0x6f, 0x6d, 0x91, 0xc6, 0xc8, 0x62, 0x97, 0x19, 0x86, 0xdd, 0x7b, 0x8d,
-  0xaf, 0xb3, 0xdf, 0xf0, 0xf8, 0x1d, 0x48, 0x03, 0x07, 0x21, 0x1a, 0x88,
-  0x00, 0x00, 0xff, 0xff, 0xfe, 0xf1, 0x6a, 0xa3, 0xa1, 0xc0, 0x24, 0x84,
-  0x23, 0x06, 0x06, 0xa1, 0x00, 0x10, 0x18, 0x00, 0x10, 0x12, 0x54, 0xd8,
-  0xc0, 0xc5, 0xf2, 0xa4, 0x8a, 0x79, 0x40, 0x84, 0x63, 0xb8, 0x8a, 0x1b,
-  0x84, 0x11, 0x31, 0x67, 0x78, 0x24, 0x8a, 0x0c, 0xaa, 0x2c, 0x86, 0x2b,
-  0x70, 0x14, 0xb5, 0xe5, 0xb9, 0xb0, 0x9b, 0x2a, 0x4f, 0x16, 0x8d, 0x8f,
-  0x80, 0xa8, 0xe3, 0x0d, 0x3c, 0xaf, 0x67, 0x0f, 0xaa, 0xe4, 0x92, 0x24,
-  0x28, 0xfc, 0x0f, 0x3b, 0x2c, 0x77, 0x55, 0x73, 0x21, 0x8b, 0x2d, 0x07,
-  0xc2, 0x02, 0xa9, 0x7e, 0x47, 0xec, 0xb8, 0x25, 0xc8, 0x20, 0x8d, 0xb1,
-  0x22, 0xc2, 0xda, 0x93, 0x4d, 0x3a, 0xca, 0xbd, 0x8c, 0xd3, 0x14, 0xea,
-  0x4c, 0xd3, 0x16, 0x26, 0x5c, 0xa8, 0x41, 0x9b, 0xa5, 0x0c, 0x5b, 0xd6,
-  0x06, 0x2a, 0xda, 0x6b, 0xad, 0x46, 0x5a, 0x44, 0x66, 0x60, 0x40, 0x00,
-  0x3c, 0x41, 0xd5, 0x45, 0xd6, 0x5c, 0x48, 0xf6, 0x5a, 0xc8, 0xa0, 0xa5,
-  0x16, 0x45, 0x96, 0x30, 0x49, 0x10, 0xd3, 0xd3, 0xd4, 0x98, 0x4d, 0xb7,
-  0xa4, 0x89, 0x9a, 0xde, 0xb7, 0xc8, 0x8b, 0x64, 0x0d, 0x59, 0x81, 0x04,
-  0x5e, 0xef, 0x7e, 0x61, 0x14, 0xc2, 0xb5, 0x71, 0xd1, 0x16, 0x54, 0x9a,
-  0x48, 0x97, 0xcb, 0x25, 0xba, 0xba, 0xea, 0xa6, 0x82, 0x45, 0x0e, 0x38,
-  0x62, 0x4c, 0x86, 0x82, 0xed, 0x52, 0x2d, 0x16, 0x15, 0x6b, 0x34, 0x96,
-  0x18, 0xb1, 0x8e, 0x09, 0x58, 0x88, 0x78, 0xa4, 0x9a, 0xd7, 0x2e, 0x3b,
-  0x37, 0x96, 0x85, 0x55, 0xee, 0xee, 0x8e, 0x1c, 0x3e, 0x1f, 0x1f, 0xa2,
-  0x00, 0x1e, 0x7e, 0x65, 0x28, 0x22, 0x87, 0x60, 0x0a, 0x81, 0x57, 0x88,
-  0x50, 0x00, 0x2e, 0x55, 0x05, 0x28, 0x62, 0x3a, 0x0c, 0xc5, 0xad, 0xd3,
-  0x38, 0x67, 0xae, 0x2d, 0xa6, 0x00, 0x31, 0x9e, 0xbc, 0x6a, 0x24, 0x14,
-  0xae, 0xb1, 0x64, 0xef, 0x33, 0x14, 0xa8, 0x0c, 0x29, 0x33, 0x2d, 0x20,
-  0xbe, 0x50, 0x33, 0x6d, 0x67, 0x03, 0xb2, 0x37, 0x42, 0x92, 0x17, 0x49,
-  0x48, 0x4a, 0x52, 0x41, 0xad, 0x4c, 0x9d, 0xb5, 0x79, 0xdc, 0x85, 0x04,
-  0x32, 0x0a, 0x8c, 0xa6, 0x37, 0x32, 0x78, 0x78, 0x11, 0x8f, 0xd4, 0xc1,
-  0x99, 0xf6, 0x59, 0xa0, 0xcd, 0x06, 0xaf, 0x7f, 0xf2, 0x9a, 0xc0, 0x34,
-  0x26, 0x02, 0x6a, 0x69, 0x81, 0x02, 0x40, 0xf3, 0x08, 0x64, 0xa4, 0xf4,
-  0xfb, 0x36, 0x4b, 0x97, 0xe2, 0xa7, 0x81, 0xb9, 0x42, 0x94, 0xca, 0x52,
-  0x5e, 0x5d, 0x07, 0x01, 0x73, 0x5c, 0xde, 0xc1, 0x24, 0xad, 0xe1, 0x45,
-  0x2c, 0x0f, 0xbf, 0x19, 0xc9, 0x1d, 0x12, 0x5a, 0x52, 0x88, 0xbf, 0x57,
-  0x2f, 0x8f, 0x46, 0xbe, 0xf0, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0xff,
-  0xff, 0xfe, 0xfd, 0x6b, 0xa2, 0x31, 0x05, 0x44, 0x64, 0x13, 0x11, 0x42,
-  0x10, 0xe1, 0x22, 0xa5, 0x2a, 0xa5, 0x00, 0x80, 0x22, 0x25, 0x60, 0xfa,
-  0x12, 0x01, 0x15, 0xf1, 0xe3, 0xbf, 0xb9, 0xc0, 0x85, 0x3f, 0x9f, 0x20,
-  0x8f, 0xf2, 0x67, 0xba, 0xcb, 0xe1, 0x23, 0xfa, 0x6b, 0x37, 0x6b, 0x9c,
-  0x6e, 0x8a, 0xa8, 0x07, 0xa3, 0x37, 0xaf, 0x78, 0x7a, 0x6b, 0x57, 0x17,
-  0xb6, 0x47, 0x74, 0x88, 0x18, 0xe7, 0xf8, 0xd5, 0x06, 0x99, 0xc8, 0xfc,
-  0xfd, 0x33, 0x87, 0x72, 0x6f, 0xb6, 0x51, 0x97, 0xbd, 0x8c, 0xae, 0x30,
-  0x94, 0x34, 0xd1, 0x02, 0x3b, 0xab, 0x3e, 0xee, 0xd3, 0xff, 0xb5, 0x70,
-  0xc9, 0x2c, 0xb6, 0x85, 0x9b, 0x57, 0x91, 0x00, 0x30, 0xd5, 0x8d, 0x86,
-  0x4c, 0xb3, 0x74, 0x8e, 0xc5, 0xf4, 0x56, 0xc6, 0x95, 0x33, 0x4e, 0x1b,
-  0x6c, 0x23, 0xca, 0x22, 0x71, 0x84, 0x0d, 0x06, 0xba, 0x24, 0xf5, 0x5b,
-  0x0c, 0x81, 0x44, 0x1b, 0x55, 0x1d, 0x84, 0x94, 0xb7, 0xe8, 0x3d, 0x21,
-  0xcd, 0xa7, 0xfe, 0x0e, 0xca, 0x58, 0xb0, 0xb3, 0xda, 0xe4, 0xb1, 0xec,
-  0x0c, 0x56, 0x25, 0x9d, 0x2d, 0xbf, 0x7d, 0xc7, 0x00, 0x8b, 0x12, 0x5b,
-  0xd3, 0xf1, 0xc4, 0xb2, 0xb6, 0x85, 0x9f, 0xc3, 0x46, 0xad, 0xdf, 0x49,
-  0xbe, 0xdb, 0xae, 0x9c, 0x8b, 0xc3, 0x0f, 0x36, 0xf7, 0xcb, 0xbb, 0x2a,
-  0x7c, 0x9e, 0xbf, 0xf7, 0xef, 0x8e, 0xc2, 0x68, 0xf6, 0xda, 0x5a, 0x6a,
-  0xc5, 0x11, 0x75, 0x0e, 0x7b, 0xae, 0xf8, 0xf6, 0x61, 0xd5, 0xdd, 0xdf,
-  0xec, 0xfb, 0xeb, 0xa2, 0x40, 0x1e, 0x7e, 0x64, 0xd4, 0x19, 0x8a, 0x08,
-  0xe1, 0xd8, 0x00, 0xa8, 0x88, 0x00, 0x00, 0x2d, 0x01, 0xad, 0x0c, 0x89,
-  0x80, 0x2e, 0x30, 0x8a, 0x00, 0xbc, 0x21, 0x07, 0x0e, 0x05, 0x5e, 0x7b,
-  0xf5, 0x95, 0x69, 0x5f, 0x6b, 0x8c, 0x41, 0x9e, 0x43, 0x80, 0x91, 0x71,
-  0x00, 0x30, 0x70, 0xc8, 0x60, 0x5d, 0x2a, 0x81, 0xe8, 0xc1, 0xb0, 0xed,
-  0x51, 0x1d, 0x73, 0xac, 0x3b, 0xea, 0xa5, 0x83, 0x65, 0x65, 0xf0, 0x6c,
-  0x29, 0xf3, 0xf7, 0x96, 0x89, 0xa7, 0xae, 0xa0, 0xdc, 0x71, 0x53, 0x72,
-  0x3a, 0x1c, 0xeb, 0xb6, 0x50, 0xc2, 0x6b, 0x4b, 0xd3, 0x37, 0x2f, 0xb8,
-  0xc2, 0xc9, 0x44, 0xbe, 0x43, 0xf0, 0x0e, 0xe9, 0x0a, 0xa1, 0x24, 0x42,
-  0xef, 0x24, 0x72, 0xee, 0xb8, 0x8e, 0xa0, 0x0c, 0x65, 0xd0, 0xaa, 0xbc,
-  0x57, 0x1d, 0x79, 0x72, 0x30, 0x35, 0x82, 0xc2, 0x7d, 0x94, 0xbe, 0x1e,
-  0x4d, 0x3b, 0x00, 0x10, 0xcc, 0x8d, 0x91, 0x4c, 0xa0, 0xd4, 0xfd, 0x36,
-  0xc1, 0x76, 0x5c, 0x52, 0x28, 0x39, 0x7c, 0xbf, 0xf2, 0x75, 0x1a, 0x1e,
-  0x26, 0xc0, 0x0e, 0x21, 0x1a, 0x88, 0x00, 0x3f, 0xff, 0xff, 0xfe, 0xfd,
-  0x6c, 0xa2, 0xa2, 0x04, 0xea, 0x66, 0x1a, 0x84, 0x21, 0x67, 0x00, 0xa5,
-  0x00, 0x00, 0x0b, 0x28, 0x77, 0xbf, 0xd9, 0xf9, 0xe7, 0xd0, 0xff, 0xd0,
-  0x48, 0x01, 0x26, 0x40, 0x54, 0x07, 0xc7, 0x3d, 0xe1, 0xdd, 0x7b, 0xf7,
-  0xeb, 0xda, 0xef, 0x87, 0xcf, 0x5b, 0x4b, 0x57, 0xc9, 0xa0, 0xde, 0x1d,
-  0xdb, 0xb8, 0x39, 0x27, 0xff, 0x3a, 0x7f, 0x75, 0xe8, 0x32, 0x80, 0x2a,
-  0xb8, 0xdc, 0x64, 0x83, 0xca, 0x53, 0x4e, 0x7f, 0x3b, 0x7c, 0x61, 0xf1,
-  0x2b, 0x18, 0x60, 0x97, 0x3b, 0x72, 0xb9, 0x37, 0x1c, 0x2d, 0xe9, 0x99,
-  0x6d, 0x95, 0x21, 0x3e, 0xf8, 0xe3, 0x06, 0x77, 0xb1, 0xaf, 0xed, 0xaa,
-  0xd2, 0x05, 0x66, 0x13, 0x59, 0xa9, 0xf0, 0xaa, 0xcd, 0x18, 0xea, 0x9e,
-  0x32, 0xc7, 0xce, 0x7e, 0x95, 0x70, 0xc6, 0xb5, 0xa7, 0xe9, 0x08, 0x57,
-  0xb5, 0xbc, 0xb4, 0x96, 0xf1, 0x2f, 0x1e, 0x38, 0xad, 0xb3, 0x56, 0x37,
-  0x2d, 0xd3, 0x6e, 0x5e, 0x10, 0xce, 0x36, 0xec, 0x82, 0x8d, 0xc1, 0x0d,
-  0xd5, 0xc9, 0xbd, 0xae, 0xe8, 0xa3, 0x0a, 0x24, 0xc4, 0xe5, 0x33, 0x51,
-  0x46, 0xc2, 0xe3, 0x8e, 0xdc, 0xe8, 0x4e, 0x33, 0x1b, 0x2a, 0xf3, 0x55,
-  0xd5, 0x7d, 0xa2, 0xef, 0x57, 0xdd, 0xe1, 0x24, 0x87, 0xe1, 0xe1, 0xa0,
-  0xf9, 0xd9, 0x19, 0xe2, 0xd6, 0x1d, 0xd4, 0x6d, 0xef, 0x0a, 0xf2, 0xbb,
-  0xef, 0xf7, 0xf4, 0xbc, 0xf4, 0x7c, 0x57, 0x8d, 0xed, 0x35, 0x96, 0xfd,
-  0xe7, 0x27, 0xb2, 0x4e, 0x73, 0x72, 0xed, 0xd7, 0xeb, 0x8f, 0x5e, 0x7c,
-  0x79, 0x00, 0x3c, 0xfb, 0x54, 0x62, 0xd2, 0x3a, 0x0a, 0x06, 0xa1, 0xdc,
-  0xa0, 0x54, 0x00, 0x00, 0x00, 0x12, 0x26, 0x0c, 0x56, 0x5e, 0x55, 0xb9,
-  0xbc, 0x64, 0x62, 0x8d, 0x74, 0xc1, 0xa7, 0xa2, 0x39, 0x55, 0x6e, 0xec,
-  0xc3, 0x3b, 0xc7, 0x84, 0x67, 0x75, 0x57, 0xa4, 0x76, 0x93, 0xfc, 0xb3,
-  0x78, 0x61, 0x81, 0xe3, 0xcf, 0xe3, 0x44, 0xdc, 0xdc, 0x6f, 0xb5, 0x9a,
-  0x39, 0xdd, 0xd1, 0x01, 0xea, 0xee, 0xad, 0x15, 0x86, 0x43, 0x30, 0x39,
-  0x0a, 0x52, 0x9c, 0x7d, 0x2c, 0x86, 0xc3, 0xd0, 0xb0, 0x81, 0x4a, 0xd2,
-  0xc5, 0x18, 0xdf, 0x51, 0x52, 0x84, 0x9d, 0xde, 0x15, 0x51, 0x31, 0x54,
-  0x0f, 0x29, 0xfc, 0xdf, 0x0c, 0xe7, 0xd7, 0x48, 0xb4, 0x50, 0xed, 0xcb,
-  0xee, 0xdb, 0xc7, 0x19, 0xc2, 0x64, 0xc1, 0xdc, 0x95, 0x05, 0x1a, 0x2c,
-  0x56, 0xcd, 0x20, 0xf4, 0x0c, 0x5b, 0x54, 0x8f, 0x96, 0x89, 0x9d, 0x5b,
-  0x00, 0x30, 0x8c, 0x17, 0xd0, 0x86, 0xd2, 0x62, 0x76, 0x42, 0x39, 0x76,
-  0x52, 0xda, 0xa2, 0xd7, 0x5b, 0x73, 0x3f, 0x1b, 0xa7, 0xcb, 0x1f, 0x57,
-  0x3e, 0x5d, 0x1c, 0x00, 0x38, 0x21, 0x1a, 0x88, 0x00, 0x03, 0xff, 0xff,
-  0xfe, 0xfd, 0x68, 0xa4, 0x30, 0x90, 0x44, 0xd4, 0x2c, 0x0d, 0xc2, 0x02,
-  0x12, 0x4b, 0xa2, 0x84, 0x00, 0x02, 0x21, 0x43, 0xdb, 0x48, 0xa4, 0xd5,
-  0xbb, 0x89, 0x11, 0x5f, 0xc4, 0xe6, 0x72, 0x48, 0x3f, 0x3d, 0xd4, 0xc5,
-  0xe3, 0x69, 0x94, 0x50, 0xfd, 0x5d, 0xd9, 0x3f, 0x90, 0xa7, 0x7c, 0xad,
-  0x57, 0xe3, 0x5a, 0x5d, 0xa8, 0xea, 0xac, 0x73, 0x69, 0x1d, 0x2e, 0xcf,
-  0x39, 0x4d, 0xab, 0xbf, 0x2b, 0x9e, 0x70, 0x57, 0x9a, 0xd2, 0x0d, 0x7d,
-  0xaa, 0x3f, 0xaf, 0x42, 0x8a, 0xfb, 0x1c, 0xeb, 0xbe, 0x21, 0xf8, 0x11,
-  0x6d, 0xba, 0x38, 0x8c, 0xc3, 0x3a, 0x66, 0x69, 0xf6, 0x5b, 0xea, 0xe8,
-  0xa5, 0x87, 0x8e, 0xde, 0xe2, 0x48, 0x07, 0xe6, 0x4a, 0x07, 0x03, 0xa5,
-  0x85, 0xc5, 0x7c, 0x09, 0x01, 0x8c, 0x40, 0xe9, 0x07, 0x44, 0x97, 0xbc,
-  0x41, 0x11, 0x9d, 0xdf, 0x43, 0xa5, 0x64, 0x01, 0x9c, 0x3f, 0x6f, 0x92,
-  0x56, 0xe7, 0x00, 0x87, 0x16, 0x37, 0x20, 0xa2, 0xb1, 0xe8, 0x4b, 0x77,
-  0xa8, 0x80, 0x30, 0x6a, 0xbd, 0x56, 0x08, 0x42, 0xba, 0xe9, 0x70, 0xa6,
-  0xa5, 0x60, 0xfe, 0x5d, 0xed, 0x27, 0xda, 0xab, 0xd2, 0x25, 0xa1, 0xcb,
-  0x61, 0x53, 0x5b, 0x15, 0xf7, 0xbf, 0x23, 0xd1, 0xcd, 0x48, 0xa9, 0x00,
-  0x5a, 0x46, 0xde, 0xb8, 0x21, 0x12, 0x24, 0xed, 0x08, 0x64, 0x24, 0x63,
-  0x02, 0x4d, 0x85, 0x74, 0x37, 0xaf, 0xc1, 0xa7, 0x4e, 0x0d, 0x75, 0xeb,
-  0xe5, 0x5d, 0x29, 0x8a, 0x73, 0xe5, 0x13, 0x21, 0x98, 0xe7, 0xde, 0xbd,
-  0xd6, 0x7d, 0xec, 0x38, 0xdd, 0x3d, 0x4c, 0x00, 0x3d, 0xfc, 0x49, 0x22,
-  0x52, 0x22, 0x0e, 0x06, 0xe1, 0xd8, 0x09, 0x43, 0x2d, 0x56, 0x02, 0x81,
-  0x03, 0x35, 0x0a, 0x13, 0x10, 0xda, 0x5c, 0x61, 0x5b, 0x1d, 0x82, 0x83,
-  0x3e, 0x67, 0x16, 0x09, 0x8b, 0x00, 0x82, 0xa7, 0x3f, 0x77, 0xea, 0x75,
-  0x73, 0xa4, 0x8f, 0x6b, 0xa5, 0x3c, 0xb7, 0x43, 0x45, 0x1a, 0x5b, 0xbe,
-  0x54, 0x4d, 0x41, 0x12, 0x78, 0x6b, 0x59, 0x55, 0x33, 0x31, 0x6d, 0x31,
-  0xb6, 0x99, 0x2d, 0x21, 0x69, 0xe2, 0xe3, 0xd0, 0xe7, 0x3d, 0x81, 0xd9,
-  0xe3, 0xde, 0xa5, 0x86, 0x4d, 0x31, 0xda, 0xc6, 0x1a, 0x18, 0x76, 0xb7,
-  0xe2, 0x7a, 0x2f, 0x05, 0xf7, 0x3e, 0x3e, 0x4d, 0x7a, 0x9a, 0xbc, 0xbf,
-  0xb4, 0xea, 0x3e, 0xc3, 0xff, 0xf9, 0x0d, 0xea, 0x0f, 0x13, 0x2e, 0xd0,
-  0x9a, 0xb8, 0x1f, 0xac, 0x0e, 0xa4, 0xb5, 0x14, 0x93, 0x15, 0x01, 0x42,
-  0xd5, 0xc6, 0x42, 0x35, 0xb6, 0x0b, 0xc8, 0x80, 0xa8, 0x6d, 0xa4, 0xda,
-  0x5d, 0x5c, 0xad, 0x10, 0x35, 0x2d, 0x03, 0xd7, 0x7a, 0x59, 0xfb, 0xde,
-  0xaf, 0xe2, 0xb7, 0xfb, 0xe9, 0x00, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00,
-  0xff, 0xff, 0xff, 0x01, 0x6a, 0xa4, 0xa0, 0x85, 0xc8, 0x26, 0x0c, 0x0c,
-  0xc2, 0x05, 0xe5, 0xe9, 0x2e, 0xab, 0x54, 0x18, 0x00, 0x0b, 0x25, 0x4e,
-  0x45, 0x78, 0x40, 0xc4, 0xca, 0xe3, 0x21, 0x2c, 0xbb, 0xeb, 0x9d, 0x7a,
-  0xe6, 0x83, 0x1e, 0x93, 0xf7, 0xf2, 0x0f, 0x1b, 0x37, 0xac, 0x9d, 0x0d,
-  0xad, 0xb7, 0x73, 0xbf, 0xbf, 0xfc, 0x6f, 0xa2, 0xb3, 0xde, 0xab, 0xf7,
-  0x8e, 0xdf, 0x5e, 0xd5, 0xe5, 0x55, 0x99, 0x26, 0x51, 0x4a, 0xd4, 0x9f,
-  0xce, 0xd6, 0x1b, 0xde, 0xef, 0x34, 0x12, 0x54, 0x8c, 0x6a, 0x16, 0x50,
-  0x84, 0x5c, 0xa0, 0xa4, 0x83, 0xa9, 0x8e, 0x24, 0x21, 0x0e, 0xfe, 0xd9,
-  0xac, 0x67, 0xb0, 0x1d, 0x09, 0x5e, 0xdd, 0x3c, 0xf5, 0xc8, 0x56, 0x34,
-  0xbc, 0x2d, 0x49, 0xc4, 0xa9, 0x48, 0xcd, 0x3b, 0x30, 0xad, 0x3c, 0x92,
-  0x42, 0x36, 0xb0, 0xa4, 0x5d, 0xb8, 0x72, 0x95, 0xd7, 0xba, 0x32, 0x97,
-  0x84, 0x92, 0x93, 0xa4, 0x0b, 0x49, 0x79, 0x5e, 0xc7, 0x75, 0x23, 0x7a,
-  0xda, 0x59, 0xa5, 0xf3, 0x8c, 0x3b, 0x99, 0x10, 0x10, 0xf6, 0x5c, 0x7f,
-  0x0a, 0xec, 0x3c, 0xbc, 0x1b, 0xe4, 0xc5, 0x21, 0x19, 0x92, 0xe5, 0x51,
-  0x69, 0x2e, 0x1d, 0xf9, 0xcc, 0x65, 0xa6, 0x33, 0x3f, 0x44, 0x74, 0xa8,
-  0x07, 0x22, 0xd2, 0xae, 0x94, 0x58, 0x2f, 0x94, 0x8a, 0x0d, 0x92, 0x71,
-  0x27, 0x7b, 0x5c, 0x81, 0x25, 0xcf, 0xaf, 0xaa, 0x04, 0x6c, 0x32, 0x8f,
-  0x82, 0x4d, 0x06, 0x12, 0xbb, 0x2d, 0xdb, 0x1f, 0x4c, 0xf3, 0x9f, 0xd0,
-  0xf2, 0x81, 0xeb, 0xe6, 0x4f, 0x31, 0xb0, 0xe0, 0x2a, 0x1d, 0x82, 0xa0,
-  0xa8, 0x2a, 0x48, 0x00, 0x00, 0x84, 0xd8, 0x3a, 0x33, 0x55, 0xb7, 0x9b,
-  0x2d, 0x0a, 0x10, 0x21, 0x63, 0xc1, 0x75, 0xb1, 0xf9, 0x3a, 0xd7, 0xe4,
-  0x96, 0x03, 0x92, 0x15, 0xb5, 0x66, 0x31, 0x9d, 0x2e, 0xf3, 0x0b, 0x62,
-  0x51, 0x14, 0x0a, 0x6c, 0xc9, 0x61, 0xd6, 0x07, 0x88, 0x56, 0xf0, 0x01,
-  0xa2, 0x3b, 0x88, 0x1d, 0x4a, 0xab, 0xd1, 0xab, 0x36, 0xbf, 0xdd, 0x1a,
-  0x53, 0x19, 0xfb, 0x55, 0x28, 0x1f, 0xac, 0x63, 0xcd, 0x34, 0x68, 0x56,
-  0x15, 0x97, 0xa1, 0xeb, 0xbd, 0x44, 0x19, 0x77, 0x5d, 0x13, 0x8c, 0x76,
-  0xd2, 0x72, 0xa5, 0x32, 0x19, 0x49, 0x73, 0xbf, 0x56, 0x76, 0x60, 0x68,
-  0xd8, 0xe3, 0x33, 0x10, 0xd3, 0xed, 0x92, 0xa1, 0x97, 0xb6, 0x4b, 0xe9,
-  0x33, 0x0a, 0x6d, 0x91, 0xc4, 0x4e, 0xb6, 0xe9, 0xd3, 0x89, 0xc2, 0x6a,
-  0xfc, 0x6a, 0x0c, 0x11, 0x09, 0x20, 0x7a, 0xe7, 0xed, 0xe7, 0xcf, 0xdb,
-  0xaf, 0xf1, 0xa0, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0xff, 0xff, 0xff,
-  0x03, 0x6a, 0xa2, 0x58, 0xa0, 0x68, 0x42, 0x6b, 0x0e, 0x04, 0x61, 0x10,
-  0xe8, 0x34, 0xa4, 0xa9, 0x92, 0xa0, 0x04, 0x0c, 0xb4, 0x39, 0x19, 0x72,
-  0xb3, 0x37, 0xe1, 0x72, 0x60, 0xfa, 0x57, 0xa6, 0x69, 0x7d, 0xd5, 0xab,
-  0x3d, 0xc7, 0xc5, 0x73, 0x4d, 0xde, 0xaf, 0xa8, 0x4e, 0xec, 0xa2, 0x4d,
-  0x8f, 0xcb, 0xe3, 0x93, 0xb1, 0xaa, 0x00, 0xeb, 0x18, 0x9d, 0x25, 0x7f,
-  0x48, 0xfc, 0xf5, 0x3e, 0x46, 0x01, 0xa2, 0xdc, 0xc8, 0x6c, 0x6b, 0x2b,
-  0x2c, 0xfe, 0x07, 0x86, 0xb5, 0xd7, 0x23, 0xf2, 0x0e, 0x76, 0xfe, 0x12,
-  0xdb, 0xb7, 0x67, 0x15, 0xe6, 0x08, 0x91, 0x5a, 0xda, 0x9e, 0x59, 0x73,
-  0xa5, 0x85, 0xdf, 0x55, 0xe9, 0x8b, 0x80, 0xbf, 0x87, 0x4f, 0x16, 0xb3,
-  0x23, 0xcd, 0xa4, 0x36, 0x54, 0x27, 0x14, 0xe8, 0x3d, 0x8c, 0x35, 0xc2,
-  0xc5, 0x62, 0x1f, 0x51, 0xd0, 0x2e, 0x58, 0x7d, 0x9e, 0x11, 0xae, 0x13,
-  0xb0, 0xe0, 0x25, 0x0b, 0x03, 0xe6, 0x94, 0xab, 0xd1, 0x73, 0xf8, 0xcb,
-  0xf3, 0x4e, 0xf5, 0xa9, 0x7e, 0x1d, 0x10, 0x29, 0x65, 0x90, 0x17, 0x54,
-  0xda, 0x79, 0x95, 0x99, 0x65, 0xc7, 0xdb, 0x45, 0x3e, 0x9f, 0x97, 0x5a,
-  0x31, 0x86, 0xcd, 0x15, 0x1c, 0x01, 0xb8, 0xc2, 0xa0, 0x1b, 0x94, 0x4c,
-  0x33, 0x92, 0xbb, 0x62, 0xb0, 0xa8, 0xb2, 0x79, 0xea, 0x89, 0xf3, 0xc2,
-  0xad, 0xba, 0x75, 0x61, 0x9c, 0xd3, 0x13, 0x55, 0xfa, 0xc6, 0x6e, 0xf5,
-  0x38, 0xb3, 0x67, 0xc2, 0xd6, 0x8e, 0x9a, 0x48, 0x12, 0xd6, 0x08, 0xe9,
-  0xbe, 0xc0, 0xe4, 0x97, 0xc2, 0x14, 0x42, 0x4a, 0x91, 0x61, 0x8b, 0x18,
-  0x8f, 0xfa, 0xcf, 0x7d, 0xff, 0x5b, 0xea, 0xe0, 0xf7, 0xf3, 0x19, 0x30,
-  0xe0, 0x2e, 0x1d, 0x80, 0x0a, 0x42, 0xaa, 0x00, 0x00, 0x23, 0x34, 0x0a,
-  0x5f, 0x94, 0xa9, 0x06, 0xe8, 0x57, 0xf4, 0x1e, 0x64, 0x50, 0xcd, 0x2c,
-  0x76, 0x92, 0xf1, 0x51, 0x74, 0xf0, 0x6a, 0x55, 0x9e, 0xe4, 0x91, 0xd5,
-  0x2a, 0x91, 0xed, 0x68, 0x90, 0x2b, 0x05, 0xb2, 0x55, 0xaa, 0x13, 0x6b,
-  0x92, 0xb8, 0xe1, 0x59, 0xe7, 0x22, 0xbe, 0xde, 0x45, 0xe2, 0xc9, 0xf4,
-  0x81, 0x0a, 0x4b, 0x31, 0x42, 0x29, 0x0f, 0xe8, 0x89, 0x14, 0x9a, 0x1e,
-  0xb0, 0xb7, 0x88, 0xae, 0x4e, 0xbf, 0x4d, 0x34, 0x6f, 0xb2, 0xf9, 0x44,
-  0x02, 0xea, 0x19, 0xa8, 0xd3, 0x68, 0x95, 0x1a, 0x6c, 0xde, 0xa0, 0x00,
-  0x62, 0x0d, 0x7a, 0x03, 0xb7, 0x53, 0x06, 0x31, 0x2f, 0x02, 0x57, 0x0f,
-  0x8f, 0x13, 0x49, 0x97, 0xa9, 0xa9, 0xc6, 0xf0, 0xb3, 0xe9, 0xec, 0x24,
-  0x03, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x07, 0x6a,
-  0xa4, 0x31, 0x10, 0x44, 0x94, 0x2c, 0x0d, 0x42, 0x01, 0x5c, 0x4b, 0xd5,
-  0x2e, 0xae, 0xaf, 0x21, 0x40, 0x02, 0x52, 0xd8, 0x35, 0xf7, 0xa7, 0xf8,
-  0x19, 0x14, 0x0f, 0xef, 0xb9, 0x4e, 0xe9, 0x1d, 0xa6, 0x2e, 0xfe, 0x98,
-  0xfd, 0x13, 0xb8, 0x22, 0xba, 0xb9, 0xf9, 0x1a, 0xe2, 0x51, 0x4e, 0x5b,
-  0xed, 0xdb, 0x8f, 0x5b, 0xbc, 0xf0, 0x39, 0xe6, 0x75, 0x4b, 0xe8, 0x1e,
-  0xf8, 0x7b, 0x2f, 0x27, 0xb1, 0xc7, 0x99, 0xc7, 0xff, 0x1c, 0x8a, 0xc0,
-  0xe9, 0x9b, 0x56, 0xd4, 0x2a, 0x57, 0x7b, 0x34, 0xfb, 0x65, 0x09, 0x34,
-  0xb5, 0x95, 0x7d, 0x20, 0x96, 0x1d, 0x71, 0xb2, 0x68, 0x71, 0xae, 0xb6,
-  0x5b, 0x2c, 0x31, 0xba, 0x25, 0x5c, 0x2c, 0xf8, 0x25, 0x00, 0x96, 0xa3,
-  0x49, 0x1a, 0x39, 0xd3, 0xa1, 0x07, 0x52, 0xd3, 0x3a, 0xd6, 0xb1, 0x54,
-  0xe1, 0x71, 0x24, 0x08, 0x94, 0x15, 0xe2, 0x7c, 0x26, 0xb5, 0xc6, 0x13,
-  0x09, 0x58, 0xc8, 0xb7, 0x8c, 0x48, 0x03, 0x6a, 0xcc, 0xd5, 0x13, 0x47,
-  0xc6, 0xb7, 0x79, 0x70, 0x62, 0x41, 0x0a, 0xb1, 0xd7, 0xf0, 0x8b, 0xe5,
-  0x6b, 0xd0, 0xc9, 0xe1, 0x73, 0x8c, 0x67, 0x06, 0x75, 0x1c, 0x1d, 0xac,
-  0xa2, 0xa9, 0xa7, 0xd3, 0xa7, 0x0c, 0x0d, 0xee, 0x86, 0x5f, 0xd3, 0x95,
-  0x78, 0xc1, 0xae, 0x00, 0x23, 0x53, 0x9d, 0x98, 0x2c, 0xdb, 0x65, 0x86,
-  0xd0, 0x0b, 0x6e, 0x70, 0xab, 0xa7, 0xd8, 0xca, 0x65, 0xd6, 0x34, 0x26,
-  0x27, 0x28, 0xf5, 0x55, 0x3a, 0x0a, 0x58, 0x52, 0x28, 0x6d, 0x15, 0x84,
-  0x87, 0x10, 0x95, 0xfc, 0xa1, 0x66, 0x4d, 0x2d, 0x3f, 0xaf, 0xaa, 0xf1,
-  0xf2, 0xee, 0xfa, 0x64, 0x01, 0xef, 0xec, 0x2f, 0x62, 0x28, 0x76, 0x00,
-  0x05, 0x44, 0x95, 0x50, 0x00, 0x08, 0xaa, 0x09, 0xee, 0x99, 0xfb, 0x7f,
-  0x69, 0x9a, 0x6c, 0x82, 0x6d, 0x74, 0x1d, 0xe4, 0xb1, 0xc6, 0x41, 0xa5,
-  0x6e, 0x02, 0x13, 0x21, 0x2d, 0x59, 0xc4, 0xf0, 0xda, 0xd6, 0x49, 0xe8,
-  0xac, 0xf6, 0xa7, 0xea, 0xfd, 0x59, 0x37, 0xe0, 0x2b, 0xe6, 0xbe, 0x15,
-  0xf9, 0xbd, 0xff, 0xb5, 0xe7, 0x6e, 0xa3, 0xee, 0xb9, 0x9d, 0x6b, 0x5a,
-  0xdf, 0xad, 0xc0, 0x00, 0x9a, 0x8e, 0x68, 0xd9, 0xea, 0x96, 0xc8, 0xee,
-  0xa1, 0x13, 0xc6, 0xb4, 0xae, 0xab, 0xf8, 0x33, 0xd7, 0x34, 0xfb, 0xde,
-  0x9a, 0x58, 0x37, 0x02, 0x88, 0x80, 0x28, 0x68, 0x3a, 0x87, 0x9e, 0x70,
-  0xe2, 0x7b, 0x69, 0xbe, 0xe0, 0x06, 0x8c, 0xbd, 0x3f, 0x3e, 0xdc, 0xf6,
-  0xc6, 0x24, 0x03, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0xff, 0xff, 0xff,
-  0x09, 0x6f, 0x82, 0x21, 0x45, 0x88, 0x16, 0x1c, 0x05, 0x42, 0x22, 0x4c,
-  0x3d, 0x8b, 0x82, 0x50, 0xa5, 0x10, 0x04, 0x95, 0x26, 0x0b, 0xf5, 0x98,
-  0x3f, 0x1b, 0xf1, 0x42, 0x60, 0x05, 0x76, 0x4f, 0x26, 0xe9, 0xbd, 0x09,
-  0xff, 0xfb, 0x77, 0x98, 0x3b, 0xc2, 0x90, 0xca, 0x6e, 0x8b, 0xc2, 0x0d,
-  0xa2, 0x0e, 0xf3, 0x6e, 0xba, 0xed, 0x7e, 0x4a, 0xcb, 0x55, 0x52, 0x8d,
-  0x25, 0xbd, 0x54, 0x1a, 0x5d, 0x2b, 0xa1, 0x54, 0xcb, 0x54, 0xa2, 0x6a,
-  0xb3, 0x42, 0x3a, 0xdb, 0xdf, 0x4a, 0x56, 0xd5, 0x0f, 0x32, 0x08, 0x72,
-  0x2b, 0x2c, 0x49, 0x46, 0x26, 0xe5, 0x90, 0x28, 0xae, 0x02, 0x1d, 0x22,
-  0x56, 0x31, 0x48, 0x7f, 0xdc, 0xc8, 0x76, 0xa7, 0x4b, 0xaf, 0x36, 0xa1,
-  0x22, 0xe1, 0xab, 0x57, 0x72, 0xf5, 0x46, 0xb2, 0x56, 0x45, 0x00, 0x2e,
-  0xc7, 0x7d, 0x94, 0x52, 0x53, 0x94, 0xeb, 0x7c, 0xf4, 0x55, 0x5c, 0xa7,
-  0xb9, 0x54, 0x0c, 0x3f, 0x0a, 0xde, 0x9e, 0x8b, 0xdd, 0x92, 0x72, 0xad,
-  0xa9, 0x8f, 0x65, 0x2d, 0xd2, 0xac, 0xa2, 0xea, 0x42, 0x95, 0x96, 0x55,
-  0x03, 0xd4, 0x38, 0x43, 0xa0, 0x8a, 0xcd, 0x8a, 0xbb, 0xe1, 0x39, 0xa0,
-  0xa6, 0x8d, 0x51, 0x63, 0x69, 0x2a, 0x86, 0x59, 0xae, 0x97, 0xf0, 0xdd,
-  0x56, 0x4f, 0x53, 0x31, 0xe2, 0x06, 0x5c, 0x6b, 0xa3, 0x61, 0xc9, 0xac,
-  0x42, 0xda, 0x2a, 0x22, 0xb6, 0x81, 0x2b, 0xbb, 0x9f, 0x53, 0x52, 0xca,
-  0x4d, 0x11, 0xaa, 0xb3, 0x63, 0xe7, 0xf5, 0x74, 0xd7, 0x67, 0xd5, 0x9e,
-  0x00, 0x0f, 0x5f, 0x33, 0x81, 0x09, 0x88, 0xe1, 0xd8, 0x00, 0x54, 0x95,
-  0x56, 0x05, 0xb0, 0x01, 0x10, 0x03, 0x40, 0xb9, 0x28, 0x68, 0x0c, 0xe0,
-  0xbc, 0x4b, 0xfc, 0x1b, 0xa3, 0x22, 0x4a, 0xab, 0x69, 0x05, 0x13, 0x5e,
-  0x4e, 0xd1, 0xb7, 0x12, 0xf2, 0x28, 0x82, 0xd1, 0x88, 0x53, 0x1b, 0x0e,
-  0x0e, 0x1a, 0x99, 0xa7, 0xa8, 0x11, 0x97, 0x38, 0x1a, 0x3d, 0x54, 0xe9,
-  0x3b, 0x58, 0xef, 0x48, 0xea, 0xd4, 0x05, 0x9f, 0x53, 0x6e, 0x18, 0x23,
-  0x50, 0x82, 0x49, 0x4b, 0x32, 0x1b, 0xed, 0x7a, 0x00, 0x2c, 0xbb, 0xa9,
-  0xae, 0xb7, 0x46, 0xbe, 0xd7, 0x36, 0xfd, 0x60, 0x34, 0xff, 0x51, 0x2c,
-  0x76, 0xe0, 0x09, 0xd8, 0xab, 0x68, 0xc2, 0xdd, 0x49, 0xd2, 0xdb, 0xa6,
-  0x54, 0xba, 0x8a, 0xba, 0xba, 0xfe, 0x4f, 0x57, 0xd7, 0xb8, 0xba, 0x3e,
-  0x70, 0x0e, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x11, 0x6f,
-  0xa1, 0xb1, 0x10, 0x62, 0xc6, 0x20, 0x85, 0xad, 0xe9, 0x67, 0xc0, 0xb5,
-  0x2d, 0xbb, 0x03, 0x02, 0x02, 0x43, 0xb0, 0xc5, 0xdf, 0xd3, 0x1f, 0x63,
-  0xf1, 0x52, 0x1b, 0x2f, 0x1f, 0x82, 0x83, 0x15, 0x08, 0x32, 0xf8, 0xab,
-  0x1c, 0xfd, 0x4f, 0xce, 0x34, 0x87, 0x8a, 0xc6, 0xb9, 0x0e, 0x37, 0xf1,
-  0xa5, 0x31, 0x7f, 0x7a, 0x3c, 0x9a, 0x3a, 0x56, 0x60, 0x7e, 0xaf, 0x18,
-  0x73, 0x6f, 0x90, 0x34, 0xf5, 0x8e, 0x63, 0x8f, 0x35, 0xdd, 0x76, 0x17,
-  0xe2, 0x8d, 0x51, 0x8c, 0x9f, 0x58, 0xb3, 0x19, 0x84, 0xca, 0x9c, 0xc9,
-  0xbc, 0xc0, 0x56, 0xca, 0x12, 0x6f, 0x8c, 0xee, 0xa3, 0x7d, 0x97, 0x45,
-  0x59, 0x32, 0x9a, 0xc5, 0x2f, 0x5b, 0x39, 0x57, 0x40, 0xf7, 0xc1, 0xfd,
-  0xfd, 0x13, 0x19, 0xb8, 0xee, 0x8a, 0xab, 0xd4, 0xcc, 0x63, 0xbd, 0x38,
-  0xea, 0x8d, 0x70, 0xa4, 0x40, 0x8c, 0xe1, 0x89, 0xd4, 0x50, 0x23, 0x7c,
-  0x88, 0x8c, 0xe1, 0x62, 0x8b, 0x2c, 0xb6, 0xca, 0x4d, 0x81, 0xf6, 0x66,
-  0xb6, 0xac, 0xdb, 0xe8, 0xef, 0xaa, 0xfe, 0x8a, 0x3a, 0x79, 0x62, 0x86,
-  0x4a, 0xfe, 0xd9, 0xdd, 0xeb, 0xc1, 0xd9, 0xa5, 0x01, 0x02, 0x84, 0x96,
-  0x8b, 0x14, 0x41, 0x9c, 0x9a, 0x8f, 0x26, 0x3d, 0xae, 0xdc, 0xac, 0xee,
-  0x8b, 0x17, 0xde, 0xbf, 0xa9, 0x4c, 0xec, 0xc3, 0xdd, 0x3f, 0xe0, 0x24,
-  0x97, 0x4c, 0xb1, 0x0b, 0x53, 0x62, 0xfe, 0x5e, 0x13, 0x48, 0x42, 0x78,
-  0xfc, 0xa7, 0x9f, 0xce, 0x7f, 0xbf, 0x9f, 0xf3, 0xc8, 0x0f, 0x5f, 0x44,
-  0x20, 0xb1, 0x04, 0xc2, 0x80, 0xb8, 0x76, 0x00, 0x05, 0x2f, 0x2e, 0xc0,
-  0x60, 0x02, 0xea, 0xf0, 0x58, 0x75, 0x7e, 0xc3, 0x54, 0xd2, 0x80, 0x9a,
-  0x71, 0x68, 0x70, 0x55, 0xf8, 0x4c, 0xf5, 0x38, 0x5a, 0xd2, 0xae, 0x48,
-  0x85, 0x16, 0xb7, 0x79, 0xd1, 0x69, 0x95, 0xca, 0x21, 0x23, 0x74, 0x62,
-  0xbc, 0xf3, 0x28, 0xa4, 0x30, 0xd0, 0xa7, 0xd9, 0xfa, 0xc1, 0x02, 0x36,
-  0xd7, 0x7b, 0x7c, 0x8b, 0x61, 0xbf, 0xaa, 0x80, 0xc0, 0xa7, 0x81, 0x17,
-  0x7c, 0xaa, 0x2a, 0xd3, 0x5a, 0xf9, 0xc7, 0x88, 0x26, 0x07, 0xca, 0xf4,
-  0x0e, 0xae, 0x98, 0x23, 0xb1, 0x36, 0xce, 0x3e, 0xcb, 0xbd, 0x5c, 0xe5,
-  0x33, 0x13, 0xd0, 0xfa, 0xdd, 0xc0, 0x6c, 0x94, 0x81, 0xb0, 0xdb, 0x84,
-  0x6a, 0x8c, 0x4d, 0x9c, 0xd9, 0x1b, 0x70, 0x8f, 0x27, 0x36, 0x41, 0x0a,
-  0xc9, 0xca, 0xd4, 0xbe, 0x56, 0x3e, 0x27, 0xc7, 0xe0, 0x80, 0x38, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x05, 0x6a, 0xa2, 0x58, 0x68,
-  0x8c, 0x42, 0x11, 0x94, 0x88, 0x84, 0x61, 0x40, 0x4c, 0x20, 0x38, 0x17,
-  0x60, 0x01, 0x44, 0x40, 0x12, 0xa7, 0x63, 0x26, 0x0b, 0x05, 0x95, 0xf4,
-  0x72, 0xfc, 0x0a, 0x90, 0x55, 0xd3, 0xff, 0x82, 0x49, 0x64, 0x84, 0x5d,
-  0x40, 0xe8, 0xca, 0x10, 0x92, 0x78, 0x39, 0x8b, 0xbf, 0xbb, 0x47, 0x3c,
-  0xfa, 0xbd, 0xbc, 0xda, 0x18, 0xbd, 0xd3, 0xe5, 0x76, 0x2a, 0x17, 0xde,
-  0x8e, 0xff, 0xdd, 0x53, 0x6f, 0x5f, 0x0f, 0xd2, 0x63, 0x72, 0x70, 0xd9,
-  0x1e, 0x97, 0xc4, 0x31, 0x3a, 0x44, 0x5d, 0x27, 0xf8, 0x61, 0xdf, 0xa6,
-  0xe4, 0xe1, 0x16, 0x27, 0x05, 0xf9, 0x71, 0xb0, 0x6c, 0x26, 0x8a, 0x1c,
-  0xb4, 0x59, 0x9a, 0x38, 0x99, 0x15, 0xb3, 0x12, 0x46, 0x28, 0x4a, 0x8a,
-  0x35, 0x85, 0xc1, 0x7c, 0xe0, 0x8a, 0xf5, 0x72, 0xf1, 0xd8, 0x7a, 0xd4,
-  0xea, 0xd4, 0x56, 0xa4, 0x11, 0x25, 0x1c, 0x22, 0xb4, 0x4c, 0xed, 0xfd,
-  0x7a, 0x5a, 0x75, 0xd5, 0xb9, 0xe3, 0xa9, 0x87, 0x21, 0x82, 0xa1, 0xf1,
-  0x0b, 0x5f, 0x40, 0x1d, 0xfc, 0x2e, 0x42, 0xfc, 0x18, 0xb8, 0x89, 0x7e,
-  0xac, 0x37, 0xba, 0xf0, 0xe5, 0xac, 0x2d, 0x54, 0x52, 0x48, 0x80, 0x76,
-  0x42, 0x43, 0xce, 0x94, 0x13, 0x23, 0xda, 0x36, 0x61, 0x29, 0xe9, 0xf2,
-  0xfd, 0x7d, 0xdb, 0xab, 0xc4, 0xd7, 0x5f, 0x40, 0x52, 0x26, 0xcb, 0x67,
-  0x8d, 0xc6, 0xd8, 0xd4, 0xdc, 0xf0, 0xa9, 0x37, 0xd2, 0x7d, 0xe6, 0x62,
-  0x76, 0xde, 0x7c, 0x47, 0xb8, 0xea, 0x16, 0x71, 0x05, 0xfa, 0xf8, 0x2b,
-  0x1b, 0xc0, 0x5b, 0x1b, 0x98, 0xf2, 0x38, 0xbe, 0xf4, 0xf1, 0x7c, 0x88,
-  0x3d, 0xfd, 0xc5, 0xa8, 0x26, 0x1b, 0x87, 0x60, 0x00, 0x04, 0x4a, 0x68,
-  0x6c, 0x16, 0x22, 0x80, 0x0f, 0x34, 0x77, 0xcd, 0x79, 0x89, 0x72, 0x48,
-  0x41, 0x79, 0x72, 0x1a, 0x8c, 0x62, 0x9b, 0xa5, 0x85, 0x26, 0xd1, 0x64,
-  0xc6, 0x35, 0xea, 0x9d, 0x4b, 0x62, 0x35, 0xa5, 0x8f, 0x23, 0x82, 0x27,
-  0x88, 0x54, 0x9a, 0x9f, 0xa2, 0x84, 0x14, 0x9b, 0xa3, 0x74, 0x42, 0xf0,
-  0x1a, 0xed, 0x9a, 0xa2, 0x93, 0x90, 0xf7, 0xa1, 0x5d, 0x12, 0x66, 0xe7,
-  0xf9, 0x59, 0x7b, 0xf2, 0xcb, 0xc2, 0x69, 0x81, 0x0f, 0xf3, 0x72, 0x65,
-  0xed, 0x21, 0x0f, 0x80, 0xec, 0x42, 0x5a, 0xe7, 0x96, 0xa2, 0xaf, 0xbc,
-  0x2f, 0xc7, 0x15, 0x33, 0xbf, 0x3c, 0xe4, 0xeb, 0xd5, 0xe4, 0xea, 0xe9,
-  0xf9, 0xfa, 0xee, 0xcb, 0x68, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0xff,
-  0xff, 0xff, 0x09, 0x6f, 0xa2, 0x21, 0x49, 0x88, 0x26, 0x1a, 0x84, 0x0b,
-  0x1d, 0x2e, 0xa2, 0xd3, 0x99, 0x56, 0x00, 0x0b, 0xcd, 0x53, 0x07, 0x2b,
-  0x92, 0x30, 0x2e, 0xb2, 0x63, 0xc2, 0x64, 0x21, 0x12, 0x3b, 0xec, 0xc6,
-  0xd4, 0x60, 0xe9, 0x2f, 0xf4, 0xc6, 0x58, 0x21, 0xb5, 0x6e, 0xf7, 0xce,
-  0xc5, 0xea, 0x8e, 0xce, 0xc6, 0x4a, 0x62, 0xdc, 0x5a, 0x13, 0x83, 0x2f,
-  0x90, 0x78, 0xa7, 0x57, 0xe8, 0x1a, 0x21, 0xf1, 0xa4, 0x2f, 0x96, 0x22,
-  0x9b, 0x74, 0xfc, 0xf7, 0xf3, 0xfb, 0x22, 0x6d, 0xb9, 0x60, 0xa5, 0xaf,
-  0xb4, 0x79, 0xaf, 0x6b, 0xe9, 0x16, 0xe8, 0xa3, 0x1e, 0x1e, 0x2a, 0x0a,
-  0xd3, 0x5a, 0x9b, 0x0e, 0x1a, 0xcf, 0xb0, 0xe6, 0x71, 0xa1, 0x90, 0x64,
-  0x88, 0x39, 0xb6, 0x34, 0xe4, 0xfc, 0x94, 0xfa, 0x91, 0x23, 0x0b, 0x40,
-  0xcc, 0x53, 0x14, 0xd9, 0x02, 0x54, 0xa6, 0x40, 0x60, 0x4c, 0x30, 0x3d,
-  0x6e, 0x5b, 0x9c, 0xe1, 0x59, 0x16, 0xa0, 0xb1, 0x7c, 0x22, 0xb1, 0x50,
-  0x1e, 0x42, 0x92, 0x02, 0xf0, 0x98, 0xd4, 0x71, 0xd0, 0x9c, 0xc9, 0x53,
-  0x14, 0xdf, 0x81, 0xfd, 0x09, 0x89, 0x13, 0x46, 0x4a, 0x98, 0x7a, 0x2f,
-  0xbb, 0xc4, 0x00, 0x9c, 0x28, 0x45, 0x10, 0x84, 0x32, 0x71, 0x21, 0x01,
-  0x27, 0x53, 0xcf, 0x1a, 0x83, 0xbd, 0x94, 0x7f, 0xf4, 0xee, 0xb9, 0xb9,
-  0xf5, 0xc7, 0xa1, 0x7f, 0xdf, 0xf9, 0x6f, 0xcb, 0x85, 0x1b, 0xf2, 0x0e,
-  0x34, 0x74, 0xba, 0xcb, 0xe8, 0xa6, 0x6e, 0xd3, 0xc7, 0x54, 0x83, 0x62,
-  0x88, 0xf7, 0xbd, 0x9d, 0x8d, 0x3b, 0x5b, 0xdb, 0x89, 0x68, 0xc7, 0xd9,
-  0xbe, 0xaf, 0xe5, 0xf0, 0xb0, 0x03, 0xd7, 0xcc, 0xe4, 0xc2, 0x81, 0x28,
-  0x76, 0x00, 0xa8, 0x09, 0x50, 0x02, 0x80, 0x22, 0xea, 0x85, 0x9b, 0x8d,
-  0x68, 0xc7, 0x9b, 0x9a, 0xb4, 0x97, 0xcc, 0x3a, 0x9d, 0xb8, 0x42, 0x31,
-  0x5e, 0x85, 0x30, 0xf1, 0x40, 0x2c, 0xb4, 0x67, 0x98, 0x0b, 0xd6, 0x0d,
-  0xcc, 0x46, 0x58, 0xa6, 0xcc, 0xbd, 0xc0, 0x35, 0x69, 0x44, 0x05, 0x23,
-  0xbf, 0x89, 0xa1, 0x8d, 0x3e, 0xaa, 0xb4, 0x9e, 0x1c, 0x4a, 0x6b, 0xe5,
-  0xf1, 0xf8, 0xa5, 0xe3, 0xaf, 0xdb, 0xe8, 0xf5, 0x30, 0x68, 0x72, 0x35,
-  0xb6, 0x00, 0x2e, 0x77, 0x82, 0x95, 0xd6, 0x3b, 0xac, 0x5c, 0xc3, 0x7f,
-  0x09, 0x3c, 0xc9, 0xbe, 0x88, 0x9e, 0xda, 0x77, 0x6b, 0x80, 0xb7, 0xa5,
-  0x16, 0x34, 0x4b, 0xba, 0x05, 0x86, 0x09, 0x00, 0xd9, 0x97, 0xf8, 0xf7,
-  0xc7, 0x68, 0xa0, 0x06, 0xde, 0x8c, 0xec, 0x98, 0x8d, 0xca, 0x80, 0x84,
-  0x64, 0x73, 0xfa, 0xbc, 0xbd, 0xd5, 0xe7, 0x5e, 0x00, 0x1c, 0x21, 0x1a,
-  0x88, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x0b, 0x6a, 0xa4, 0xb0, 0xd0, 0x42,
-  0xb4, 0x1b, 0x0d, 0xc2, 0x08, 0x79, 0x0b, 0xa7, 0x02, 0x99, 0x60, 0x00,
-  0x91, 0x41, 0x4c, 0xd4, 0x41, 0xc7, 0x88, 0xe5, 0x4a, 0xc0, 0x76, 0x20,
-  0xa5, 0xf3, 0xc7, 0x39, 0x4a, 0x54, 0x0d, 0xc9, 0xd9, 0xfd, 0x9b, 0x61,
-  0x44, 0x34, 0xc4, 0xae, 0xcb, 0x7c, 0x04, 0x0c, 0x99, 0x58, 0x9f, 0x60,
-  0x63, 0xef, 0x2e, 0x59, 0xf4, 0x8a, 0xfe, 0x0f, 0xdc, 0x18, 0xe9, 0x4f,
-  0xdb, 0x52, 0x68, 0x1b, 0x17, 0xcf, 0xde, 0x66, 0xb5, 0x4b, 0x9a, 0x87,
-  0x84, 0xa3, 0xb1, 0xd2, 0xd4, 0x77, 0x09, 0x8d, 0x83, 0x8d, 0xb0, 0x69,
-  0x5b, 0x48, 0xa9, 0x80, 0x11, 0xe2, 0x99, 0xf9, 0xc9, 0x2e, 0x28, 0x8c,
-  0x59, 0x0e, 0x2e, 0x76, 0x4b, 0x46, 0x5f, 0xa4, 0xab, 0x70, 0xa3, 0x4b,
-  0x72, 0xac, 0x3f, 0xd6, 0x65, 0x34, 0xb2, 0x45, 0xd2, 0x9c, 0x12, 0x05,
-  0x25, 0x6c, 0xe8, 0x9d, 0x73, 0xc5, 0x81, 0x6f, 0xd2, 0x86, 0x1b, 0x61,
-  0x68, 0xa2, 0x33, 0xa4, 0xf0, 0xde, 0x8c, 0x57, 0x3c, 0x19, 0xfe, 0x59,
-  0x0c, 0x9e, 0x3e, 0x54, 0xdc, 0x87, 0x0e, 0x50, 0x2d, 0x62, 0x97, 0x1d,
-  0x09, 0xf3, 0xbd, 0x2c, 0x14, 0x1c, 0xdd, 0x87, 0x47, 0xdb, 0xca, 0xe8,
-  0xda, 0x00, 0xc7, 0x3d, 0xcb, 0x32, 0x03, 0x8d, 0xb5, 0x4c, 0xab, 0x95,
-  0x47, 0xed, 0xea, 0x09, 0x7f, 0x32, 0x26, 0x14, 0xcd, 0x67, 0x3a, 0xec,
-  0xc3, 0xa9, 0x3a, 0x7c, 0xbf, 0xb1, 0xd1, 0x65, 0x85, 0x4f, 0x85, 0x2a,
-  0x18, 0x20, 0x87, 0x3e, 0x57, 0xc4, 0xe3, 0x63, 0xab, 0xe1, 0xcf, 0x38,
-  0x03, 0xe7, 0xdc, 0x5c, 0xc4, 0x00, 0xec, 0x00, 0x02, 0x28, 0x96, 0x32,
-  0xca, 0x05, 0xa8, 0x28, 0x8d, 0xf6, 0x4a, 0xd6, 0x94, 0xa6, 0xa6, 0xf8,
-  0x68, 0x92, 0x31, 0xf8, 0x8a, 0x00, 0x9b, 0x52, 0x93, 0x10, 0x45, 0x97,
-  0xd0, 0x9d, 0xe8, 0x98, 0x26, 0xac, 0x19, 0x21, 0x6c, 0xae, 0xdc, 0x1b,
-  0x2b, 0x14, 0xe1, 0x66, 0x94, 0x75, 0x05, 0x52, 0x32, 0xe6, 0xfb, 0xd2,
-  0xdf, 0x90, 0x72, 0xa0, 0x43, 0x52, 0xc8, 0x0e, 0xb4, 0x11, 0x65, 0x94,
-  0xfe, 0x1e, 0xda, 0xa1, 0xce, 0xb9, 0x7b, 0x39, 0x17, 0x5e, 0x96, 0xe8,
-  0x57, 0xd7, 0x3c, 0xe8, 0x82, 0x14, 0xe2, 0xc0, 0xd6, 0xf6, 0x75, 0x56,
-  0x24, 0x13, 0x69, 0xf8, 0x87, 0xce, 0x47, 0xdd, 0x9d, 0x5c, 0x21, 0x1a,
-  0x88, 0x00, 0x00, 0x03, 0xff, 0xff, 0x0f, 0x6a, 0xa4, 0xb1, 0x85, 0x24,
-  0x36, 0x22, 0x84, 0x5a, 0xea, 0x2b, 0xa6, 0x98, 0xd4, 0x10, 0xa0, 0x00,
-  0x90, 0xa1, 0x10, 0x22, 0xa0, 0x7c, 0x91, 0x06, 0x8b, 0x53, 0x72, 0x4f,
-  0xb0, 0x65, 0x42, 0x64, 0x10, 0xfe, 0x4f, 0x71, 0x4d, 0x38, 0x7e, 0x7a,
-  0xd9, 0x5b, 0xb7, 0x3d, 0x52, 0x72, 0x78, 0x22, 0xfe, 0xc7, 0xdc, 0xab,
-  0xb5, 0x8a, 0x76, 0x37, 0x5e, 0xf2, 0xc3, 0x83, 0xb0, 0x53, 0xf0, 0x5c,
-  0x72, 0xe5, 0x56, 0xf6, 0x5e, 0x5b, 0x93, 0x34, 0xf3, 0x96, 0x8e, 0xe6,
-  0x37, 0x19, 0x5d, 0x31, 0x00, 0x0d, 0x2b, 0xd4, 0x9b, 0xfb, 0xce, 0x74,
-  0x94, 0x93, 0x23, 0xd2, 0x77, 0x5f, 0x68, 0x10, 0xa9, 0x1e, 0x96, 0xdd,
-  0xa3, 0x45, 0x18, 0xcb, 0xc4, 0x17, 0x7f, 0x2b, 0x40, 0x2d, 0xbe, 0x32,
-  0x2a, 0x1a, 0xc1, 0x44, 0x50, 0x3b, 0x91, 0x04, 0x11, 0x3a, 0x38, 0x65,
-  0xea, 0x8b, 0x9c, 0x27, 0x1e, 0x06, 0xc5, 0x62, 0xad, 0x94, 0xb4, 0x27,
-  0x5d, 0x2a, 0x79, 0x54, 0xe7, 0x53, 0x08, 0x05, 0x55, 0x68, 0x5a, 0xff,
-  0xa1, 0x28, 0xd3, 0x63, 0xb4, 0xa2, 0xb5, 0x7c, 0x30, 0x39, 0xcc, 0xb6,
-  0x1e, 0x67, 0x3c, 0xc9, 0x80, 0xaf, 0x54, 0x55, 0xcc, 0xd2, 0x41, 0x4d,
-  0x5e, 0xe6, 0xba, 0xc6, 0x16, 0x6c, 0xe8, 0x3c, 0x29, 0x99, 0x36, 0x57,
-  0x66, 0xf2, 0x19, 0xff, 0x38, 0xf6, 0x2e, 0x78, 0x55, 0x3d, 0x7a, 0x24,
-  0x17, 0x9a, 0xab, 0x5e, 0xe0, 0xee, 0x7d, 0x56, 0x0f, 0x51, 0xa6, 0x1a,
-  0xdc, 0xeb, 0xcb, 0x54, 0xf2, 0xed, 0xf2, 0xcf, 0xc3, 0x3e, 0x8f, 0xfa,
-  0xbf, 0xcc, 0x01, 0xef, 0xf2, 0x2b, 0x41, 0x30, 0xcc, 0x3b, 0x00, 0x00,
-  0x12, 0xa4, 0x58, 0x50, 0x4a, 0x2a, 0x02, 0x7d, 0xd9, 0xe3, 0x8f, 0x6f,
-  0x6b, 0x5f, 0xfa, 0x93, 0x16, 0x13, 0xdb, 0x12, 0x35, 0xfc, 0x52, 0xb6,
-  0x91, 0x04, 0xeb, 0x10, 0xa4, 0xfd, 0x65, 0xd7, 0x53, 0x8f, 0x2a, 0xe6,
-  0x4f, 0x46, 0x0d, 0xf1, 0xea, 0xb6, 0x08, 0xbf, 0x94, 0xd2, 0xb0, 0xbb,
-  0x38, 0x5c, 0x7d, 0xce, 0x87, 0x33, 0xc2, 0x45, 0x8e, 0xfe, 0x91, 0x1f,
-  0xff, 0xe5, 0xae, 0x54, 0x22, 0x88, 0xeb, 0xb6, 0x97, 0xb1, 0x45, 0xc4,
-  0xce, 0xcc, 0x56, 0x60, 0x00, 0xb0, 0x72, 0xa0, 0x40, 0x11, 0x90, 0xf3,
-  0xc3, 0x56, 0x9c, 0x4b, 0x0a, 0x33, 0xb4, 0x31, 0xaf, 0xdf, 0x9f, 0xd4,
-  0x7e, 0xbc, 0x38, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x07, 0xff, 0xff, 0x0f,
-  0x70, 0xa1, 0xb1, 0x48, 0x62, 0x64, 0x08, 0x8d, 0x85, 0x02, 0x50, 0x82,
-  0x47, 0x92, 0xd6, 0x44, 0x09, 0xb0, 0x10, 0x16, 0x9b, 0x12, 0x70, 0xbc,
-  0x72, 0xcf, 0x07, 0x28, 0x54, 0xa1, 0xe7, 0x4c, 0xaa, 0xc7, 0xde, 0xc6,
-  0x68, 0xbd, 0xe7, 0xad, 0x03, 0xc3, 0xba, 0xc5, 0xdc, 0x49, 0x40, 0xf2,
-  0xbc, 0xa8, 0x0d, 0x3f, 0x54, 0xe6, 0x99, 0x21, 0xd3, 0x8a, 0x77, 0x8e,
-  0x9d, 0x9c, 0x31, 0xd6, 0x62, 0x31, 0xdd, 0x1f, 0x48, 0xd2, 0x34, 0x75,
-  0x3b, 0x83, 0xd9, 0x2f, 0x19, 0x22, 0x93, 0xdb, 0xd6, 0x05, 0x95, 0x03,
-  0x27, 0xd5, 0x86, 0x8c, 0xe6, 0x57, 0x4d, 0xe0, 0x97, 0xd2, 0x71, 0xdb,
-  0xf7, 0xd3, 0x07, 0x40, 0xf6, 0x76, 0x3d, 0xfd, 0x1e, 0x8b, 0x28, 0xda,
-  0x75, 0xd5, 0xd8, 0xb8, 0x59, 0x51, 0xb9, 0xff, 0xd8, 0xe9, 0x2c, 0x5c,
-  0x59, 0xd9, 0xd7, 0xdd, 0x66, 0x5a, 0x2e, 0xfd, 0xa6, 0xc4, 0xd8, 0x5d,
-  0x6b, 0x3b, 0x0f, 0x4d, 0x2b, 0x62, 0x09, 0x72, 0xf7, 0x1d, 0x97, 0xaf,
-  0xf4, 0x4e, 0x64, 0xac, 0xa2, 0xf7, 0x6c, 0xec, 0xda, 0xaa, 0xaa, 0x4a,
-  0x3f, 0x19, 0xdd, 0x1d, 0xdc, 0xde, 0x6b, 0x7c, 0x6a, 0x44, 0x4d, 0xe3,
-  0x8a, 0x43, 0x5f, 0x83, 0xcb, 0x2d, 0x8d, 0xb0, 0x6f, 0xc8, 0x53, 0x2c,
-  0x27, 0x6c, 0x22, 0x6a, 0x42, 0x88, 0x46, 0xa4, 0xc5, 0xc8, 0x8e, 0x71,
-  0xc4, 0xfb, 0x78, 0xd9, 0x6c, 0x92, 0x7b, 0x87, 0x56, 0xda, 0xf2, 0xc1,
-  0x86, 0x5a, 0x11, 0xb1, 0x06, 0x6d, 0x4b, 0xe8, 0x1c, 0x6d, 0x92, 0x2a,
-  0xbc, 0xaa, 0x15, 0x31, 0x29, 0x78, 0xd9, 0xd9, 0x4f, 0x3a, 0xae, 0x63,
-  0xe5, 0x9b, 0x97, 0x67, 0x55, 0xe3, 0xe5, 0xe1, 0xc8, 0x01, 0xef, 0xf0,
-  0x2c, 0x41, 0x30, 0xd4, 0x3b, 0x00, 0x00, 0x26, 0x48, 0x80, 0xa1, 0x05,
-  0x9b, 0x1b, 0xb0, 0x08, 0x2e, 0x9c, 0xd4, 0x6a, 0x04, 0xa4, 0xa2, 0xf4,
-  0x06, 0x2b, 0xaf, 0x48, 0x4a, 0x24, 0xee, 0x9c, 0x87, 0x15, 0x68, 0x55,
-  0x12, 0x85, 0x45, 0x66, 0x85, 0x38, 0xec, 0xfb, 0xee, 0xdb, 0xa8, 0xf9,
-  0x2e, 0xc6, 0x7b, 0x2d, 0xbe, 0xdc, 0xa1, 0xe9, 0x00, 0xfb, 0x91, 0x2f,
-  0xd9, 0x01, 0x5f, 0x28, 0xb1, 0xb7, 0xbf, 0x4d, 0xbd, 0x7d, 0x12, 0xc5,
-  0xc6, 0xee, 0xea, 0x3a, 0x78, 0x63, 0xba, 0xe6, 0x7c, 0xb2, 0xe3, 0xd1,
-  0x3e, 0x46, 0x10, 0xbf, 0xd7, 0x63, 0x9f, 0x67, 0x7b, 0xa7, 0xf9, 0xf2,
-  0xe5, 0x55, 0x7f, 0x0d, 0xbf, 0x0e, 0x51, 0x9e, 0xdf, 0x54, 0xf4, 0x40,
-  0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x07, 0xff, 0xff, 0x0f, 0x70, 0xa2,
-  0x30, 0xcd, 0xc8, 0x18, 0x19, 0x84, 0x17, 0x70, 0xb9, 0xa2, 0xe2, 0xa6,
-  0x5d, 0x50, 0x02, 0x09, 0x13, 0x91, 0xf4, 0x8c, 0xec, 0x2a, 0x00, 0x18,
-  0x04, 0x32, 0x23, 0x05, 0xac, 0x32, 0x63, 0x8f, 0xb3, 0xbb, 0x59, 0x5b,
-  0x23, 0x6e, 0xbc, 0x57, 0xd7, 0x26, 0x2d, 0x55, 0x47, 0xd5, 0x19, 0x0c,
-  0xa8, 0x38, 0xa2, 0x22, 0xef, 0xf6, 0xce, 0x25, 0xd0, 0xb6, 0x46, 0xfc,
-  0xd3, 0x5a, 0x4f, 0x2d, 0xe1, 0xfb, 0x5e, 0x23, 0x71, 0x55, 0x5f, 0xf5,
-  0xf3, 0x08, 0xac, 0x4c, 0xe7, 0x4f, 0x9d, 0x6b, 0xc9, 0xeb, 0xfb, 0x6d,
-  0x6f, 0x17, 0x41, 0x1a, 0xa4, 0x6b, 0x9b, 0x1c, 0xda, 0x4d, 0x9a, 0x4b,
-  0xa3, 0x2c, 0x81, 0x10, 0x81, 0x40, 0x62, 0x81, 0x15, 0x93, 0xeb, 0xbd,
-  0xe6, 0x97, 0x78, 0x49, 0xa5, 0x68, 0x0f, 0x0b, 0x36, 0x27, 0x9e, 0xbb,
-  0x7b, 0xb8, 0xe3, 0x2d, 0x84, 0x80, 0xd8, 0x8b, 0x14, 0x69, 0x98, 0x8d,
-  0x13, 0x64, 0x9f, 0x10, 0x1a, 0x2b, 0xd6, 0x08, 0x5d, 0x64, 0x91, 0x5d,
-  0x2e, 0x1c, 0xf0, 0x9c, 0xee, 0xb5, 0x73, 0x66, 0xf8, 0x33, 0xa6, 0x42,
-  0x50, 0xd2, 0xc2, 0xa1, 0x05, 0x15, 0xd8, 0xc7, 0x45, 0x99, 0xb8, 0xb5,
-  0xb2, 0xf6, 0xbc, 0x3c, 0x5c, 0x83, 0xbf, 0x06, 0xf4, 0x76, 0xaf, 0xb1,
-  0x81, 0xea, 0x08, 0x02, 0xfc, 0x36, 0x24, 0x3d, 0x3d, 0x2b, 0x3b, 0xd6,
-  0xb7, 0xd0, 0x1b, 0x43, 0x12, 0x6c, 0x5f, 0x7c, 0xc9, 0x58, 0x37, 0x48,
-  0xb8, 0x57, 0x89, 0x61, 0x16, 0x55, 0x6d, 0x49, 0xa6, 0x66, 0x51, 0xb0,
-  0xc0, 0xd5, 0x3c, 0xad, 0x9f, 0xce, 0xfc, 0xb7, 0xba, 0x0f, 0x7f, 0x81,
-  0x6b, 0x06, 0x06, 0xa1, 0xd8, 0x00, 0x00, 0x85, 0x40, 0xa0, 0x84, 0x86,
-  0xc4, 0x00, 0xc2, 0xdd, 0xc2, 0x90, 0x3e, 0xa6, 0x55, 0x45, 0xbc, 0xe5,
-  0x9a, 0x80, 0x8a, 0x8b, 0x8b, 0xad, 0xbd, 0x96, 0xaa, 0x33, 0xc9, 0x36,
-  0x2e, 0xf1, 0x91, 0x94, 0xbc, 0xfe, 0x0f, 0x6e, 0x1e, 0x18, 0x1b, 0x84,
-  0x69, 0xa0, 0xb4, 0xb1, 0x98, 0x70, 0x78, 0x42, 0x58, 0x0b, 0x94, 0xde,
-  0xe3, 0x73, 0x94, 0xb4, 0xcd, 0x38, 0xee, 0x50, 0x34, 0x51, 0xab, 0x42,
-  0xc9, 0x2b, 0x99, 0xc6, 0xfd, 0xf1, 0x8d, 0x6c, 0xbc, 0x6d, 0x49, 0xac,
-  0x1f, 0x51, 0x6d, 0xb1, 0x10, 0x7a, 0x1c, 0xba, 0x88, 0x70, 0x81, 0x82,
-  0x5a, 0x89, 0x8a, 0x30, 0x82, 0x54, 0x8f, 0x4f, 0x7d, 0xeb, 0xf1, 0xef,
-  0x9e, 0x40, 0x1c, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x07, 0xff, 0xff, 0x07,
-  0x73, 0x62, 0x98, 0x49, 0x68, 0x26, 0x19, 0x84, 0x06, 0x99, 0x6d, 0x05,
-  0xdb, 0x20, 0x20, 0x28, 0x44, 0xa0, 0xe7, 0xe2, 0x62, 0xa7, 0xf4, 0xf2,
-  0x21, 0x0e, 0x57, 0x25, 0xbc, 0xda, 0xc8, 0x94, 0x58, 0xbe, 0x03, 0x98,
-  0xfe, 0x8f, 0xc1, 0x15, 0xfd, 0xa3, 0x44, 0xcc, 0x5e, 0xe3, 0x8e, 0x3e,
-  0xf8, 0xa1, 0x50, 0x83, 0xb1, 0xef, 0x2a, 0x6f, 0x65, 0x89, 0x4f, 0x99,
-  0x36, 0x5c, 0xf2, 0x54, 0x6a, 0xdf, 0x92, 0x23, 0xd6, 0xff, 0x4d, 0xf3,
-  0x8c, 0x5e, 0x3e, 0x98, 0x66, 0x28, 0x34, 0x4a, 0x72, 0x97, 0x08, 0x14,
-  0xb8, 0xcd, 0x14, 0x3d, 0x44, 0xb7, 0x6f, 0x85, 0x73, 0x98, 0x75, 0x45,
-  0x76, 0x66, 0xe3, 0x09, 0x89, 0xc5, 0x21, 0xf3, 0xba, 0x98, 0xd6, 0x84,
-  0xf3, 0xe3, 0x9d, 0x31, 0xb3, 0x9c, 0xb6, 0xa8, 0x50, 0x8c, 0x45, 0x8d,
-  0x3f, 0x46, 0xdd, 0x49, 0x56, 0x4e, 0xa4, 0x0d, 0xc6, 0x17, 0x0e, 0xc6,
-  0xfb, 0x24, 0x7e, 0xcb, 0x7c, 0x1a, 0x94, 0x20, 0x4f, 0x57, 0x1a, 0xc5,
-  0xa6, 0x00, 0xb9, 0xb9, 0xfc, 0x54, 0x0e, 0x13, 0xe0, 0x75, 0x9b, 0x22,
-  0x79, 0x7e, 0xbc, 0x85, 0x1d, 0xea, 0xa7, 0xbf, 0xd5, 0x8d, 0xec, 0xac,
-  0x82, 0xa9, 0x4c, 0xa0, 0xf3, 0x0c, 0x4f, 0x3b, 0xe1, 0xe6, 0x31, 0x83,
-  0x4e, 0x55, 0xb1, 0x1e, 0x93, 0xd9, 0xe0, 0xa4, 0xce, 0x9e, 0xaf, 0x0b,
-  0x45, 0xb7, 0x70, 0x7a, 0x55, 0x12, 0x14, 0xdb, 0x14, 0x92, 0xa7, 0x18,
-  0x1e, 0x69, 0x7c, 0x9b, 0x32, 0x00, 0x3f, 0x7f, 0x66, 0xa9, 0xb8, 0x7d,
-  0x69, 0xd1, 0xdf, 0x46, 0x95, 0xd7, 0x46, 0xd9, 0xab, 0x7c, 0xbe, 0x9d,
-  0xf9, 0xb9, 0xf7, 0x74, 0x88, 0xff, 0xd5, 0x40, 0x1e, 0xbf, 0x02, 0xd6,
-  0x22, 0x87, 0x60, 0x01, 0x28, 0x2a, 0x5d, 0x10, 0x14, 0x08, 0x8c, 0x00,
-  0x34, 0x4c, 0x73, 0xa4, 0xfc, 0x37, 0x92, 0x84, 0xe0, 0xce, 0x76, 0xa4,
-  0x33, 0xd6, 0xf4, 0xd0, 0x9f, 0x8a, 0xaa, 0xea, 0x86, 0x89, 0x16, 0xbc,
-  0x34, 0x6b, 0x23, 0x09, 0xa5, 0x2d, 0xf3, 0xb7, 0xf9, 0x47, 0x2a, 0xdf,
-  0x5d, 0x39, 0x5c, 0x30, 0x53, 0x0a, 0x69, 0x5d, 0x4d, 0x67, 0x78, 0xda,
-  0x3e, 0x5d, 0x66, 0x94, 0x8a, 0x43, 0x60, 0x0b, 0x98, 0xcd, 0xab, 0x54,
-  0x05, 0x06, 0x81, 0x8d, 0xb5, 0xa1, 0x99, 0xbc, 0x8d, 0xa1, 0x64, 0xd1,
-  0x76, 0x7d, 0x25, 0xd3, 0x47, 0xa2, 0xca, 0x64, 0xf2, 0x51, 0xd3, 0x7d,
-  0x36, 0x75, 0x69, 0x9b, 0xc1, 0x62, 0x23, 0x2c, 0x43, 0xdd, 0x9b, 0xe9,
-  0xe8, 0xdf, 0xd2, 0x00, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x03, 0xff,
-  0xff, 0x13, 0x6f, 0xa2, 0x31, 0x85, 0x8c, 0x43, 0x08, 0x68, 0x9d, 0x12,
-  0xe3, 0x2e, 0xca, 0x5e, 0x5e, 0x00, 0x05, 0xd5, 0xe0, 0xd6, 0x5f, 0x4c,
-  0xbb, 0xc6, 0x40, 0x65, 0xfd, 0x56, 0xdf, 0xfa, 0x77, 0x5d, 0xe0, 0x00,
-  0xd6, 0xec, 0x53, 0xa0, 0x27, 0x8d, 0x83, 0xc3, 0xf7, 0x1f, 0xf5, 0x79,
-  0xc3, 0x3b, 0xb3, 0xcb, 0x66, 0xf5, 0x9a, 0xbe, 0x08, 0x82, 0x33, 0x86,
-  0x7f, 0xf5, 0x24, 0x93, 0x8e, 0x56, 0xe2, 0xb9, 0xf9, 0x32, 0x5a, 0xec,
-  0x52, 0xc9, 0xf1, 0xc3, 0x1e, 0x83, 0xed, 0x5e, 0x35, 0xbe, 0xef, 0x01,
-  0xa5, 0xd5, 0x39, 0x0c, 0x78, 0x31, 0xa5, 0x00, 0xdf, 0xc5, 0x59, 0x1f,
-  0x0f, 0x12, 0xfc, 0xb3, 0x69, 0xce, 0xb7, 0x30, 0xb1, 0x0f, 0x91, 0x93,
-  0x16, 0x53, 0x6a, 0xe5, 0xb7, 0x71, 0xd8, 0x35, 0x96, 0x8b, 0xa8, 0x11,
-  0x3b, 0x81, 0x2e, 0xbc, 0xa1, 0x2c, 0x89, 0x56, 0x5c, 0xb5, 0xa6, 0x77,
-  0x29, 0x7b, 0xac, 0x08, 0x96, 0xf1, 0x42, 0xdc, 0x45, 0x16, 0xf6, 0x1d,
-  0x5e, 0x24, 0x15, 0xfd, 0x0c, 0x59, 0x44, 0xa1, 0x6a, 0xd5, 0xd2, 0x51,
-  0x8e, 0x33, 0xa4, 0x42, 0xd6, 0x5d, 0xd0, 0xf2, 0x94, 0xfa, 0x4f, 0x76,
-  0xfe, 0x03, 0xdb, 0x49, 0x78, 0x7a, 0xa2, 0x9b, 0xa3, 0x0d, 0xa4, 0xc5,
-  0x43, 0xb2, 0xaa, 0x5b, 0x2d, 0xad, 0x5f, 0x1b, 0xa5, 0x6c, 0xb5, 0xd9,
-  0x30, 0xd0, 0x98, 0xc9, 0x90, 0xcf, 0xa7, 0x4a, 0x89, 0x91, 0x9b, 0x04,
-  0x05, 0x36, 0xdb, 0x85, 0x3d, 0xf7, 0x67, 0xe6, 0xc3, 0x6c, 0x78, 0x67,
-  0xff, 0xfd, 0xfd, 0x5b, 0xe6, 0xff, 0x50, 0x0f, 0x9f, 0x93, 0x63, 0x10,
-  0xc3, 0xb0, 0x00, 0x02, 0xaa, 0xe1, 0x60, 0xa0, 0x48, 0xac, 0x06, 0xde,
-  0x02, 0x19, 0x30, 0x14, 0xdb, 0x64, 0xa2, 0xf8, 0x62, 0x6d, 0xd6, 0x12,
-  0x12, 0x5c, 0xa9, 0x73, 0x74, 0x55, 0x2a, 0xc4, 0xd5, 0xa3, 0x72, 0xad,
-  0xd3, 0xb6, 0xfa, 0x50, 0x4b, 0xd6, 0x8d, 0xd7, 0x68, 0x32, 0x85, 0xa9,
-  0xcd, 0x4f, 0x61, 0x10, 0x00, 0x02, 0x3c, 0xd8, 0xd9, 0x98, 0x24, 0x9e,
-  0x04, 0xd4, 0x41, 0x01, 0x24, 0xfa, 0xd0, 0x76, 0xea, 0x33, 0xf0, 0xee,
-  0x11, 0x04, 0x26, 0xa2, 0x45, 0xf3, 0x1a, 0xaa, 0x88, 0xaa, 0x8a, 0x82,
-  0x44, 0x7c, 0xf4, 0xc5, 0xfd, 0x53, 0xf6, 0x7f, 0xa9, 0xea, 0x7d, 0x5f,
-  0x6c, 0x38, 0x21, 0x1a, 0x88, 0x00, 0x03, 0xff, 0xff, 0xff, 0x09, 0x73,
-  0x63, 0x12, 0x10, 0x8c, 0x28, 0x12, 0x84, 0x16, 0x45, 0xe7, 0x4e, 0x5a,
-  0xf3, 0x59, 0x5a, 0x52, 0x80, 0x58, 0x25, 0x05, 0xa0, 0x6c, 0x98, 0x1c,
-  0x54, 0x80, 0x95, 0x29, 0x00, 0x83, 0xdd, 0x82, 0x1f, 0x1e, 0x1e, 0xc4,
-  0x49, 0x04, 0x8a, 0xd6, 0x51, 0x32, 0xc3, 0x99, 0x47, 0xb5, 0xbd, 0x8b,
-  0x10, 0xd1, 0x1b, 0xf7, 0x20, 0x12, 0x5a, 0x1f, 0x33, 0x75, 0xbe, 0xaa,
-  0xdf, 0x78, 0x87, 0x52, 0x42, 0x27, 0x9a, 0x1c, 0x8e, 0xdd, 0xf4, 0xe1,
-  0xe4, 0x2e, 0x29, 0x0e, 0x4a, 0xd5, 0xfe, 0xe3, 0x04, 0x56, 0x88, 0x65,
-  0xd3, 0x57, 0x3a, 0xd3, 0x8a, 0x7d, 0xdd, 0x14, 0xbf, 0xbf, 0x1a, 0xda,
-  0xe2, 0xfc, 0xf3, 0x71, 0xa9, 0x9e, 0x28, 0x56, 0xca, 0x25, 0x62, 0x95,
-  0x90, 0x9d, 0x3a, 0x4b, 0xe9, 0x4c, 0xe6, 0x8c, 0x92, 0x9d, 0x4f, 0x8f,
-  0xbb, 0xc3, 0xf4, 0x09, 0x2f, 0x4b, 0xdd, 0x2a, 0x86, 0xb9, 0x49, 0x68,
-  0x52, 0x11, 0x42, 0x27, 0x8e, 0x60, 0x55, 0x2c, 0xab, 0x25, 0x00, 0x8e,
-  0xc1, 0x05, 0xad, 0x08, 0x13, 0xda, 0x12, 0x83, 0x25, 0x17, 0x61, 0x02,
-  0x7a, 0xd4, 0x07, 0x41, 0xba, 0x00, 0x1a, 0xc1, 0x92, 0x20, 0x8a, 0x04,
-  0x6a, 0x4c, 0x43, 0x21, 0xb8, 0x92, 0x0d, 0xd7, 0xe1, 0xb4, 0xc5, 0x24,
-  0xc7, 0xc0, 0x9d, 0x9a, 0x99, 0x50, 0xa4, 0x32, 0x44, 0x37, 0x7d, 0xb3,
-  0xdc, 0x71, 0xc7, 0x9c, 0xbe, 0x72, 0x10, 0x94, 0xf2, 0xdc, 0x97, 0x4d,
-  0x00, 0xb6, 0x4a, 0xd6, 0x4d, 0x87, 0xb1, 0x07, 0x94, 0x4b, 0x2a, 0xe1,
-  0xe7, 0xa4, 0xa7, 0x18, 0xd9, 0xf2, 0x5a, 0x85, 0xab, 0x6a, 0x43, 0x29,
-  0x61, 0x4d, 0x82, 0x28, 0xa6, 0x84, 0x3b, 0xf2, 0x97, 0x6c, 0x09, 0x53,
-  0x68, 0x5f, 0x08, 0x46, 0x53, 0xfe, 0xf3, 0xe9, 0xdf, 0xd3, 0xdf, 0xf8,
-  0x80, 0x3d, 0x7c, 0x58, 0x44, 0x71, 0x4a, 0x09, 0x87, 0x01, 0x70, 0xec,
-  0x00, 0x2a, 0x24, 0xa4, 0x05, 0x00, 0x2c, 0x40, 0x69, 0x68, 0x96, 0x23,
-  0x2e, 0x57, 0x42, 0x22, 0xd1, 0xc9, 0xa4, 0x3b, 0xe1, 0x81, 0x73, 0xd6,
-  0x22, 0x73, 0x9c, 0x12, 0x29, 0x90, 0x30, 0x8a, 0x12, 0xde, 0x44, 0x44,
-  0xe0, 0x25, 0xa1, 0xed, 0x4d, 0x67, 0xc6, 0x93, 0xce, 0x8c, 0x08, 0x99,
-  0xb4, 0x4d, 0x3a, 0x5a, 0xfe, 0x52, 0x90, 0x5d, 0x3f, 0x1c, 0x56, 0x65,
-  0xe7, 0xaf, 0x91, 0x0a, 0xe8, 0xcb, 0x93, 0xe9, 0x3b, 0x72, 0x4a, 0x76,
-  0xd1, 0xa0, 0xbc, 0xb0, 0xe5, 0x0a, 0x77, 0x0c, 0x0c, 0x88, 0x26, 0xc2,
-  0xc4, 0x92, 0xbd, 0xd6, 0x12, 0x95, 0x36, 0x4a, 0x94, 0xc5, 0xe8, 0xf2,
-  0x8c, 0xd7, 0xca, 0x93, 0xcf, 0x54, 0x9d, 0x5d, 0xc2, 0x74, 0xef, 0xbe,
-  0xbc, 0x2d, 0x9f, 0x9d, 0xb7, 0xc2, 0xf3, 0xdd, 0xae, 0xd2, 0x50, 0x6e,
-  0x1c, 0x28, 0xcf, 0xc7, 0x8c, 0xc6, 0x19, 0xf6, 0xda, 0x9a, 0xb6, 0xbc,
-  0x17, 0x2b, 0xa8, 0xf1, 0xf6, 0x71, 0xf6, 0x80, 0x70, 0x21, 0x1a, 0x88,
-  0x00, 0x00, 0x7f, 0xff, 0xff, 0x0b, 0x6f, 0xa1, 0xd8, 0x58, 0xa8, 0x22,
-  0x2a, 0x19, 0x85, 0x02, 0x50, 0x80, 0x0f, 0x65, 0x8d, 0x2a, 0x5d, 0x4c,
-  0x20, 0x00, 0x93, 0x63, 0x1f, 0x84, 0x9c, 0x71, 0x11, 0x45, 0x32, 0x30,
-  0x47, 0x45, 0x10, 0x98, 0x49, 0x5b, 0x30, 0x98, 0x03, 0x6e, 0x83, 0xf3,
-  0xd2, 0xe8, 0xc8, 0x04, 0x1a, 0x8e, 0x56, 0x2f, 0x27, 0x61, 0x90, 0x7e,
-  0x77, 0xed, 0x3b, 0x14, 0x11, 0x9d, 0x37, 0x09, 0xa6, 0x2f, 0x4c, 0xff,
-  0xcb, 0xd9, 0xd0, 0x76, 0x56, 0x45, 0xd6, 0x95, 0x44, 0x3a, 0x99, 0xc7,
-  0x30, 0x7d, 0xcf, 0x2c, 0x9e, 0x9c, 0xca, 0x7b, 0x2c, 0x96, 0x4a, 0x7b,
-  0x6a, 0x85, 0x90, 0x6d, 0x07, 0xd1, 0xad, 0xfe, 0x69, 0x04, 0x95, 0xb5,
-  0xc7, 0x82, 0xac, 0xa6, 0x0f, 0x2c, 0xaf, 0x8b, 0xff, 0x5a, 0x72, 0x52,
-  0xd4, 0xbb, 0xf7, 0x73, 0x6e, 0x98, 0x53, 0xfd, 0x5b, 0xbb, 0xef, 0x86,
-  0x7e, 0xea, 0xf5, 0xec, 0xf0, 0xa7, 0xa5, 0xa6, 0xb9, 0x44, 0xde, 0x9c,
-  0x01, 0x6f, 0xd3, 0x59, 0x50, 0x28, 0x63, 0x7c, 0x56, 0x93, 0x15, 0x6a,
-  0x25, 0x3e, 0xac, 0x8f, 0xf0, 0x21, 0x56, 0xab, 0x1b, 0x09, 0xa9, 0xd9,
-  0xd8, 0x2c, 0xf6, 0x4f, 0xfd, 0x12, 0x35, 0x15, 0x1a, 0x69, 0xa7, 0x96,
-  0x43, 0x15, 0xc8, 0x51, 0xe5, 0x44, 0x98, 0x2b, 0x67, 0xf1, 0x26, 0x64,
-  0xfd, 0xd9, 0x1a, 0x32, 0x38, 0x1a, 0xb2, 0x54, 0xeb, 0x8d, 0x26, 0x03,
-  0x88, 0xd8, 0x60, 0x6d, 0x03, 0xc5, 0xd0, 0xce, 0x3d, 0x68, 0x31, 0x0a,
-  0x0a, 0xcc, 0xf8, 0x36, 0x19, 0xce, 0x00, 0x55, 0x46, 0x11, 0x85, 0x72,
-  0x4c, 0x27, 0x90, 0xdd, 0xd5, 0xbc, 0xdb, 0xc3, 0x3d, 0xb4, 0x63, 0x56,
-  0xe0, 0x51, 0x9f, 0x28, 0x7c, 0x45, 0xd6, 0x85, 0xe9, 0xf3, 0x26, 0x3c,
-  0x78, 0xf1, 0x69, 0x6d, 0x29, 0x6f, 0xdc, 0xd7, 0xda, 0xdd, 0x5f, 0x8b,
-  0x9f, 0x8f, 0x8f, 0x18, 0xdd, 0x5f, 0x4f, 0xd9, 0xaf, 0xf9, 0xfb, 0x3e,
-  0x60, 0x08, 0x0f, 0xa0, 0xc9, 0x04, 0xc1, 0x10, 0xec, 0x00, 0x00, 0x1d,
-  0x21, 0x32, 0xe8, 0x30, 0x65, 0xf2, 0x00, 0x27, 0x0d, 0x5f, 0xa7, 0x09,
-  0x2f, 0xf9, 0xef, 0x45, 0x6a, 0xde, 0x32, 0x88, 0x2a, 0x2c, 0xa4, 0x09,
-  0x86, 0x7d, 0x17, 0xc1, 0xc0, 0xce, 0x34, 0x05, 0xd5, 0x8d, 0xfe, 0x5f,
-  0xe3, 0xb7, 0x8f, 0x10, 0x54, 0xbf, 0xee, 0x96, 0xd3, 0xb7, 0x9c, 0x1f,
-  0x9d, 0x9c, 0x05, 0xe3, 0x28, 0xa9, 0xb3, 0x65, 0xf1, 0xf0, 0xb7, 0x4b,
-  0xfb, 0x74, 0xb1, 0x77, 0x59, 0xfd, 0xef, 0xaa, 0xd6, 0x26, 0x4b, 0xb2,
-  0x31, 0xfe, 0x6f, 0x75, 0x9c, 0x7a, 0x20, 0x15, 0x72, 0x88, 0x5b, 0x24,
-  0xf7, 0x3d, 0x44, 0x20, 0x0f, 0xd5, 0xc3, 0x1f, 0xb6, 0x01, 0xc0, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x72, 0x83, 0x20, 0xc9,
-  0x4c, 0x45, 0x08, 0x03, 0x35, 0xf0, 0x95, 0xa3, 0x40, 0xa0, 0x00, 0x95,
-  0x26, 0xc7, 0xbc, 0xfe, 0xce, 0x77, 0x67, 0x15, 0xe7, 0x51, 0xd0, 0x22,
-  0x93, 0xc7, 0xa0, 0x56, 0x68, 0xfc, 0x7d, 0x79, 0xd1, 0x5e, 0xe5, 0xef,
-  0x7f, 0x3e, 0xc5, 0xa1, 0xa1, 0xfd, 0xe1, 0x9f, 0x9e, 0x2f, 0xf8, 0x45,
-  0x27, 0xb8, 0x3d, 0x72, 0x49, 0xfc, 0x97, 0xe3, 0x53, 0xf0, 0x77, 0x4f,
-  0x24, 0x55, 0x17, 0x9a, 0x9f, 0x69, 0xcc, 0x85, 0x86, 0xba, 0x61, 0x95,
-  0x5d, 0x83, 0xa9, 0xa9, 0x86, 0xac, 0xd3, 0x91, 0xe7, 0x32, 0x3d, 0xe4,
-  0xb4, 0xf1, 0x15, 0xd3, 0x72, 0xff, 0x0d, 0x96, 0x4b, 0x5b, 0xd3, 0x77,
-  0xc9, 0x21, 0x51, 0xf2, 0x53, 0x2d, 0x39, 0x49, 0xad, 0x19, 0xce, 0x6e,
-  0xed, 0x3a, 0xb0, 0x01, 0x30, 0xcf, 0x95, 0x4e, 0xaa, 0xd6, 0xca, 0x7a,
-  0x09, 0x37, 0x7e, 0x34, 0x6f, 0x99, 0x5d, 0xda, 0x45, 0x35, 0x6d, 0x7d,
-  0x92, 0x68, 0xd5, 0x71, 0x2f, 0x57, 0xce, 0x13, 0x52, 0xb8, 0x03, 0xce,
-  0x69, 0x64, 0xea, 0x6d, 0x6a, 0x57, 0xd0, 0xe8, 0x4e, 0x60, 0x3c, 0x84,
-  0xdc, 0xc3, 0x22, 0x6a, 0x3d, 0x20, 0x52, 0xfd, 0x60, 0x24, 0x3a, 0xee,
-  0xbf, 0xc0, 0xa6, 0x03, 0x77, 0xba, 0x72, 0x64, 0x4d, 0xa4, 0xdf, 0x3c,
-  0x3a, 0x73, 0x99, 0x63, 0x41, 0xe2, 0xf6, 0xf1, 0xfb, 0x0e, 0xde, 0x2e,
-  0x6b, 0x34, 0xf1, 0xd8, 0x30, 0xe2, 0x53, 0x4a, 0xa0, 0x4e, 0x79, 0xd0,
-  0x90, 0xf7, 0x9a, 0xcc, 0x43, 0x9e, 0x16, 0x34, 0xe5, 0x59, 0x6f, 0xd0,
-  0xd8, 0x0d, 0xb6, 0x8e, 0x36, 0x76, 0x4a, 0x9c, 0x6c, 0x18, 0xbd, 0xbd,
-  0x3e, 0x6d, 0xcd, 0x45, 0x5e, 0x6a, 0xda, 0xbc, 0x66, 0x2e, 0xbc, 0x7f,
-  0x1e, 0x3b, 0xe9, 0xd7, 0xd9, 0xe0, 0x00, 0xf9, 0xfb, 0x14, 0xa0, 0x98,
-  0x6a, 0x1d, 0x80, 0x00, 0x24, 0xa4, 0xa2, 0xb4, 0x30, 0x08, 0x60, 0x84,
-  0x96, 0xad, 0x8d, 0x8b, 0x55, 0xed, 0xb9, 0x5a, 0xa1, 0x0e, 0xd0, 0x80,
-  0x10, 0x41, 0x01, 0xcc, 0xac, 0x71, 0x19, 0xed, 0xbe, 0x5c, 0xd1, 0xa6,
-  0x32, 0x9a, 0xc5, 0xa6, 0x9c, 0xa5, 0x42, 0x99, 0x60, 0x15, 0x35, 0x74,
-  0xcf, 0x1e, 0x64, 0x09, 0x7e, 0x38, 0x2e, 0xa8, 0xa4, 0x0b, 0x2e, 0x09,
-  0x68, 0x9f, 0x6c, 0xf8, 0xe3, 0xd7, 0xd5, 0x34, 0x90, 0x6a, 0x09, 0x5e,
-  0x03, 0xdf, 0xb6, 0xab, 0xb0, 0xc1, 0xca, 0xea, 0xe5, 0x51, 0xaa, 0x79,
-  0x80, 0x74, 0x6e, 0xca, 0xcc, 0xa6, 0xf5, 0x7c, 0x7c, 0xf3, 0xea, 0xc7,
-  0xbf, 0x80, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x07, 0xff, 0xff, 0x0f,
-  0x70, 0xa2, 0x31, 0x0d, 0x04, 0x56, 0x21, 0x84, 0x02, 0x3a, 0x5e, 0x81,
-  0xa5, 0x4a, 0xc0, 0x40, 0x22, 0x36, 0x39, 0x10, 0x82, 0xa0, 0x6d, 0x2a,
-  0xd0, 0xd2, 0xfb, 0x70, 0x58, 0x57, 0x72, 0xab, 0x06, 0xff, 0x1f, 0xd4,
-  0x3b, 0x3a, 0xb5, 0x21, 0x6a, 0x3c, 0x6f, 0x5e, 0x1d, 0xd6, 0x3d, 0xc7,
-  0x61, 0xcc, 0xc8, 0xd6, 0xfc, 0xc7, 0xe8, 0x39, 0xa7, 0xaa, 0x7a, 0x02,
-  0x53, 0x04, 0x8b, 0x8b, 0xc4, 0x6e, 0x56, 0xdd, 0x78, 0xae, 0xf9, 0xa6,
-  0xab, 0x18, 0x37, 0x87, 0x81, 0x6e, 0x08, 0x1f, 0xbd, 0xaa, 0x18, 0x73,
-  0xd0, 0x0b, 0x2c, 0x3d, 0xbe, 0x00, 0xd5, 0x90, 0x8d, 0x17, 0x00, 0xfc,
-  0xf0, 0x89, 0x49, 0x92, 0xf3, 0xcf, 0x77, 0xca, 0xbd, 0x9b, 0x37, 0x23,
-  0xcb, 0x38, 0xf9, 0x12, 0xa5, 0xd3, 0xf6, 0x9b, 0xd9, 0xa3, 0xf1, 0xdf,
-  0x8c, 0x54, 0xf8, 0xd3, 0xc5, 0xaa, 0xd5, 0x0d, 0x57, 0x76, 0x3d, 0xa2,
-  0x5c, 0xcd, 0x3a, 0xcc, 0xb8, 0x4e, 0x51, 0xd5, 0x20, 0x47, 0x44, 0x04,
-  0x23, 0xb0, 0x76, 0xd1, 0x8d, 0xab, 0x3a, 0x4d, 0x15, 0xa9, 0x75, 0xc5,
-  0x03, 0x53, 0x6e, 0x90, 0x39, 0x50, 0x98, 0x67, 0x00, 0x89, 0x3c, 0xc4,
-  0x64, 0x31, 0x73, 0xb0, 0xf5, 0x18, 0x7a, 0x38, 0xc4, 0xa3, 0x86, 0x78,
-  0xc3, 0x58, 0xc4, 0xea, 0x00, 0x61, 0x32, 0x18, 0x66, 0x45, 0x25, 0xf4,
-  0x82, 0xf7, 0xad, 0x26, 0xee, 0xdb, 0x9c, 0x6c, 0xc2, 0xa0, 0x8e, 0x54,
-  0x5d, 0xaa, 0xa8, 0xb6, 0x9c, 0xfb, 0xa5, 0xc3, 0xdf, 0xbe, 0x0b, 0xcc,
-  0x4a, 0x47, 0x77, 0x87, 0x66, 0xe5, 0xc7, 0xe3, 0xfe, 0x91, 0xea, 0xdf,
-  0x3a, 0x0f, 0x7f, 0x81, 0x62, 0x09, 0x86, 0xa1, 0xd8, 0x25, 0x00, 0x11,
-  0x52, 0xac, 0x00, 0x12, 0x00, 0x05, 0x20, 0x45, 0x27, 0x32, 0x10, 0x4f,
-  0x80, 0x13, 0x9a, 0x13, 0xb3, 0x48, 0x56, 0x53, 0x56, 0x11, 0x4b, 0x65,
-  0xc2, 0x2b, 0xc4, 0x46, 0x8b, 0xfd, 0x3f, 0xc1, 0x7e, 0x62, 0x8c, 0x72,
-  0x00, 0x77, 0xb3, 0xd9, 0x48, 0x24, 0x22, 0x01, 0xb6, 0x74, 0xf2, 0xee,
-  0x46, 0x83, 0xbe, 0xf3, 0x40, 0x22, 0x50, 0x91, 0xdd, 0x6e, 0x48, 0x54,
-  0x49, 0x70, 0x63, 0xf7, 0xa8, 0x22, 0x10, 0xaa, 0xbf, 0xca, 0x20, 0x58,
-  0x57, 0x9f, 0x7c, 0xd5, 0xed, 0xe1, 0xe8, 0xa6, 0xf9, 0x27, 0x76, 0xcb,
-  0x7e, 0xee, 0xff, 0x3f, 0xf1, 0x8c, 0xfd, 0x9c, 0xf8, 0x00, 0x70, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x13, 0x73, 0x62, 0x92, 0xd0,
-  0x4c, 0x28, 0x11, 0x84, 0x1c, 0x0b, 0x69, 0xd0, 0x59, 0x49, 0x80, 0x80,
-  0x42, 0x6c, 0x79, 0x3e, 0x06, 0x5a, 0x84, 0x44, 0x69, 0x97, 0x7b, 0x7b,
-  0x9c, 0x9a, 0x72, 0x66, 0x14, 0x0b, 0xe4, 0x3a, 0x17, 0x68, 0xf0, 0x49,
-  0xd4, 0x3e, 0xfc, 0xe7, 0x10, 0x90, 0x76, 0x25, 0xa4, 0x5e, 0x6c, 0x8a,
-  0xed, 0xe1, 0x72, 0x2f, 0xa4, 0xbe, 0x2e, 0x90, 0x75, 0x9b, 0xff, 0x83,
-  0x37, 0x2f, 0x38, 0x96, 0x7c, 0xd8, 0x9e, 0x2d, 0x6a, 0x42, 0xf6, 0x89,
-  0x60, 0x17, 0x97, 0x42, 0x75, 0x4e, 0x69, 0x73, 0xd3, 0xef, 0xf7, 0xc8,
-  0xcb, 0x38, 0x9b, 0xb0, 0xe3, 0xe9, 0x36, 0x53, 0x7e, 0xc4, 0x1d, 0xe7,
-  0xb2, 0xf3, 0x1d, 0x6d, 0xc7, 0x64, 0x4f, 0x7e, 0x8f, 0x31, 0x86, 0x9e,
-  0x59, 0x15, 0x47, 0xc3, 0xdc, 0x3e, 0x7b, 0xe9, 0xb6, 0x4b, 0xb9, 0x1f,
-  0x6d, 0x24, 0x2c, 0xc3, 0xa7, 0xc2, 0x40, 0x3a, 0x50, 0xb2, 0x03, 0xc1,
-  0x23, 0x04, 0x2a, 0x50, 0x7a, 0xbe, 0x76, 0x85, 0x44, 0x95, 0xc8, 0xc0,
-  0xab, 0xc5, 0x8e, 0x1c, 0x1c, 0x15, 0xd4, 0xf4, 0x42, 0xd1, 0x84, 0xe7,
-  0xc9, 0xa5, 0x7f, 0xad, 0x3d, 0x6b, 0x01, 0x76, 0x03, 0x94, 0xc8, 0xee,
-  0x16, 0x6a, 0xdd, 0x1e, 0x1b, 0x37, 0x42, 0x94, 0x1d, 0xfd, 0xaa, 0x62,
-  0xe1, 0xca, 0x2b, 0x28, 0x9c, 0x18, 0x2a, 0xea, 0x6c, 0x72, 0x6b, 0xee,
-  0x83, 0x26, 0xa8, 0x8b, 0x0a, 0x11, 0x94, 0x88, 0xa8, 0xe0, 0x00, 0xf4,
-  0xcf, 0xa5, 0x4a, 0xe4, 0xba, 0x96, 0x5b, 0x51, 0x21, 0x70, 0xd2, 0x9d,
-  0x72, 0x37, 0x9d, 0xe2, 0xc2, 0x31, 0xe6, 0x98, 0x52, 0x4a, 0x3a, 0x08,
-  0x92, 0x03, 0xc5, 0x92, 0xd3, 0x3f, 0x3d, 0xfd, 0x7f, 0xc4, 0x7f, 0xf7,
-  0x03, 0xe7, 0xd1, 0x08, 0x28, 0x22, 0xa0, 0x60, 0x6a, 0x1d, 0x80, 0x00,
-  0xa8, 0xad, 0x00, 0xc1, 0x01, 0x67, 0x21, 0x6b, 0x49, 0x77, 0x95, 0x97,
-  0x65, 0x15, 0xa9, 0xa1, 0xbf, 0x86, 0x57, 0x2a, 0x82, 0xee, 0x33, 0xa2,
-  0xd7, 0x97, 0xe0, 0x9e, 0xab, 0xb0, 0x46, 0xb5, 0xc8, 0x13, 0x71, 0x13,
-  0x03, 0x54, 0xf2, 0x91, 0x23, 0x41, 0x1e, 0x77, 0x2f, 0xe9, 0x50, 0x86,
-  0xc3, 0x13, 0x1d, 0x55, 0xa2, 0x59, 0x4e, 0x95, 0xb1, 0x54, 0x86, 0x03,
-  0x26, 0x8c, 0x2b, 0xe3, 0x52, 0x58, 0x7e, 0x7f, 0x93, 0x1c, 0x3a, 0x51,
-  0x1b, 0x61, 0xdc, 0xf2, 0x72, 0x98, 0xa9, 0xa9, 0x09, 0xd7, 0x22, 0xc9,
-  0xf3, 0x33, 0x93, 0xc5, 0x51, 0x53, 0x05, 0x44, 0xba, 0x73, 0x98, 0x00,
-  0xad, 0x8c, 0x67, 0x42, 0xfc, 0x9a, 0xb4, 0x8a, 0x74, 0x8d, 0xf1, 0x80,
-  0x4a, 0x2c, 0xb2, 0x3c, 0x68, 0xcd, 0xd6, 0xb3, 0x5d, 0x75, 0xe7, 0x00,
-  0x1c, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x0d, 0x6f, 0xa2,
-  0x41, 0x18, 0x48, 0x31, 0x4b, 0x11, 0xc2, 0x00, 0xbb, 0x58, 0xe0, 0x45,
-  0xd6, 0x2f, 0x08, 0x04, 0x26, 0x0b, 0x40, 0x24, 0x64, 0xd0, 0x26, 0x68,
-  0xa4, 0x2d, 0xc1, 0x24, 0xc9, 0xe4, 0x05, 0x20, 0x88, 0x01, 0xfb, 0xb9,
-  0x7d, 0x53, 0x30, 0x7e, 0xd9, 0xfb, 0xe2, 0x02, 0x06, 0xf2, 0xf2, 0x7c,
-  0x14, 0x3f, 0x5f, 0x45, 0x77, 0x86, 0xc0, 0x90, 0x72, 0x4f, 0x31, 0xc2,
-  0x77, 0x1c, 0x97, 0xb7, 0x27, 0x42, 0x75, 0x1a, 0x3d, 0xd9, 0x94, 0xf1,
-  0xaf, 0xe8, 0x9d, 0x3b, 0x4f, 0x56, 0x05, 0x75, 0xb4, 0xc1, 0xee, 0xfa,
-  0x42, 0xbd, 0x1e, 0x7b, 0xec, 0x39, 0x95, 0x41, 0xf3, 0x0f, 0x4d, 0x57,
-  0x53, 0x6a, 0xd7, 0x2c, 0xe0, 0x21, 0x30, 0xd9, 0x4f, 0x9c, 0xc7, 0xbe,
-  0x59, 0x05, 0x06, 0x00, 0x41, 0x98, 0xc8, 0xa8, 0x42, 0x06, 0x50, 0xac,
-  0x91, 0x51, 0xb4, 0xd2, 0x44, 0x3d, 0xcf, 0xdd, 0xe6, 0x0b, 0x0c, 0x41,
-  0xaa, 0x37, 0x91, 0xe0, 0x09, 0x14, 0xc0, 0x5d, 0xb5, 0xeb, 0xcf, 0x66,
-  0xfa, 0x0d, 0x91, 0xf6, 0x17, 0x68, 0x4a, 0x86, 0x48, 0xa4, 0xb7, 0xb1,
-  0x81, 0x69, 0x32, 0xf6, 0xd9, 0xbf, 0x44, 0xec, 0x33, 0xa1, 0xc9, 0x4c,
-  0xdd, 0xaf, 0x04, 0xe3, 0xf6, 0xe1, 0x59, 0xb9, 0xd7, 0x2c, 0x2f, 0xa3,
-  0x5d, 0xb3, 0xc8, 0x96, 0x25, 0x64, 0xac, 0x78, 0x53, 0xe7, 0x2f, 0x13,
-  0x8e, 0xd6, 0x80, 0x47, 0x40, 0x8a, 0x4e, 0x6a, 0xd4, 0xec, 0xef, 0xd8,
-  0xaf, 0x95, 0x96, 0x0d, 0x45, 0x35, 0xe8, 0x07, 0x0d, 0x29, 0x5e, 0xe7,
-  0xab, 0x8a, 0x89, 0x3d, 0xa2, 0xc1, 0x48, 0xa6, 0x03, 0x95, 0x12, 0x0e,
-  0x79, 0xf0, 0xab, 0xf9, 0xdb, 0x7f, 0xd7, 0xaa, 0xeb, 0x34, 0xfa, 0x7c,
-  0xba, 0x7a, 0xd2, 0x00, 0xf9, 0xfc, 0x14, 0xb1, 0x14, 0x3b, 0x00, 0x01,
-  0x2a, 0x54, 0xa1, 0x13, 0x45, 0x60, 0x59, 0x30, 0x6d, 0x8f, 0x38, 0x55,
-  0x33, 0x64, 0x3a, 0x43, 0x5d, 0x6b, 0x62, 0xff, 0x48, 0xc6, 0x34, 0x4f,
-  0xe8, 0xe7, 0xf8, 0xc6, 0x29, 0x40, 0x52, 0xbb, 0x94, 0x01, 0x2b, 0x8f,
-  0xf2, 0xae, 0xf2, 0x52, 0x97, 0xda, 0x53, 0x47, 0x3a, 0xb3, 0xc2, 0x34,
-  0x13, 0x0c, 0xe0, 0x41, 0x72, 0x80, 0x51, 0x82, 0xca, 0x34, 0x2c, 0xd5,
-  0xd4, 0xeb, 0x47, 0x5b, 0x4a, 0x57, 0x94, 0xdc, 0x22, 0x80, 0x25, 0xa0,
-  0xa5, 0xc6, 0xa8, 0xe1, 0xfb, 0xb0, 0xb5, 0x9d, 0x32, 0xd9, 0x37, 0xeb,
-  0xa6, 0x3f, 0x2d, 0xfb, 0x78, 0x7a, 0xf7, 0x18, 0xe4, 0x01, 0xc0, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x72, 0x83, 0x21, 0x49,
-  0x0c, 0x45, 0x08, 0x13, 0x50, 0xe1, 0x15, 0xc1, 0xa2, 0x94, 0x00, 0x10,
-  0x9b, 0x15, 0xa1, 0xc8, 0x10, 0xbd, 0xda, 0x42, 0x90, 0xe8, 0x01, 0xe3,
-  0xc2, 0xf3, 0x31, 0x23, 0x8a, 0xd1, 0x37, 0xd4, 0x31, 0x9f, 0x3e, 0xd8,
-  0xa4, 0x33, 0x57, 0xb0, 0xfd, 0xfd, 0x50, 0x80, 0xc9, 0xaa, 0x20, 0x9f,
-  0x03, 0x4b, 0xd9, 0x30, 0x4f, 0x4b, 0xe3, 0x79, 0xf8, 0xae, 0x5d, 0x0f,
-  0x12, 0xcd, 0xad, 0x76, 0x1e, 0x91, 0x82, 0x59, 0x54, 0x21, 0xa7, 0x27,
-  0x2c, 0x24, 0x57, 0x13, 0x91, 0x24, 0x97, 0x4a, 0xaa, 0x2a, 0xa5, 0xe3,
-  0x78, 0xf9, 0xb4, 0xdf, 0x6f, 0x77, 0x8f, 0xfe, 0x8c, 0x7f, 0xd7, 0x3b,
-  0xc4, 0x4d, 0x02, 0xb3, 0x5b, 0x2c, 0xa2, 0xf2, 0xf8, 0x64, 0xd3, 0xb8,
-  0x6b, 0xb3, 0xa0, 0x13, 0xd8, 0x51, 0x3d, 0xc2, 0x12, 0xa2, 0xd2, 0x72,
-  0x33, 0xee, 0x6b, 0x20, 0x29, 0x65, 0x01, 0x49, 0x3a, 0x2e, 0xc8, 0x2e,
-  0x0d, 0x29, 0x5a, 0x6b, 0xe8, 0xd7, 0x59, 0xa3, 0x55, 0xde, 0xd5, 0xd3,
-  0x91, 0x84, 0x79, 0x72, 0x4a, 0x4e, 0x11, 0x1d, 0xa2, 0x90, 0xcc, 0x54,
-  0xc6, 0x92, 0x91, 0xb2, 0xd3, 0xa8, 0xc3, 0xb2, 0x14, 0x98, 0xc3, 0x74,
-  0xea, 0x32, 0xdf, 0x93, 0x80, 0x91, 0x75, 0x42, 0x84, 0xa5, 0x69, 0xd4,
-  0x01, 0x46, 0x28, 0xbb, 0xf8, 0x55, 0x24, 0xe3, 0x98, 0xee, 0xc9, 0xc6,
-  0xf7, 0x9a, 0x6e, 0x09, 0x9d, 0xe0, 0x2a, 0x57, 0x5a, 0x6d, 0xba, 0x72,
-  0x90, 0x66, 0x8a, 0x2c, 0x79, 0xb2, 0x6a, 0x86, 0x11, 0x2c, 0xc1, 0xf4,
-  0x47, 0x6e, 0x0b, 0x72, 0xd9, 0x6b, 0x01, 0xda, 0xe8, 0x00, 0x18, 0xe7,
-  0x83, 0x79, 0x5e, 0x36, 0x49, 0x4e, 0x1f, 0xd7, 0x1f, 0x7c, 0xe3, 0x7c,
-  0x7a, 0x40, 0x0f, 0x7f, 0xb1, 0x52, 0x0a, 0x04, 0xe1, 0xd8, 0x00, 0x09,
-  0x46, 0x5c, 0x54, 0x90, 0x50, 0x0b, 0xa0, 0x8d, 0xab, 0xa4, 0xc3, 0x34,
-  0x14, 0x95, 0xeb, 0x86, 0xfc, 0xe6, 0xa4, 0x44, 0x2a, 0xa2, 0x82, 0xc9,
-  0xec, 0x19, 0x33, 0x3b, 0xf0, 0xfc, 0x5a, 0x5b, 0xb9, 0x79, 0x9f, 0x5e,
-  0xb7, 0x77, 0xc3, 0x4b, 0xea, 0x29, 0x69, 0x71, 0x8f, 0x1a, 0x88, 0x7c,
-  0x73, 0x3d, 0x76, 0x4e, 0xf2, 0x83, 0x30, 0x11, 0xe1, 0x49, 0xf6, 0x7d,
-  0x15, 0x0e, 0x8f, 0x70, 0xc1, 0xde, 0xc3, 0x3e, 0x94, 0xe8, 0x5d, 0x00,
-  0xde, 0xbf, 0xbe, 0xd1, 0xbc, 0x4d, 0x0c, 0x6b, 0x98, 0x39, 0x48, 0xe1,
-  0x4e, 0x34, 0x61, 0x42, 0xf0, 0x7c, 0x4f, 0x1b, 0x4f, 0x57, 0xc4, 0xd1,
-  0xd2, 0xd8, 0x01, 0xc0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x7f, 0xff, 0xff,
-  0x11, 0x6f, 0xa2, 0xc1, 0x08, 0xc8, 0x51, 0x0b, 0x12, 0x02, 0xa1, 0x00,
-  0x9a, 0x4d, 0x45, 0xaf, 0x38, 0xa4, 0xaa, 0x55, 0x80, 0x25, 0x49, 0x82,
-  0xa2, 0x05, 0x05, 0x52, 0x6f, 0x22, 0x70, 0x10, 0x21, 0x67, 0xe4, 0x90,
-  0x87, 0x0e, 0x8b, 0x5f, 0x59, 0x6a, 0xd9, 0x70, 0x1b, 0xc2, 0xa7, 0x1e,
-  0xe2, 0xe6, 0x8b, 0xac, 0x5b, 0x4d, 0xd6, 0x4d, 0x06, 0xb4, 0xcd, 0x18,
-  0xf2, 0xee, 0x71, 0xc1, 0x89, 0x35, 0x92, 0x33, 0x86, 0x73, 0x73, 0xbb,
-  0x76, 0x3a, 0x1c, 0xb9, 0x72, 0xcd, 0x35, 0x2e, 0xe6, 0x99, 0x4d, 0xef,
-  0x2b, 0xcf, 0x8e, 0xda, 0x67, 0xb1, 0x62, 0xd5, 0xbb, 0x1d, 0x4d, 0x6d,
-  0x96, 0xa1, 0xbc, 0xd5, 0x33, 0xb5, 0xdb, 0x43, 0xba, 0xc3, 0x90, 0xe0,
-  0x59, 0x8a, 0x6a, 0xe0, 0x45, 0x28, 0x54, 0xa8, 0xe3, 0x14, 0x68, 0x92,
-  0xa6, 0x47, 0x2a, 0x06, 0xea, 0x3f, 0x2d, 0x1b, 0x0c, 0x7e, 0x62, 0x18,
-  0xe2, 0x3b, 0x70, 0x33, 0x08, 0xd9, 0x0d, 0x50, 0xab, 0xb3, 0x33, 0xca,
-  0x26, 0xef, 0xf3, 0xc5, 0x78, 0xa1, 0x69, 0x80, 0x8e, 0x7a, 0x90, 0xc0,
-  0x8c, 0x18, 0x6e, 0x9e, 0x75, 0x3a, 0xc4, 0x31, 0xbb, 0x0d, 0x9e, 0xd8,
-  0x52, 0xcb, 0xf6, 0x1f, 0xc5, 0x86, 0x4b, 0xcf, 0x38, 0xe4, 0x14, 0xb0,
-  0x68, 0xbe, 0x0a, 0x60, 0x45, 0xf1, 0x10, 0xb0, 0xc9, 0x77, 0x3e, 0x69,
-  0x68, 0x20, 0x38, 0xa6, 0x63, 0xb3, 0x32, 0x0d, 0xc7, 0x76, 0x72, 0x40,
-  0x27, 0x22, 0x67, 0x31, 0xa4, 0x11, 0xa9, 0x7a, 0x2b, 0x46, 0xe6, 0xeb,
-  0x2b, 0x4a, 0x36, 0x98, 0xd2, 0xd2, 0xd8, 0x0d, 0x95, 0x54, 0x36, 0x3c,
-  0x2f, 0xea, 0x97, 0xb2, 0x83, 0xb8, 0xd5, 0xe8, 0xc4, 0x24, 0x39, 0x7b,
-  0x1e, 0x88, 0x93, 0xb2, 0x06, 0x45, 0xcf, 0x7e, 0xe5, 0x99, 0x52, 0x26,
-  0x37, 0x6b, 0x19, 0xea, 0xfa, 0x79, 0x7b, 0x2b, 0xa7, 0xd9, 0x00, 0x0f,
-  0x7f, 0x40, 0x0a, 0x10, 0x56, 0xa3, 0x81, 0x38, 0x76, 0x00, 0x02, 0x55,
-  0x44, 0x00, 0x00, 0x90, 0x22, 0x20, 0xc4, 0xd0, 0x6a, 0x50, 0x3d, 0xa2,
-  0x70, 0xfa, 0x38, 0x8d, 0x77, 0x99, 0x98, 0x00, 0x55, 0xf9, 0xf8, 0xe7,
-  0x42, 0x90, 0x2b, 0x8d, 0xca, 0x6b, 0xdd, 0x3a, 0xca, 0xbe, 0x62, 0xd4,
-  0x24, 0x4e, 0x8f, 0xc5, 0xfb, 0x52, 0xd0, 0xdb, 0x79, 0xee, 0x69, 0xa6,
-  0xee, 0xdd, 0x8b, 0x54, 0xef, 0xc5, 0xf8, 0xef, 0x45, 0x13, 0x3d, 0x6b,
-  0xa6, 0xee, 0x3b, 0x0b, 0x11, 0x98, 0xab, 0x94, 0x4c, 0x77, 0xab, 0xa2,
-  0xaf, 0x37, 0x6e, 0xda, 0x93, 0xed, 0x81, 0xc7, 0xcb, 0xaa, 0x75, 0xab,
-  0x8a, 0x24, 0x84, 0xc6, 0x3a, 0xd6, 0xb7, 0x39, 0xe8, 0x25, 0xe3, 0xb2,
-  0xc5, 0x9e, 0x5d, 0x16, 0x64, 0x66, 0x34, 0xdd, 0x54, 0xd3, 0x5c, 0x43,
-  0x2a, 0xf7, 0x1b, 0x93, 0xf7, 0xdc, 0x8d, 0x1e, 0xa7, 0x8b, 0x00, 0x1c,
-  0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x15, 0x6f, 0xa2, 0x41,
-  0x50, 0x82, 0x96, 0x22, 0x84, 0x1d, 0x04, 0x70, 0x2c, 0xbb, 0xaa, 0xba,
-  0xa0, 0x14, 0x5a, 0x6e, 0xc3, 0x78, 0xc9, 0xe2, 0xf0, 0xfb, 0x36, 0x31,
-  0x17, 0x82, 0xde, 0x31, 0x39, 0x01, 0xe1, 0x60, 0xd4, 0x80, 0xf4, 0x39,
-  0xf8, 0x34, 0x32, 0xbb, 0x0e, 0xce, 0x45, 0xd4, 0x2e, 0x76, 0xf1, 0x46,
-  0xaa, 0x90, 0x58, 0x77, 0x32, 0xf4, 0x56, 0x6a, 0xc9, 0x71, 0x8f, 0xa1,
-  0x43, 0xfc, 0x2c, 0xf3, 0x91, 0x92, 0xe5, 0x1a, 0x32, 0x11, 0x5f, 0xb1,
-  0xda, 0x40, 0xa1, 0x08, 0x93, 0x2f, 0xef, 0xdc, 0x63, 0x26, 0x46, 0x93,
-  0x5f, 0xb8, 0x5d, 0xbc, 0x50, 0xcc, 0x7e, 0x19, 0xf5, 0x77, 0x23, 0x4c,
-  0x98, 0xc2, 0x2f, 0x1a, 0xf3, 0xc4, 0x26, 0x1e, 0x0c, 0x6b, 0x74, 0x83,
-  0x17, 0x49, 0x25, 0x11, 0x3c, 0x17, 0x8d, 0x6c, 0x8d, 0xe7, 0x6e, 0x20,
-  0x33, 0xd4, 0x22, 0x3d, 0x24, 0xb8, 0xef, 0x42, 0x57, 0xf5, 0x9f, 0x07,
-  0x8b, 0x9c, 0x35, 0x43, 0xde, 0x9a, 0x85, 0x48, 0x97, 0x84, 0x61, 0x26,
-  0x4e, 0xd5, 0xce, 0xbd, 0xa4, 0xa7, 0x85, 0x06, 0x94, 0x3c, 0xc9, 0xa3,
-  0x62, 0x3e, 0x4a, 0xfa, 0xf2, 0x76, 0x5e, 0x77, 0x32, 0xfa, 0xab, 0x05,
-  0xe6, 0xe1, 0x79, 0xfe, 0x34, 0x88, 0x60, 0x64, 0x13, 0xda, 0x75, 0x08,
-  0x49, 0x6f, 0x16, 0x49, 0x4a, 0x59, 0x63, 0xd9, 0xd8, 0xd1, 0x4e, 0x19,
-  0x8c, 0xd6, 0x68, 0x03, 0x0b, 0xa9, 0x5a, 0x93, 0xc3, 0x68, 0xb5, 0x24,
-  0x17, 0xba, 0x82, 0x4a, 0x4e, 0x20, 0xa8, 0xf4, 0x00, 0xd8, 0x68, 0x14,
-  0xac, 0x53, 0x84, 0x5a, 0x91, 0x53, 0x44, 0xb1, 0x3e, 0x7a, 0xba, 0xab,
-  0xa7, 0xdf, 0xf1, 0x00, 0x3e, 0x7f, 0x05, 0x28, 0x26, 0x12, 0x87, 0x60,
-  0x00, 0x02, 0x52, 0x29, 0x2c, 0x18, 0x44, 0x50, 0xc5, 0x80, 0xb5, 0x38,
-  0x53, 0x97, 0x3a, 0x5a, 0x37, 0x9a, 0xb6, 0xc9, 0x47, 0x48, 0xab, 0xfd,
-  0x29, 0x9e, 0xb6, 0x45, 0xa9, 0x0b, 0x58, 0x2a, 0x9b, 0xf0, 0x60, 0x00,
-  0x99, 0x6f, 0xd9, 0xcf, 0x7a, 0xb0, 0x7c, 0xca, 0x5e, 0x49, 0x4f, 0xa4,
-  0xd0, 0x9a, 0x52, 0x8e, 0xae, 0x47, 0xe9, 0xd2, 0x49, 0xfc, 0x58, 0xd0,
-  0x14, 0xed, 0x78, 0x1a, 0x77, 0x63, 0x2f, 0x2d, 0xb4, 0x4f, 0x76, 0xbb,
-  0xac, 0xeb, 0xb3, 0x09, 0xac, 0x9f, 0x05, 0xa2, 0xdb, 0x6e, 0xf5, 0xf3,
-  0xec, 0xf8, 0x6f, 0x68, 0x00, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00,
-  0xff, 0xff, 0x11, 0x6f, 0xa4, 0xa0, 0xc9, 0x4c, 0x43, 0x08, 0x05, 0xb4,
-  0x97, 0x6d, 0xf5, 0x51, 0x69, 0x80, 0x4a, 0x08, 0xa9, 0xb1, 0x5a, 0x34,
-  0x81, 0x00, 0x4c, 0xb1, 0x65, 0x39, 0xe4, 0x48, 0x1a, 0x0c, 0x33, 0x34,
-  0x4f, 0xb1, 0x7d, 0x81, 0xd3, 0x67, 0xa3, 0xb0, 0x7a, 0x4f, 0x57, 0xca,
-  0x83, 0xdd, 0x7c, 0x97, 0x46, 0x13, 0x20, 0x25, 0x11, 0xbe, 0xb2, 0xb8,
-  0xb6, 0x77, 0xe5, 0xc4, 0xbc, 0xaf, 0x2d, 0xe1, 0x98, 0x9a, 0xef, 0xef,
-  0xd7, 0xa5, 0x97, 0xa5, 0x4f, 0x06, 0xac, 0x50, 0xa9, 0xf8, 0x0b, 0x07,
-  0xb6, 0x55, 0x20, 0xf4, 0x91, 0xae, 0x29, 0xf5, 0xd8, 0xeb, 0x4d, 0x1e,
-  0x90, 0x93, 0x49, 0xe3, 0x06, 0xee, 0x95, 0x3b, 0x4e, 0xba, 0xf2, 0x81,
-  0x0f, 0x8b, 0x14, 0x06, 0x78, 0x67, 0xa4, 0x70, 0x69, 0x62, 0xcc, 0x54,
-  0xa5, 0xf1, 0xb3, 0xab, 0x99, 0x6a, 0xcd, 0x03, 0x91, 0xba, 0x52, 0xf6,
-  0xad, 0xfc, 0x76, 0x66, 0x1e, 0xc3, 0xfe, 0xaf, 0xa2, 0xee, 0x6a, 0x23,
-  0x48, 0x70, 0x3f, 0x45, 0x37, 0x65, 0xe3, 0x43, 0x10, 0x0d, 0x19, 0x86,
-  0x58, 0xc9, 0x37, 0x18, 0x0a, 0xcd, 0xa5, 0x25, 0x8a, 0xd6, 0x06, 0xf3,
-  0x2c, 0x1c, 0xc0, 0x81, 0xdd, 0x93, 0x87, 0x54, 0x17, 0xfd, 0xc9, 0x37,
-  0x05, 0x24, 0x18, 0x28, 0x96, 0xcc, 0xe7, 0x3c, 0x07, 0x94, 0xc6, 0xb8,
-  0xc2, 0x5a, 0xee, 0x77, 0xe6, 0x52, 0xa1, 0x21, 0xed, 0x27, 0xf9, 0x3e,
-  0x67, 0x56, 0xa3, 0x2a, 0x2b, 0x94, 0x45, 0x39, 0x0b, 0x9b, 0x38, 0xd7,
-  0x40, 0xd7, 0x5e, 0x0d, 0x52, 0xd1, 0x1c, 0x56, 0x45, 0x2a, 0x82, 0xc9,
-  0x26, 0x28, 0x66, 0xb1, 0x8a, 0x6d, 0x9d, 0x14, 0x67, 0xb2, 0x80, 0x21,
-  0x79, 0x83, 0x82, 0xee, 0xde, 0xdf, 0xff, 0x7b, 0xff, 0xb9, 0xf2, 0x20,
-  0xf9, 0xfb, 0x15, 0x20, 0x58, 0x66, 0x1d, 0x80, 0x94, 0x00, 0x15, 0x75,
-  0x2a, 0x00, 0x12, 0x56, 0x02, 0x75, 0xa1, 0x36, 0x87, 0x32, 0x31, 0x48,
-  0x9a, 0xc3, 0x32, 0xf2, 0x0b, 0xc0, 0xa5, 0x9d, 0x78, 0x62, 0xae, 0x8c,
-  0xd7, 0x4a, 0xeb, 0x16, 0x6e, 0xfd, 0x65, 0x0e, 0x56, 0xbc, 0x65, 0x38,
-  0xf5, 0x6b, 0xf1, 0xb7, 0xd7, 0x0a, 0x54, 0x3a, 0xf3, 0xaa, 0x44, 0x4f,
-  0x19, 0x05, 0xec, 0xe0, 0xdd, 0xd9, 0xcf, 0x58, 0x62, 0x1b, 0x0a, 0xd8,
-  0xba, 0xbc, 0x78, 0xf0, 0xe1, 0x62, 0xe7, 0x2c, 0xba, 0x3a, 0xa8, 0xa7,
-  0x30, 0x09, 0x4b, 0xc1, 0x26, 0x50, 0x92, 0xca, 0x47, 0x1a, 0x67, 0xff,
-  0xac, 0xff, 0x7b, 0xd1, 0xb6, 0x03, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00,
-  0x07, 0xff, 0xff, 0x17, 0x73, 0x82, 0xa0, 0x49, 0x6c, 0x54, 0x08, 0x0d,
-  0x2e, 0x5b, 0xa5, 0x38, 0x2d, 0x4a, 0x05, 0x02, 0xc5, 0x50, 0x91, 0x2e,
-  0xe8, 0x7d, 0x63, 0x95, 0x1a, 0x41, 0x48, 0xf1, 0xeb, 0xe2, 0x65, 0x4d,
-  0x88, 0x2e, 0xd4, 0xd9, 0x39, 0x08, 0x2e, 0x4e, 0xd9, 0xc8, 0xff, 0x47,
-  0xe1, 0xd9, 0x86, 0xcc, 0x25, 0x39, 0x55, 0xd2, 0x3b, 0x82, 0x8d, 0x43,
-  0xe9, 0xe9, 0xa4, 0xe1, 0x6a, 0x48, 0xe6, 0x9c, 0x58, 0xd5, 0xdc, 0x51,
-  0xbd, 0xfc, 0x33, 0x2d, 0x4b, 0xae, 0xf6, 0x2f, 0xb4, 0xeb, 0xbd, 0x71,
-  0xb0, 0x70, 0xa9, 0x0a, 0xa8, 0xcf, 0xb9, 0xed, 0xfc, 0xe8, 0x8d, 0xd5,
-  0x96, 0xe2, 0xb7, 0x36, 0x9b, 0xcd, 0xd9, 0xea, 0x14, 0xa1, 0x5b, 0x00,
-  0x5b, 0x64, 0x6d, 0x72, 0x0a, 0xd3, 0x48, 0xc4, 0xb7, 0xbb, 0xcc, 0xba,
-  0xac, 0xda, 0x17, 0x89, 0x4b, 0x95, 0x57, 0x05, 0x63, 0x7e, 0x3c, 0xaa,
-  0x75, 0xb8, 0x12, 0x44, 0xba, 0xc6, 0xd9, 0x36, 0xb9, 0xfb, 0x14, 0x78,
-  0x4e, 0x85, 0x2b, 0x73, 0xfc, 0xf6, 0x25, 0xde, 0x52, 0x7b, 0x75, 0x51,
-  0xb5, 0x3f, 0xeb, 0xd4, 0x0e, 0x88, 0x22, 0x42, 0x01, 0xe9, 0xd0, 0x56,
-  0xa9, 0x09, 0x31, 0x84, 0x9d, 0x67, 0xbd, 0x87, 0x08, 0x32, 0x50, 0xb9,
-  0xb1, 0x38, 0x10, 0x10, 0xeb, 0x75, 0x40, 0x44, 0x3d, 0x29, 0xfd, 0xb4,
-  0x6b, 0xd5, 0x7a, 0x55, 0x2c, 0xe6, 0x7d, 0x10, 0x9d, 0x0c, 0x4c, 0xd0,
-  0x6b, 0x32, 0x86, 0x40, 0x05, 0xaa, 0xd3, 0x10, 0x99, 0x82, 0x18, 0x1b,
-  0xa9, 0xdf, 0x2b, 0x2a, 0x04, 0x89, 0x45, 0xe6, 0x23, 0x42, 0x92, 0xae,
-  0xa4, 0xa6, 0x53, 0xb8, 0xe5, 0x98, 0x8c, 0xb9, 0xdd, 0x9a, 0x60, 0x45,
-  0x6d, 0x08, 0x52, 0x65, 0x23, 0x19, 0x96, 0xeb, 0xb0, 0xff, 0xe8, 0x9f,
-  0xfe, 0x51, 0xa7, 0x77, 0x77, 0xcf, 0xc0, 0x08, 0x56, 0x81, 0x61, 0x98,
-  0x76, 0x00, 0x00, 0x8a, 0x25, 0x80, 0xc2, 0x44, 0xc1, 0xf9, 0x77, 0xb6,
-  0xa6, 0xf5, 0xd1, 0x86, 0xe1, 0x56, 0x80, 0xd0, 0xa2, 0xa8, 0x27, 0xf4,
-  0xac, 0x5c, 0x42, 0x32, 0x8c, 0xca, 0x7a, 0x33, 0xec, 0xf3, 0x6c, 0xd0,
-  0x2d, 0x39, 0xca, 0x66, 0x84, 0x43, 0x1d, 0x3f, 0x03, 0x2d, 0xc5, 0xa2,
-  0x3e, 0x36, 0x2f, 0xe8, 0xc3, 0x37, 0x04, 0xe2, 0x74, 0x53, 0xae, 0x2d,
-  0x4d, 0x59, 0x4b, 0x6c, 0xad, 0x09, 0xa9, 0x02, 0xfb, 0x5d, 0x56, 0xe1,
-  0x6c, 0x77, 0xe4, 0xfd, 0x9d, 0x2c, 0xa3, 0xa2, 0x02, 0xe1, 0xeb, 0x80,
-  0x6e, 0x53, 0x53, 0xf3, 0xfb, 0xa7, 0xff, 0xfa, 0xfc, 0x9f, 0xfb, 0x10,
-  0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x07, 0xff, 0xff, 0x15, 0x73, 0x82,
-  0xaa, 0x08, 0xac, 0x43, 0x08, 0x0b, 0xb3, 0x4b, 0x89, 0xc5, 0x48, 0xab,
-  0xac, 0x00, 0x84, 0x4c, 0xd8, 0x26, 0x38, 0x17, 0x4c, 0x0b, 0xa4, 0x16,
-  0xe4, 0x9a, 0x20, 0x9f, 0x67, 0xc1, 0xcf, 0x51, 0x8b, 0xb2, 0xae, 0x82,
-  0x7a, 0x96, 0x46, 0xc4, 0x78, 0x7e, 0x1f, 0x28, 0x87, 0x8c, 0xb7, 0xb4,
-  0xae, 0x3f, 0xc4, 0x2d, 0x74, 0xe2, 0xb6, 0xc7, 0xec, 0x7e, 0x48, 0x98,
-  0x78, 0xaf, 0x46, 0x38, 0xd3, 0xa1, 0xaf, 0x5d, 0x79, 0x8e, 0x3b, 0x91,
-  0x28, 0x45, 0x37, 0x76, 0x3e, 0x8b, 0xa3, 0x29, 0xc4, 0xad, 0xa7, 0x6d,
-  0xe4, 0x89, 0xb1, 0x10, 0x68, 0x9b, 0xdd, 0xb3, 0xcc, 0x69, 0xfd, 0x0d,
-  0x4e, 0xb6, 0xd9, 0x0f, 0xef, 0x59, 0x2c, 0x34, 0xb7, 0xa8, 0xb2, 0xce,
-  0x6d, 0xcd, 0x0d, 0x94, 0x58, 0xf8, 0xb7, 0x95, 0xcb, 0xba, 0x54, 0x55,
-  0x25, 0x9f, 0x51, 0x20, 0xcd, 0xd4, 0x70, 0x8b, 0xc5, 0xfd, 0x64, 0xc4,
-  0x4e, 0xd7, 0xa3, 0xb5, 0xd9, 0xd7, 0xc2, 0x65, 0x29, 0xe1, 0x59, 0x1a,
-  0xd9, 0x69, 0xb2, 0x2e, 0x58, 0x4b, 0x17, 0x4c, 0x37, 0x4b, 0x46, 0xd6,
-  0xad, 0xae, 0x82, 0xc9, 0x40, 0x52, 0x97, 0x48, 0x64, 0x95, 0x95, 0x50,
-  0x0d, 0x42, 0x18, 0x6e, 0xb1, 0x78, 0x73, 0x24, 0xce, 0xb0, 0x10, 0x0c,
-  0x14, 0xc0, 0x5c, 0xaa, 0x3d, 0xb8, 0xae, 0x54, 0x19, 0x53, 0x67, 0xed,
-  0x1c, 0x29, 0x17, 0x8c, 0x6d, 0x0c, 0x8a, 0x2d, 0x12, 0x14, 0x3c, 0xb2,
-  0xb1, 0x33, 0x95, 0x4a, 0x08, 0xb6, 0xd6, 0xce, 0xe2, 0x11, 0x7f, 0x0b,
-  0x16, 0x86, 0xc7, 0x5b, 0xea, 0xaf, 0x05, 0xfd, 0xe5, 0x49, 0x3e, 0x13,
-  0x4b, 0xde, 0xa9, 0x95, 0x0a, 0x73, 0xd2, 0x27, 0x12, 0x47, 0x37, 0xc0,
-  0xe2, 0x5f, 0xeb, 0x36, 0x3e, 0x05, 0xc1, 0xf7, 0xf0, 0x2b, 0x21, 0xb0,
-  0xcc, 0x3b, 0x00, 0x00, 0x2a, 0x0b, 0x00, 0x02, 0xb5, 0xc8, 0xe1, 0x39,
-  0x30, 0xfb, 0x91, 0x85, 0x8b, 0xa3, 0xbe, 0x8e, 0xf6, 0x20, 0x77, 0xa6,
-  0x76, 0xf2, 0x62, 0x42, 0x2d, 0x76, 0x9d, 0x80, 0x5e, 0xb0, 0x6f, 0xd8,
-  0xbd, 0x2b, 0xc8, 0x0e, 0x96, 0xbd, 0x04, 0x69, 0xf2, 0xd4, 0x90, 0x82,
-  0x17, 0x8b, 0xed, 0x39, 0x05, 0xfb, 0x6d, 0xd2, 0x81, 0xd3, 0xb7, 0xe1,
-  0xb5, 0x29, 0x80, 0xc2, 0xd9, 0x28, 0x2b, 0x51, 0x40, 0xac, 0x24, 0x53,
-  0x79, 0xa0, 0x89, 0x22, 0xa7, 0x34, 0x52, 0xf0, 0xd5, 0xf7, 0xca, 0x26,
-  0xd6, 0xfc, 0x66, 0xcb, 0xf1, 0xfa, 0xc2, 0xec, 0x76, 0xfe, 0xe7, 0xfa,
-  0x20, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x01, 0xff, 0xff, 0x19, 0x6f,
-  0xa1, 0xc1, 0xd0, 0x44, 0x45, 0x09, 0x15, 0x88, 0x61, 0x04, 0x96, 0x70,
-  0x95, 0x73, 0x53, 0x38, 0xab, 0x14, 0x00, 0x10, 0xc1, 0x82, 0x92, 0x5c,
-  0x77, 0x31, 0x73, 0x06, 0x4e, 0x11, 0x17, 0xb2, 0xcf, 0x37, 0xe7, 0x30,
-  0x30, 0x7e, 0x02, 0xf5, 0xe2, 0x37, 0xbe, 0x33, 0xed, 0x3f, 0x1a, 0xb5,
-  0xb2, 0x6a, 0x40, 0x79, 0x84, 0xba, 0x4b, 0x6e, 0xf3, 0xda, 0xf6, 0x0b,
-  0x5e, 0xaf, 0xcc, 0x38, 0xe1, 0xb8, 0xee, 0xab, 0xe0, 0x0f, 0xe5, 0x2a,
-  0x4b, 0x45, 0x4b, 0x23, 0x53, 0x80, 0xce, 0x99, 0xab, 0xbe, 0x26, 0xea,
-  0x39, 0x8b, 0x37, 0xbc, 0x6a, 0x4e, 0x1b, 0x23, 0x00, 0xcf, 0x76, 0x5f,
-  0x14, 0x66, 0xeb, 0xf8, 0x2f, 0xec, 0xc8, 0x9d, 0x23, 0x0c, 0x7a, 0xf2,
-  0xd7, 0x6f, 0x42, 0x7f, 0x8e, 0x7c, 0xdb, 0x18, 0x9e, 0x36, 0xb2, 0x38,
-  0xae, 0x67, 0x00, 0x74, 0x0b, 0x64, 0x8c, 0xd0, 0x63, 0xd1, 0x2e, 0x75,
-  0xb9, 0x45, 0x01, 0xf8, 0xe0, 0x33, 0xcb, 0x2e, 0x93, 0x63, 0xa1, 0x14,
-  0x89, 0x19, 0x3e, 0xaf, 0x36, 0xc3, 0xf9, 0x02, 0xf3, 0xe0, 0x95, 0x77,
-  0xab, 0x99, 0xa6, 0xc7, 0x54, 0xe0, 0x58, 0xb8, 0xa0, 0x83, 0x22, 0xd0,
-  0xa5, 0x82, 0x5b, 0x28, 0x44, 0xaf, 0xa2, 0xc9, 0xff, 0x56, 0x73, 0xa9,
-  0x14, 0xcc, 0x3d, 0x45, 0x80, 0xe3, 0xa4, 0x6d, 0x36, 0x26, 0x17, 0xa8,
-  0x05, 0x52, 0xa3, 0xf4, 0xa6, 0x74, 0xdc, 0xa5, 0x1c, 0x74, 0x83, 0x20,
-  0x21, 0x33, 0x8a, 0x1e, 0x20, 0x16, 0x19, 0x89, 0x26, 0x05, 0xca, 0xeb,
-  0x6c, 0x6d, 0x52, 0xf5, 0x1b, 0x5d, 0xcf, 0xaa, 0x48, 0x96, 0x8b, 0x12,
-  0x6a, 0x80, 0xae, 0xee, 0xb2, 0x97, 0x53, 0x64, 0x10, 0xc2, 0x1a, 0xc9,
-  0x9a, 0xac, 0xfe, 0x17, 0xe0, 0x3a, 0x9e, 0xe0, 0x3f, 0x7e, 0x85, 0x68,
-  0x43, 0x0e, 0xc0, 0x02, 0x51, 0x2a, 0xa0, 0x93, 0x40, 0x28, 0x32, 0x06,
-  0xd6, 0x85, 0xa9, 0x41, 0x41, 0x21, 0x1b, 0x57, 0x7a, 0xf2, 0x90, 0xa3,
-  0x3a, 0x8d, 0x51, 0xab, 0x52, 0xb0, 0x78, 0x29, 0xd8, 0x4e, 0x87, 0x5c,
-  0x98, 0x69, 0x55, 0x96, 0xcd, 0x58, 0x96, 0x33, 0x29, 0xfb, 0x37, 0xd3,
-  0x91, 0x8d, 0xe2, 0xa6, 0xf7, 0x1e, 0x28, 0xb8, 0xa7, 0x1a, 0xb7, 0x3d,
-  0x23, 0xf7, 0xef, 0x8a, 0x82, 0xc7, 0x16, 0xc7, 0xc4, 0x11, 0xd4, 0x06,
-  0x53, 0xda, 0x93, 0x2f, 0x9b, 0xf6, 0x4a, 0x4e, 0x2c, 0x2f, 0x4b, 0xe3,
-  0x3f, 0x3f, 0x50, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x3f, 0xff, 0xff,
-  0x1d, 0x73, 0x82, 0xa0, 0xc9, 0x28, 0x26, 0x1a, 0x84, 0x2e, 0xd3, 0xab,
-  0x21, 0x1c, 0x25, 0xd4, 0x9b, 0x00, 0x05, 0xab, 0x07, 0xdc, 0xc9, 0x42,
-  0x2d, 0x40, 0x1a, 0x04, 0x92, 0xfa, 0x39, 0x7e, 0x55, 0x13, 0x8b, 0x43,
-  0x58, 0x83, 0xe1, 0xd8, 0x20, 0xec, 0x0f, 0xd4, 0xc9, 0x5d, 0x2b, 0xd2,
-  0x3f, 0x94, 0x27, 0x38, 0xd9, 0xd5, 0x12, 0x0e, 0xb7, 0xed, 0x5b, 0x25,
-  0xf5, 0xab, 0xa2, 0x5b, 0xa3, 0xbd, 0xa3, 0x1c, 0xe5, 0x91, 0xdb, 0xf8,
-  0x5b, 0xb6, 0x8c, 0x9b, 0xa6, 0x52, 0x39, 0xa3, 0x96, 0x28, 0x64, 0xc5,
-  0x65, 0x68, 0x0d, 0x5a, 0x32, 0x30, 0xc9, 0x34, 0xc0, 0x8e, 0x85, 0xab,
-  0xee, 0xe7, 0xf0, 0xc8, 0x91, 0xc7, 0x03, 0x79, 0xbd, 0xfb, 0x3b, 0x72,
-  0xb8, 0xd0, 0xd2, 0x4f, 0xde, 0xe9, 0x5d, 0xc9, 0xaa, 0x7d, 0xb9, 0x83,
-  0x5e, 0xa2, 0x51, 0x15, 0x6d, 0xea, 0x6b, 0xb2, 0x3b, 0xbe, 0xc6, 0xbf,
-  0x6b, 0x54, 0xd6, 0xd3, 0x22, 0x48, 0x11, 0xd6, 0x3b, 0xd6, 0x1a, 0xeb,
-  0xbc, 0xc3, 0x9b, 0x07, 0x05, 0xd3, 0xcc, 0x78, 0x66, 0x30, 0x5e, 0x6b,
-  0xc0, 0x18, 0x18, 0xcd, 0xde, 0x26, 0x1b, 0x56, 0x17, 0xee, 0x0f, 0x19,
-  0x1c, 0xe7, 0xa5, 0xcb, 0x18, 0x47, 0x61, 0xb0, 0x65, 0xba, 0x4e, 0x4e,
-  0x21, 0x5f, 0x7e, 0xf2, 0x6e, 0xaf, 0x30, 0x13, 0x2f, 0x90, 0xee, 0xb1,
-  0x99, 0xcd, 0x6a, 0x0f, 0xce, 0x0d, 0xd4, 0x80, 0x10, 0x02, 0xab, 0xc6,
-  0x58, 0x7b, 0xba, 0x32, 0x86, 0x79, 0x2c, 0x55, 0xf3, 0xfe, 0x99, 0x03,
-  0x79, 0x7a, 0x48, 0x57, 0x7c, 0xc7, 0xa0, 0x52, 0xed, 0x2f, 0x87, 0x9a,
-  0xc6, 0xf3, 0x0d, 0xa5, 0x2c, 0xd9, 0xc9, 0x9f, 0xb9, 0xff, 0x58, 0x87,
-  0x66, 0x22, 0x21, 0x32, 0x48, 0x3a, 0x3e, 0x9a, 0x69, 0x95, 0xae, 0xd7,
-  0x5c, 0xe3, 0x97, 0x4f, 0x68, 0x01, 0xef, 0xea, 0x83, 0x16, 0xb1, 0x1c,
-  0x3b, 0x00, 0x00, 0xa8, 0xa0, 0x00, 0x10, 0x8a, 0x17, 0xa3, 0x62, 0xcc,
-  0x8b, 0xf2, 0x6d, 0x27, 0xc9, 0xc9, 0x40, 0x2f, 0x2b, 0x27, 0xc8, 0xba,
-  0x32, 0x90, 0x1b, 0x96, 0x8a, 0xd8, 0x6c, 0xa5, 0xca, 0x2f, 0x7a, 0x44,
-  0x1d, 0x93, 0x9a, 0x4a, 0x8d, 0x0d, 0x6e, 0x71, 0x92, 0xc5, 0x3c, 0x1a,
-  0x89, 0xfa, 0xa1, 0x08, 0xdf, 0x76, 0x8d, 0x59, 0xb4, 0x7d, 0x83, 0x1a,
-  0x39, 0x6d, 0x9f, 0xd7, 0xbb, 0xf6, 0x41, 0xa6, 0x04, 0x26, 0x71, 0x38,
-  0x5f, 0x3d, 0x46, 0x33, 0xb9, 0x3c, 0xc2, 0x5b, 0x20, 0xcc, 0xec, 0xfc,
-  0x53, 0x5b, 0x59, 0xa8, 0xa6, 0xc7, 0xc9, 0x26, 0x2f, 0x76, 0x0b, 0x45,
-  0x92, 0xc9, 0xfc, 0xa6, 0x42, 0x19, 0x74, 0xa6, 0x25, 0x3e, 0xe1, 0xbf,
-  0x67, 0x6f, 0x6b, 0xe8, 0x6b, 0x5b, 0x4e, 0x33, 0x80, 0x0e, 0x21, 0x1a,
-  0x88, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x19, 0x73, 0x82, 0x30, 0xd0, 0x66,
-  0x22, 0x32, 0x05, 0x86, 0xa1, 0x05, 0x69, 0x4b, 0xf8, 0x14, 0xe1, 0x68,
-  0x28, 0x00, 0x22, 0x04, 0xfa, 0xb9, 0xfc, 0xdc, 0xef, 0xf9, 0xfc, 0x1d,
-  0xad, 0x64, 0xa1, 0x1b, 0xc0, 0x36, 0x45, 0x1f, 0x4d, 0xcb, 0x62, 0xa7,
-  0x73, 0xa0, 0x2c, 0x1f, 0x84, 0xd5, 0x94, 0xef, 0x74, 0x6a, 0x1c, 0xe5,
-  0xcc, 0xfb, 0x0c, 0x59, 0xb2, 0xe8, 0x05, 0x0a, 0x0a, 0x89, 0x9e, 0x83,
-  0xb1, 0x65, 0x32, 0x28, 0x53, 0x1a, 0x41, 0x49, 0xab, 0xb8, 0xac, 0x45,
-  0xcd, 0xbf, 0x99, 0xa6, 0x2c, 0xbd, 0x48, 0xe2, 0xc6, 0x26, 0x5c, 0x03,
-  0xb4, 0xa7, 0x73, 0xb5, 0xee, 0xca, 0x6b, 0x9c, 0xe1, 0x6c, 0x4a, 0x93,
-  0x4a, 0x82, 0x93, 0xb2, 0x66, 0xd8, 0x66, 0x0c, 0x86, 0xde, 0x6b, 0x37,
-  0x17, 0x4e, 0x94, 0x71, 0xa7, 0x1e, 0xef, 0x53, 0x86, 0x65, 0x4d, 0x50,
-  0x97, 0xaa, 0x6e, 0x4f, 0x5d, 0xbc, 0x1a, 0x77, 0xb6, 0xda, 0x31, 0x4a,
-  0x1d, 0xa7, 0x9e, 0xd0, 0x22, 0x38, 0x58, 0x68, 0x7a, 0x01, 0x15, 0x95,
-  0x5c, 0xc5, 0x9d, 0xd2, 0xae, 0xdb, 0xb1, 0x54, 0x9e, 0xcb, 0x2e, 0x23,
-  0xb5, 0x96, 0xd5, 0xd2, 0x5a, 0x91, 0xea, 0xcf, 0x23, 0x46, 0x85, 0xeb,
-  0xd1, 0xbc, 0x67, 0xbf, 0x14, 0x7a, 0x9f, 0xac, 0xa5, 0x31, 0x80, 0x3e,
-  0x10, 0x7c, 0x76, 0x21, 0xe9, 0x55, 0x02, 0x0d, 0x22, 0xd4, 0x80, 0xac,
-  0x00, 0x8b, 0x1a, 0x92, 0x84, 0xe1, 0xb5, 0x39, 0x8c, 0xc9, 0x6f, 0x00,
-  0x7f, 0x61, 0xbe, 0xb6, 0x1c, 0xc2, 0x0d, 0xf4, 0x02, 0xac, 0xcc, 0x00,
-  0x5c, 0x18, 0x2a, 0x60, 0x42, 0x7b, 0x2c, 0x11, 0xc7, 0xf0, 0x44, 0x56,
-  0x37, 0x3e, 0xa9, 0xf9, 0x51, 0xc3, 0x66, 0xb9, 0x83, 0xb7, 0x1e, 0x9f,
-  0x9a, 0x24, 0x00, 0xfb, 0xfc, 0x14, 0xa0, 0x58, 0x66, 0x1d, 0x80, 0x00,
-  0x54, 0xa9, 0x2a, 0x54, 0x99, 0x61, 0x82, 0xc0, 0x2b, 0x1a, 0x7e, 0xba,
-  0xa9, 0x7b, 0x44, 0x5d, 0x15, 0x21, 0xae, 0x4b, 0x41, 0x25, 0xb4, 0x48,
-  0x70, 0xe8, 0x72, 0x99, 0x7b, 0x2a, 0x41, 0x72, 0x49, 0x3c, 0x32, 0xb8,
-  0x87, 0x80, 0x5a, 0x48, 0xe3, 0x0c, 0x74, 0xb7, 0xf2, 0x06, 0xc9, 0x44,
-  0x3d, 0x2c, 0xd5, 0x8d, 0x06, 0x38, 0xc4, 0x53, 0x42, 0xa5, 0xd7, 0xda,
-  0x2e, 0x16, 0x45, 0x8a, 0xf2, 0xb0, 0xd2, 0x97, 0x64, 0x66, 0x18, 0xa3,
-  0xc0, 0x6a, 0x09, 0xc3, 0xef, 0xdd, 0x5c, 0x3c, 0x80, 0xe0, 0x21, 0x1a,
-  0x88, 0x00, 0x00, 0x00, 0xff, 0xff, 0x19, 0x6f, 0xa4, 0xa1, 0x09, 0x2c,
-  0x43, 0x09, 0x1c, 0xf0, 0x54, 0xea, 0x5a, 0xd2, 0xda, 0x54, 0x94, 0x28,
-  0x10, 0x25, 0x60, 0xdd, 0x35, 0x84, 0x9f, 0xbf, 0xe2, 0xd7, 0x35, 0x57,
-  0x2b, 0x9c, 0x8c, 0x35, 0x5e, 0x53, 0xd4, 0xb2, 0x22, 0x20, 0x0e, 0x29,
-  0xd5, 0x75, 0xb8, 0xff, 0x07, 0xf0, 0xaf, 0x7c, 0x0d, 0xb3, 0xcb, 0x5e,
-  0x7d, 0xc3, 0xda, 0x73, 0xe3, 0x93, 0xc8, 0x79, 0xbf, 0xd8, 0x66, 0x29,
-  0x1a, 0xff, 0xa4, 0xd2, 0xc7, 0xd7, 0xd7, 0xe5, 0xac, 0xd5, 0xbd, 0xe6,
-  0x7c, 0xaf, 0x15, 0xf7, 0x93, 0xb7, 0x28, 0x2e, 0xcf, 0x01, 0x43, 0x26,
-  0xa3, 0x8e, 0xf0, 0x38, 0xaa, 0xdf, 0xc4, 0x24, 0x1e, 0x72, 0xfd, 0x8b,
-  0x25, 0x20, 0x81, 0x7f, 0x3a, 0xc2, 0xaf, 0x32, 0x30, 0xb1, 0x13, 0x93,
-  0x49, 0x1b, 0x7b, 0x8a, 0x61, 0x0b, 0x28, 0xa4, 0x49, 0x22, 0x1d, 0xa4,
-  0x3d, 0x48, 0xd3, 0x7a, 0xd3, 0x20, 0xaf, 0x84, 0x33, 0xa0, 0x37, 0x9f,
-  0x44, 0x4e, 0xb3, 0xd4, 0x7e, 0xed, 0x62, 0x8b, 0xa9, 0xd0, 0x7f, 0xb7,
-  0xc8, 0xaf, 0xa0, 0xb6, 0xc9, 0x05, 0xe0, 0x69, 0x3d, 0x40, 0x0e, 0x82,
-  0x41, 0x50, 0xc2, 0x37, 0x07, 0x30, 0xa9, 0xff, 0x47, 0xd6, 0x75, 0x33,
-  0xf7, 0x67, 0x5c, 0x37, 0x18, 0x0a, 0xde, 0x84, 0x99, 0x7d, 0x29, 0x08,
-  0x33, 0xc0, 0xcf, 0x09, 0xbf, 0x3a, 0x22, 0x3b, 0x5a, 0xf3, 0xfa, 0x03,
-  0xde, 0xc7, 0xa4, 0xd5, 0x3f, 0x34, 0xbd, 0x6a, 0xfe, 0xfe, 0xbb, 0x35,
-  0x10, 0x58, 0x04, 0x25, 0x00, 0x22, 0xce, 0x6d, 0xb5, 0xc0, 0x23, 0xbe,
-  0x94, 0xdf, 0xa8, 0x0a, 0xca, 0x15, 0x2a, 0x27, 0xe7, 0xde, 0x99, 0x61,
-  0x31, 0x35, 0xe9, 0xda, 0x15, 0xd8, 0xf8, 0x31, 0xd8, 0x5c, 0xf2, 0xc0,
-  0xb6, 0xcb, 0x18, 0x86, 0x19, 0x3c, 0xa7, 0xc7, 0xbd, 0x00, 0x3f, 0x7e,
-  0xc5, 0x04, 0x36, 0x18, 0x87, 0x60, 0x00, 0x04, 0xa1, 0x6c, 0xd0, 0x01,
-  0x0e, 0x42, 0x3d, 0x40, 0x66, 0x95, 0xd4, 0xb8, 0x11, 0xb1, 0xbf, 0xbb,
-  0xa2, 0xcc, 0x1c, 0xab, 0xd1, 0x2b, 0xa1, 0xf3, 0x13, 0x22, 0xcd, 0xdb,
-  0x14, 0x77, 0x5d, 0x7d, 0x8e, 0x81, 0x7f, 0x41, 0xbc, 0x61, 0xe5, 0xfe,
-  0x39, 0xd7, 0xcf, 0x0b, 0xbe, 0x4f, 0x72, 0xfa, 0x8f, 0xf1, 0xba, 0x59,
-  0xde, 0xca, 0xbc, 0xb3, 0x2b, 0x7a, 0xa7, 0x8a, 0xad, 0x92, 0x54, 0x48,
-  0x4b, 0xb7, 0xf8, 0x35, 0x71, 0xaf, 0x67, 0x76, 0xcc, 0x2f, 0x7b, 0x3b,
-  0xa7, 0x07, 0x0d, 0x94, 0x49, 0x5c, 0xe9, 0x8f, 0xf3, 0xae, 0x27, 0xfd,
-  0xbf, 0x86, 0x10, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x01, 0xff, 0xff,
-  0x19, 0x6f, 0xa4, 0xb0, 0x88, 0xe8, 0x46, 0x21, 0x84, 0x04, 0xaf, 0x26,
-  0x70, 0xa9, 0xd3, 0xa5, 0x54, 0x40, 0xa1, 0x29, 0x6a, 0x9b, 0x16, 0x81,
-  0x2e, 0xa5, 0xcb, 0x81, 0x20, 0x94, 0xe7, 0x65, 0x5d, 0xc1, 0x9d, 0x11,
-  0x72, 0x7d, 0x13, 0x9a, 0x25, 0x68, 0x0b, 0x91, 0xb8, 0x76, 0x7b, 0x68,
-  0xb6, 0xe9, 0x12, 0x01, 0x3c, 0xce, 0x3e, 0x35, 0xc5, 0x7c, 0x8b, 0x55,
-  0xd8, 0x1a, 0x42, 0x99, 0xe1, 0xb1, 0xc3, 0x85, 0xdf, 0x20, 0xde, 0x69,
-  0x5c, 0xb1, 0xd6, 0x7a, 0x97, 0x02, 0xae, 0xc6, 0xc3, 0x4e, 0xfd, 0xac,
-  0x59, 0x4c, 0xc0, 0x62, 0x39, 0x5e, 0xdd, 0x20, 0xde, 0xdf, 0x5f, 0x61,
-  0xd5, 0xd6, 0xbe, 0x1a, 0x15, 0x2e, 0x8a, 0xc0, 0xe9, 0xa4, 0xf8, 0xac,
-  0x9a, 0x5a, 0x8d, 0x42, 0x3b, 0x1e, 0xd0, 0xc1, 0x5b, 0xa2, 0x94, 0x5c,
-  0xac, 0x52, 0x2f, 0x31, 0x85, 0x4d, 0x13, 0x45, 0x07, 0x39, 0x94, 0x84,
-  0x26, 0x9d, 0xfc, 0x67, 0x18, 0x1c, 0xda, 0x52, 0x6b, 0x89, 0x5f, 0xa1,
-  0x52, 0x0a, 0x2f, 0x9c, 0xa9, 0x55, 0x5d, 0xff, 0x29, 0x60, 0x2b, 0x28,
-  0xf5, 0x2b, 0x60, 0x72, 0x9c, 0x22, 0xae, 0x6a, 0xe8, 0xa1, 0x82, 0xba,
-  0x2b, 0x6b, 0xfd, 0xc0, 0x3c, 0xca, 0x94, 0xa1, 0xf5, 0x73, 0xef, 0xec,
-  0x8f, 0x57, 0x28, 0xbd, 0xab, 0x0b, 0x3f, 0x33, 0x03, 0xf7, 0x78, 0x0c,
-  0xa1, 0x15, 0xc4, 0x31, 0x46, 0xd8, 0x82, 0x2e, 0x1f, 0x0d, 0x3b, 0x1b,
-  0x2c, 0xe3, 0x51, 0xf3, 0xf5, 0xdd, 0x76, 0xc5, 0xa3, 0xad, 0x68, 0xf9,
-  0x86, 0xe8, 0x0a, 0xa4, 0x0b, 0xe5, 0xa8, 0x1b, 0x35, 0xa2, 0x7b, 0xad,
-  0xad, 0x86, 0x95, 0x9a, 0x56, 0xe1, 0xd8, 0x12, 0xf3, 0xed, 0x4c, 0xf4,
-  0x61, 0x65, 0x36, 0x18, 0x15, 0xd3, 0xac, 0xb4, 0x41, 0xae, 0x49, 0x4f,
-  0x85, 0x37, 0x26, 0x43, 0x4d, 0x9e, 0x69, 0xf2, 0xfe, 0xf5, 0xd3, 0xbf,
-  0xc1, 0xe5, 0x81, 0xff, 0xf4, 0x03, 0x14, 0x20, 0x58, 0x66, 0x1d, 0x80,
-  0x00, 0x09, 0x48, 0x70, 0x60, 0x41, 0xb0, 0xbd, 0x55, 0x2a, 0xb1, 0x29,
-  0x7a, 0xbf, 0xcb, 0xbc, 0xab, 0xb4, 0x0e, 0x8a, 0x50, 0x94, 0x38, 0x0c,
-  0xf0, 0xb1, 0x7c, 0x32, 0xda, 0x00, 0x29, 0xad, 0x5e, 0x62, 0x4b, 0x75,
-  0x8e, 0x5d, 0x52, 0xf3, 0xb7, 0x03, 0xa2, 0x00, 0x0f, 0x4a, 0xa8, 0x5c,
-  0xaa, 0x1a, 0xac, 0x1b, 0x85, 0x27, 0x59, 0x3b, 0x8a, 0xaa, 0x00, 0x30,
-  0x21, 0x32, 0xae, 0x51, 0x4a, 0x06, 0x4b, 0xa8, 0xd0, 0xf4, 0x7d, 0xcf,
-  0xb9, 0x69, 0x87, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x25,
-  0x73, 0x83, 0x21, 0x48, 0x88, 0x46, 0x19, 0x85, 0x04, 0xe9, 0x67, 0x4d,
-  0x0b, 0x5a, 0xea, 0x4c, 0x80, 0x05, 0x59, 0x43, 0x4b, 0x60, 0xe0, 0xf6,
-  0x4e, 0xb8, 0xae, 0x89, 0xf8, 0x4e, 0x25, 0x51, 0x14, 0xec, 0x46, 0x9f,
-  0xb8, 0x77, 0xad, 0x94, 0x49, 0x03, 0xb0, 0x7e, 0xd1, 0xd7, 0x5d, 0x47,
-  0x28, 0x86, 0x93, 0xef, 0xd7, 0x0d, 0xb7, 0xa1, 0x74, 0x6c, 0x13, 0xce,
-  0x24, 0x27, 0xf6, 0xec, 0x45, 0x0e, 0xb0, 0x62, 0x51, 0x8f, 0xc1, 0x50,
-  0xab, 0xdc, 0x92, 0x06, 0x46, 0xc5, 0xe1, 0xf4, 0xad, 0x39, 0x6d, 0xe8,
-  0xd5, 0x28, 0x05, 0xf3, 0x49, 0xc1, 0x63, 0x0a, 0xfe, 0x23, 0x78, 0x26,
-  0x5b, 0x6f, 0xa5, 0xa8, 0x92, 0xec, 0x74, 0xe0, 0x41, 0xf6, 0xb5, 0x09,
-  0x23, 0x3c, 0xdd, 0x17, 0x99, 0xa3, 0xad, 0xd3, 0x75, 0x38, 0x5b, 0xe5,
-  0x79, 0xda, 0x0e, 0xcb, 0x1a, 0xcf, 0xeb, 0x9f, 0x24, 0x45, 0xa5, 0xc5,
-  0x45, 0xe9, 0x39, 0x64, 0x5c, 0xe5, 0x0d, 0xe4, 0x88, 0x3f, 0xe5, 0x00,
-  0x2c, 0xfb, 0x43, 0x15, 0xa8, 0x01, 0x8b, 0xe5, 0x4a, 0xe3, 0x76, 0xdf,
-  0x1f, 0xe4, 0xea, 0xdf, 0x81, 0x06, 0x67, 0x65, 0x85, 0x4e, 0x9f, 0xf0,
-  0xea, 0x36, 0x4a, 0x05, 0x96, 0x72, 0xf3, 0x49, 0x2e, 0xed, 0x3f, 0x36,
-  0xd0, 0xfe, 0xf5, 0x62, 0xb4, 0xef, 0x74, 0x7f, 0xb7, 0x4a, 0x92, 0x35,
-  0x47, 0xfb, 0x4e, 0x43, 0x91, 0xec, 0xa8, 0xf8, 0xdc, 0xb7, 0xd9, 0x6d,
-  0x3a, 0x37, 0x27, 0x7f, 0x78, 0x39, 0x60, 0x9a, 0x28, 0x96, 0x23, 0xd8,
-  0xb2, 0x96, 0x0a, 0x80, 0x28, 0x52, 0x70, 0x3e, 0x66, 0x2d, 0xd4, 0x64,
-  0xa2, 0xbe, 0xb6, 0xa8, 0x5a, 0xdd, 0x38, 0x55, 0x9b, 0xef, 0x49, 0x27,
-  0xed, 0xcd, 0xc9, 0x52, 0x82, 0x8d, 0x87, 0x13, 0x75, 0xd5, 0x4d, 0x17,
-  0x51, 0x26, 0x53, 0xe5, 0x3b, 0xd1, 0x2f, 0xd2, 0xbf, 0xfd, 0xbf, 0xdd,
-  0x3c, 0xd0, 0x7d, 0xfe, 0x8a, 0x10, 0x8a, 0x1d, 0x80, 0x09, 0x41, 0x31,
-  0x78, 0x89, 0x97, 0x61, 0x80, 0x02, 0x34, 0xdb, 0xb3, 0x43, 0x2a, 0x09,
-  0x58, 0x8c, 0xc9, 0x15, 0xad, 0x16, 0x31, 0x0f, 0x41, 0x68, 0x2a, 0x02,
-  0xba, 0x78, 0x55, 0xc2, 0x49, 0x54, 0x42, 0x3d, 0x2d, 0x76, 0xc5, 0xe6,
-  0x08, 0x2c, 0xb2, 0x71, 0x43, 0xb1, 0xfc, 0x77, 0xac, 0x6d, 0x7e, 0x4e,
-  0x11, 0x57, 0xc6, 0xd4, 0xdd, 0x28, 0x56, 0x92, 0x07, 0xec, 0xee, 0x8f,
-  0x0c, 0xe5, 0x02, 0x11, 0x9d, 0xce, 0x67, 0xdd, 0xcf, 0xaa, 0x00, 0x1c,
-  0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x19, 0x6f, 0xa2, 0xc1,
-  0x58, 0x64, 0x44, 0x33, 0x0d, 0x42, 0x07, 0x1b, 0x97, 0x66, 0x95, 0xc1,
-  0xab, 0xa9, 0x2a, 0x14, 0x00, 0x95, 0x03, 0x3f, 0xd4, 0xc8, 0xb1, 0x9c,
-  0x4a, 0x5b, 0x36, 0x66, 0x3c, 0x21, 0x09, 0xe8, 0xed, 0x9e, 0xac, 0xd8,
-  0xda, 0x9b, 0x45, 0xd9, 0x7e, 0x16, 0xce, 0x0c, 0xb2, 0x16, 0xc4, 0xca,
-  0x0b, 0x30, 0x19, 0x17, 0x34, 0xe5, 0xed, 0xdf, 0x7c, 0x76, 0x5f, 0x34,
-  0x66, 0xa8, 0x94, 0x01, 0x42, 0xe6, 0xa4, 0x6f, 0x9c, 0x2d, 0xfc, 0x8f,
-  0xe7, 0xa8, 0x13, 0x2b, 0x28, 0xc8, 0x72, 0x63, 0x69, 0xab, 0xb9, 0x0b,
-  0x24, 0xf6, 0x7d, 0x43, 0x21, 0x84, 0xae, 0x54, 0x35, 0x5c, 0xfc, 0xc0,
-  0x1a, 0x72, 0x59, 0x48, 0x8c, 0xd5, 0x36, 0x51, 0xd0, 0x9d, 0x4c, 0x4f,
-  0x5a, 0xda, 0xd5, 0x4b, 0x45, 0x75, 0x01, 0x11, 0x1a, 0x78, 0xed, 0xed,
-  0x3b, 0xa5, 0xab, 0x81, 0x4b, 0x62, 0xfc, 0x8a, 0x5b, 0xac, 0x45, 0x0f,
-  0x44, 0x3e, 0x36, 0x4a, 0x4c, 0x09, 0x08, 0xce, 0xf7, 0xea, 0x1c, 0x2b,
-  0xb3, 0x20, 0xa4, 0xc4, 0xe5, 0xb5, 0x6d, 0x8b, 0x2a, 0xac, 0xac, 0x25,
-  0x53, 0x3b, 0x2c, 0x9a, 0x88, 0xcd, 0xc0, 0x8f, 0xe3, 0x53, 0xf5, 0x93,
-  0x06, 0x3f, 0x64, 0xfd, 0x64, 0x45, 0xae, 0x7f, 0xd9, 0x36, 0xa3, 0xf1,
-  0x1d, 0xce, 0xa7, 0x3b, 0xa1, 0x65, 0x83, 0xf1, 0xc2, 0x40, 0xd0, 0xa7,
-  0x10, 0xe3, 0x6a, 0x80, 0x44, 0x75, 0x46, 0xca, 0x0f, 0x3e, 0x4a, 0x5b,
-  0x32, 0xa6, 0x62, 0x10, 0x65, 0x60, 0x47, 0x1c, 0x94, 0xcc, 0xa9, 0x43,
-  0x9b, 0xbe, 0xb1, 0xc6, 0x55, 0x09, 0x30, 0xfc, 0x3e, 0x2c, 0xdd, 0x94,
-  0x5f, 0xd5, 0x66, 0x38, 0x42, 0xd5, 0x43, 0xce, 0x02, 0xf7, 0xb8, 0x95,
-  0xce, 0xf4, 0xc1, 0x0c, 0x8f, 0x6b, 0x5e, 0x85, 0x3d, 0xb6, 0xc8, 0xb3,
-  0xd2, 0xad, 0x57, 0x84, 0xb9, 0x74, 0xf5, 0xf3, 0xfb, 0x38, 0xef, 0x7e,
-  0x00, 0x10, 0x1f, 0xc1, 0x4a, 0x09, 0x47, 0xb0, 0x00, 0x94, 0x2a, 0xe8,
-  0x25, 0x1d, 0x18, 0x01, 0xb0, 0x02, 0x32, 0xea, 0x9e, 0x77, 0xe1, 0xaf,
-  0x29, 0x3c, 0x42, 0x94, 0xb4, 0xd6, 0xb1, 0x01, 0x52, 0xe4, 0xb1, 0x2f,
-  0x16, 0x7b, 0x92, 0x66, 0x27, 0x28, 0x2b, 0x13, 0x82, 0xb5, 0x20, 0x50,
-  0xcd, 0xb9, 0x97, 0xaf, 0xdc, 0x0b, 0x63, 0xc4, 0xcf, 0x7b, 0x44, 0x7d,
-  0x75, 0x37, 0x7f, 0x72, 0xe6, 0xee, 0x25, 0xd8, 0xc5, 0x1b, 0xd6, 0xbe,
-  0x51, 0x0e, 0xce, 0xbb, 0x9e, 0x9d, 0x4d, 0x73, 0x98, 0x98, 0x80, 0x0e,
-  0x21, 0x1a, 0x88, 0x00, 0x00, 0x01, 0xff, 0xff, 0x15, 0x6f, 0xa2, 0x41,
-  0xc8, 0xc8, 0x11, 0x23, 0x05, 0x04, 0xa2, 0x00, 0x38, 0x3a, 0xde, 0x96,
-  0xbb, 0xa2, 0xea, 0x80, 0x15, 0x78, 0x98, 0x24, 0xf4, 0xe4, 0xea, 0x93,
-  0xf4, 0xdb, 0x1a, 0x11, 0x10, 0x9f, 0x2a, 0x86, 0x82, 0x81, 0xbc, 0xbc,
-  0x93, 0xf8, 0x8e, 0xcb, 0x10, 0x9d, 0xa3, 0x90, 0xc7, 0x44, 0x1b, 0xbb,
-  0x79, 0x8b, 0x38, 0x93, 0x18, 0x7b, 0x02, 0xd0, 0x0f, 0x50, 0x4f, 0x51,
-  0xc5, 0xcb, 0xa9, 0xe2, 0xfd, 0x97, 0x9a, 0x63, 0xaa, 0x45, 0x52, 0xf3,
-  0xa6, 0xe0, 0xd7, 0xaf, 0x0e, 0x96, 0x06, 0xed, 0x4f, 0xe9, 0x1f, 0x47,
-  0x86, 0x18, 0x29, 0xcf, 0xae, 0xe3, 0xb9, 0xcd, 0x17, 0xc7, 0xc1, 0x46,
-  0x47, 0x72, 0x15, 0x4e, 0xdd, 0x26, 0x71, 0x18, 0x49, 0x32, 0xac, 0xa5,
-  0xe6, 0x48, 0x68, 0xf8, 0x8c, 0xc5, 0xbb, 0x85, 0x8a, 0xf9, 0x7b, 0x2e,
-  0x0b, 0xca, 0xed, 0x62, 0x5a, 0xe2, 0x72, 0xa6, 0x56, 0x5d, 0xdd, 0x4c,
-  0xde, 0xbf, 0x1e, 0x5d, 0x84, 0xf4, 0xb3, 0x27, 0x1c, 0xa2, 0x05, 0x03,
-  0x94, 0x05, 0x16, 0xd3, 0x8b, 0x4c, 0x68, 0x83, 0xb9, 0x2a, 0x39, 0xeb,
-  0x84, 0xc8, 0xcb, 0x84, 0xbe, 0x4c, 0xe6, 0x59, 0x05, 0xed, 0xbb, 0xb3,
-  0x59, 0x5e, 0x7e, 0xe7, 0xdd, 0xf9, 0xd0, 0xde, 0x32, 0x55, 0x46, 0x48,
-  0xc1, 0x4d, 0x43, 0xf2, 0xe3, 0xe8, 0x65, 0x91, 0xb7, 0x4f, 0x4d, 0x27,
-  0x3a, 0x63, 0xd3, 0xc3, 0xd3, 0x0e, 0xa8, 0xc2, 0xea, 0x66, 0xa6, 0x6c,
-  0x9e, 0x5f, 0x6e, 0xc3, 0x82, 0x87, 0x23, 0x21, 0x64, 0x34, 0x14, 0x94,
-  0xf5, 0xba, 0xc9, 0x67, 0x4c, 0x36, 0x9c, 0xe4, 0x6b, 0xaf, 0x6e, 0xe9,
-  0x74, 0xf5, 0x22, 0x79, 0xc5, 0xfb, 0x40, 0x91, 0xca, 0xfd, 0x58, 0x9f,
-  0x8f, 0x45, 0xfa, 0x60, 0xd2, 0x7b, 0xab, 0x1b, 0xed, 0x80, 0x08, 0x0f,
-  0xd0, 0x08, 0x52, 0x81, 0x61, 0x98, 0x76, 0x00, 0x00, 0x42, 0x91, 0xc0,
-  0x22, 0x90, 0xec, 0x28, 0x23, 0x9a, 0x41, 0x79, 0xc0, 0x45, 0x3b, 0xcc,
-  0x21, 0x35, 0x99, 0x0b, 0x9c, 0x52, 0x0a, 0xd2, 0x36, 0xf7, 0xed, 0xcb,
-  0x1b, 0x82, 0x10, 0x4c, 0x92, 0x95, 0x7b, 0x26, 0x78, 0x44, 0xce, 0xcf,
-  0xb4, 0x16, 0x68, 0x6c, 0xaf, 0x94, 0x45, 0x68, 0x15, 0xa2, 0xe6, 0xa2,
-  0xcb, 0xd8, 0x1e, 0x64, 0xaf, 0x42, 0x54, 0x93, 0xe5, 0x54, 0x9d, 0xc2,
-  0x00, 0x75, 0x9e, 0xea, 0x92, 0x0f, 0xba, 0x77, 0xde, 0xc7, 0xf4, 0xe8,
-  0x87, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x17, 0x6f, 0xa4,
-  0xb0, 0x90, 0xe4, 0x44, 0x13, 0x08, 0x42, 0x01, 0x69, 0x7c, 0x0d, 0x33,
-  0x57, 0x51, 0x1c, 0xe8, 0x00, 0x87, 0x22, 0xd5, 0x0d, 0xa6, 0xf2, 0x09,
-  0x65, 0x4f, 0x18, 0x98, 0x20, 0x12, 0x8f, 0x02, 0xa2, 0x93, 0x59, 0x94,
-  0x82, 0x45, 0x90, 0x5d, 0xc6, 0xfc, 0x9d, 0xe5, 0xb5, 0x00, 0x26, 0x29,
-  0x50, 0x35, 0x4e, 0x17, 0xd8, 0x0f, 0x1e, 0xaf, 0x9b, 0x9b, 0xbf, 0xcf,
-  0x92, 0xb6, 0x66, 0x8e, 0xdd, 0x32, 0x35, 0xc9, 0x98, 0x2e, 0x3a, 0xb6,
-  0xaa, 0x8d, 0x24, 0x1d, 0xd1, 0x2e, 0x96, 0x22, 0xb5, 0xc6, 0x64, 0x70,
-  0xf9, 0x37, 0x9f, 0x8d, 0xe4, 0x0f, 0xb8, 0xa6, 0xaf, 0xf5, 0x89, 0x1e,
-  0x2e, 0x3a, 0xbe, 0x53, 0x56, 0x01, 0xd4, 0x80, 0x16, 0x09, 0x50, 0xca,
-  0xe9, 0x08, 0x3e, 0x04, 0xc2, 0x86, 0xd4, 0x80, 0x69, 0xe6, 0x88, 0xdd,
-  0xe0, 0x7b, 0xbc, 0x60, 0x2c, 0xe0, 0x8b, 0x39, 0x7b, 0xd0, 0xf1, 0xf4,
-  0x27, 0x19, 0x0c, 0xbd, 0x18, 0x9d, 0x38, 0x21, 0xda, 0xfd, 0xf4, 0xe2,
-  0xed, 0x0a, 0x74, 0x3a, 0x8c, 0x3d, 0x99, 0xe2, 0x5e, 0x72, 0xe0, 0x02,
-  0xc6, 0x53, 0x49, 0x56, 0x69, 0xaf, 0x2f, 0x34, 0xb3, 0x55, 0x95, 0x87,
-  0x56, 0x10, 0xe5, 0x50, 0x2f, 0x2a, 0x4a, 0x78, 0x74, 0x38, 0x5d, 0xdb,
-  0x47, 0x58, 0xac, 0xae, 0x79, 0x56, 0x1c, 0xc2, 0x6c, 0x6c, 0xa7, 0x4a,
-  0xe4, 0x53, 0xe8, 0x7d, 0x34, 0x55, 0x9a, 0x60, 0x82, 0xfe, 0x4c, 0x4f,
-  0x44, 0x39, 0x8c, 0xc0, 0x1d, 0x6b, 0xc6, 0x57, 0xcc, 0x63, 0x17, 0x0d,
-  0x19, 0xda, 0x67, 0xad, 0x06, 0x7e, 0x53, 0xe8, 0x50, 0x8b, 0xd1, 0xed,
-  0xf4, 0x40, 0x07, 0xa6, 0xd9, 0x30, 0x1b, 0xea, 0xd9, 0x28, 0xdc, 0x3c,
-  0x2a, 0x9e, 0xd4, 0x14, 0x53, 0xc2, 0x8b, 0xec, 0x1b, 0xae, 0xbb, 0x5f,
-  0x6f, 0x96, 0x10, 0x7f, 0xfe, 0x0a, 0x98, 0x66, 0x1d, 0x80, 0x05, 0x40,
-  0x2a, 0x2a, 0x4c, 0xea, 0xb0, 0x06, 0xa9, 0xc8, 0xfb, 0xab, 0xfd, 0x34,
-  0xe5, 0xd0, 0x91, 0xb2, 0x4a, 0xab, 0xd8, 0x5a, 0x08, 0xa7, 0x72, 0xf1,
-  0x20, 0x02, 0xc5, 0x5d, 0x05, 0xe0, 0xae, 0xd8, 0x5a, 0x23, 0x41, 0x61,
-  0x64, 0xb6, 0xca, 0x78, 0xff, 0xa6, 0x7a, 0xed, 0xa0, 0xb6, 0x4d, 0x5a,
-  0x77, 0x85, 0xd9, 0x77, 0xe4, 0xb9, 0xe3, 0x69, 0x0c, 0x5b, 0x44, 0xd4,
-  0x4e, 0x04, 0x95, 0x5c, 0x32, 0x9d, 0x0f, 0x78, 0xe4, 0x19, 0x08, 0xeb,
-  0xa6, 0xb5, 0x21, 0xf6, 0xdd, 0x12, 0x18, 0x43, 0x07, 0x21, 0x1a, 0x88,
-  0x00, 0x00, 0x00, 0x3f, 0xff, 0x1d, 0x6f, 0xa4, 0xb1, 0x10, 0xa2, 0x34,
-  0x13, 0x0c, 0xc2, 0x06, 0x92, 0x3a, 0x0e, 0x22, 0x59, 0x74, 0x28, 0x28,
-  0xb4, 0x36, 0x2a, 0x08, 0x64, 0x60, 0x82, 0x39, 0x75, 0x65, 0x73, 0xfc,
-  0x27, 0x2c, 0x56, 0xe1, 0xfe, 0xdd, 0x55, 0x0e, 0xe6, 0x7e, 0x6b, 0x47,
-  0x84, 0x76, 0x6d, 0x60, 0x39, 0xb4, 0x98, 0x07, 0xce, 0x17, 0x05, 0xc9,
-  0x19, 0xe8, 0xaf, 0xc5, 0x6e, 0x9d, 0x81, 0xec, 0x3c, 0xad, 0x96, 0x64,
-  0x7c, 0xe5, 0xc1, 0x9e, 0x75, 0x8c, 0xd9, 0xcc, 0xd6, 0xa9, 0x1f, 0x19,
-  0x68, 0x4c, 0xfb, 0x63, 0xa5, 0xd7, 0x33, 0x8d, 0x15, 0x7b, 0x98, 0xd0,
-  0xa4, 0xa8, 0x73, 0x67, 0xf6, 0x3f, 0x22, 0xa9, 0x35, 0x23, 0x37, 0xf7,
-  0x6a, 0x6f, 0xb4, 0xed, 0xd4, 0x28, 0x7c, 0x19, 0x1d, 0x33, 0xf6, 0x11,
-  0xe6, 0x0d, 0x8a, 0xdd, 0xdd, 0xab, 0x9c, 0x3a, 0xc9, 0xda, 0x94, 0x95,
-  0x6d, 0x53, 0xf5, 0xfb, 0x5a, 0x73, 0x9d, 0x48, 0x36, 0x5a, 0x2c, 0xf6,
-  0xfa, 0x0a, 0xee, 0x39, 0x9f, 0x14, 0x9d, 0xef, 0xd3, 0x80, 0x4f, 0x24,
-  0x39, 0xe3, 0x3c, 0x2a, 0xd5, 0x73, 0x95, 0x60, 0x08, 0x98, 0xaa, 0xdd,
-  0x78, 0x8b, 0x3d, 0x06, 0xaf, 0xae, 0x65, 0xb6, 0xa5, 0xf5, 0xce, 0x12,
-  0x8b, 0x07, 0xad, 0x4f, 0x08, 0x13, 0x74, 0x97, 0x71, 0x26, 0xf2, 0x55,
-  0x7a, 0xab, 0xab, 0x89, 0x75, 0x18, 0x00, 0x51, 0xe6, 0x72, 0x65, 0x77,
-  0x12, 0xbe, 0xb8, 0xd9, 0xdc, 0x66, 0x60, 0xe1, 0xa6, 0xf2, 0x3c, 0xed,
-  0xb9, 0x0c, 0x70, 0x0f, 0x8f, 0xfb, 0x1c, 0x48, 0x40, 0x25, 0x0e, 0x38,
-  0xfd, 0xbf, 0xcd, 0x4b, 0x41, 0x9d, 0x38, 0x96, 0x87, 0xec, 0xf9, 0x60,
-  0xe7, 0x34, 0xf4, 0x6f, 0x20, 0x35, 0x6b, 0x19, 0x9d, 0x83, 0x0b, 0x86,
-  0xfa, 0x2e, 0x2f, 0x6a, 0xcf, 0x4d, 0x1a, 0x7a, 0xb5, 0xdb, 0xb3, 0x8b,
-  0x5d, 0xd9, 0xa7, 0xfe, 0xa3, 0xb1, 0xf9, 0x4f, 0x57, 0x07, 0xdf, 0xe8,
-  0xa5, 0x86, 0x61, 0xd8, 0x00, 0x15, 0x02, 0xa5, 0x12, 0x2c, 0xc1, 0x2a,
-  0xc0, 0xe1, 0x5a, 0xa1, 0xf9, 0x1c, 0xba, 0xce, 0x97, 0x87, 0x00, 0x9d,
-  0x65, 0x3b, 0x0d, 0xca, 0xe1, 0x05, 0x13, 0xa0, 0x33, 0x31, 0x8b, 0x73,
-  0x30, 0x59, 0x1d, 0x3b, 0xa1, 0xeb, 0xf5, 0x53, 0x9f, 0x36, 0x3f, 0xad,
-  0xa6, 0xac, 0x75, 0xb4, 0xac, 0xcb, 0x3e, 0x79, 0x30, 0xe0, 0x18, 0xc4,
-  0xc9, 0x68, 0xdb, 0x7c, 0xc8, 0x15, 0x0f, 0x39, 0x9e, 0x46, 0x3b, 0x91,
-  0x2f, 0xac, 0xce, 0xcb, 0xfa, 0x68, 0xc5, 0xbb, 0x9f, 0x86, 0xfc, 0xe7,
-  0x20, 0x38, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x01, 0xff, 0xff, 0x17, 0x70,
-  0xa4, 0xa3, 0x18, 0x86, 0x10, 0x0b, 0x38, 0x68, 0xd5, 0x3a, 0x4a, 0x55,
-  0x58, 0x02, 0x96, 0x60, 0x24, 0x10, 0x12, 0x8c, 0xb2, 0x4a, 0x29, 0x2c,
-  0x1b, 0xc8, 0x21, 0x37, 0x7c, 0xbb, 0x42, 0x8e, 0x04, 0x3f, 0xd3, 0x7e,
-  0x93, 0x98, 0x6b, 0x40, 0x68, 0xfe, 0xf7, 0xd4, 0x3b, 0x9e, 0x8b, 0x2d,
-  0xd6, 0x79, 0x70, 0xbd, 0xc5, 0xe2, 0x7b, 0x67, 0xbd, 0x9b, 0xb6, 0xfc,
-  0x35, 0x97, 0x49, 0x46, 0x1c, 0xd5, 0xc7, 0xd0, 0xad, 0x99, 0x39, 0x72,
-  0x43, 0x2a, 0x24, 0x51, 0x2b, 0xed, 0xdd, 0x6e, 0x35, 0xe1, 0xc5, 0x7d,
-  0xbb, 0xab, 0x56, 0xab, 0xbb, 0x57, 0x93, 0x97, 0x6d, 0x0e, 0xaf, 0x9b,
-  0xc9, 0x7f, 0x2d, 0xa9, 0x8e, 0x8c, 0x5c, 0x3e, 0xbe, 0xe9, 0x62, 0xcb,
-  0x85, 0x6d, 0x22, 0x99, 0x2b, 0x3c, 0x89, 0x39, 0x94, 0x0d, 0x62, 0x54,
-  0x34, 0x6a, 0x58, 0x86, 0xbb, 0x4f, 0x7d, 0x87, 0x7c, 0x63, 0x65, 0x5f,
-  0x8c, 0x9c, 0x9f, 0x87, 0x3a, 0x6c, 0x28, 0x2d, 0xce, 0x3e, 0x50, 0xe7,
-  0x04, 0xe1, 0xd4, 0xcd, 0x40, 0x18, 0x80, 0x0d, 0xa1, 0x3e, 0x0e, 0x17,
-  0xed, 0x55, 0x23, 0xe3, 0xa5, 0x38, 0x0f, 0xad, 0x69, 0x57, 0xbe, 0x83,
-  0x68, 0x40, 0x96, 0xce, 0xb7, 0x06, 0x68, 0x3c, 0x72, 0x1e, 0x51, 0xf3,
-  0x91, 0x5a, 0xd1, 0x30, 0xfa, 0xe0, 0x2b, 0x3a, 0xc0, 0x8b, 0x0d, 0x76,
-  0xa1, 0x67, 0x51, 0x55, 0x7c, 0xbd, 0xe2, 0x78, 0x9c, 0x90, 0x65, 0x0b,
-  0x7a, 0xb5, 0x02, 0x30, 0x5e, 0xdb, 0x39, 0x95, 0xea, 0x30, 0x74, 0x21,
-  0xd9, 0xde, 0xb0, 0x03, 0x13, 0xd7, 0x18, 0xd1, 0x1c, 0xb9, 0x0d, 0xa0,
-  0x00, 0xac, 0x08, 0x12, 0x24, 0x91, 0x7e, 0x86, 0x3d, 0xed, 0x2c, 0x61,
-  0x3a, 0x62, 0x20, 0x0f, 0x12, 0x9b, 0xe7, 0x98, 0x9e, 0xc0, 0x36, 0xd7,
-  0x53, 0x7f, 0x93, 0x4c, 0x57, 0xdf, 0xf5, 0x8b, 0xac, 0x9b, 0x2a, 0xa8,
-  0x6a, 0x26, 0x92, 0x3c, 0x25, 0xff, 0xed, 0x3b, 0x6f, 0xd3, 0xff, 0xee,
-  0x07, 0xff, 0xd0, 0x90, 0x8e, 0xc4, 0x10, 0xec, 0x00, 0x2a, 0x52, 0x09,
-  0x45, 0x34, 0x04, 0x10, 0xec, 0x01, 0x55, 0xce, 0xa8, 0x21, 0x34, 0xa2,
-  0x00, 0x74, 0x82, 0xc1, 0x0a, 0x49, 0xc8, 0x44, 0x16, 0x7c, 0x12, 0xe2,
-  0x42, 0x18, 0xdc, 0x69, 0x70, 0x05, 0x82, 0xac, 0xea, 0x3a, 0x93, 0xd8,
-  0x1f, 0xd3, 0xd2, 0xfe, 0xa9, 0xa7, 0x6c, 0xd2, 0x33, 0xf1, 0x8a, 0x60,
-  0xb1, 0x60, 0x68, 0x08, 0x4d, 0x50, 0x35, 0x06, 0x77, 0x0d, 0xe3, 0x9c,
-  0xad, 0x34, 0x45, 0xb3, 0x33, 0x05, 0x34, 0x26, 0x2a, 0xea, 0x58, 0x4c,
-  0xe5, 0x5d, 0x0a, 0x77, 0x05, 0xee, 0xb3, 0x24, 0xbf, 0x8c, 0x64, 0x7d,
-  0x09, 0x2a, 0x6a, 0xb3, 0xcb, 0x3f, 0x5f, 0xcb, 0xdd, 0x00, 0xe0, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x00, 0x03, 0xff, 0x21, 0x73, 0x82, 0xa2, 0xc4,
-  0x28, 0x26, 0x1a, 0x84, 0x1c, 0x2c, 0xb7, 0x0a, 0x71, 0x52, 0xec, 0x52,
-  0x41, 0x18, 0x10, 0xc1, 0x5c, 0x8a, 0x66, 0x87, 0x94, 0x31, 0xf8, 0x6d,
-  0xbc, 0x18, 0xdc, 0x4c, 0x99, 0xc7, 0xdb, 0x5e, 0xf3, 0x6f, 0xe0, 0xa7,
-  0xf2, 0x69, 0x64, 0x18, 0xef, 0x2b, 0x0b, 0x43, 0x7e, 0x08, 0x9a, 0x19,
-  0xf9, 0x4d, 0xdd, 0xcd, 0x19, 0x2e, 0xe6, 0x81, 0xe2, 0xfa, 0xab, 0xb4,
-  0xfc, 0x0b, 0xc8, 0xba, 0xe2, 0x0f, 0x11, 0x3a, 0x5b, 0xfb, 0xcd, 0xa8,
-  0x53, 0xb3, 0x92, 0xdc, 0x5c, 0x68, 0xd5, 0x15, 0x7d, 0xc8, 0x1c, 0x89,
-  0x4e, 0xd3, 0x93, 0x73, 0x04, 0x49, 0x0b, 0x8e, 0x90, 0xd4, 0x07, 0xd2,
-  0xb9, 0x8a, 0xda, 0x60, 0x04, 0x16, 0x06, 0xee, 0x01, 0x54, 0xbe, 0xe6,
-  0x95, 0xab, 0x5d, 0x18, 0x34, 0x04, 0x99, 0x1d, 0xcc, 0xcb, 0x10, 0x7c,
-  0xdc, 0x6d, 0x8f, 0x83, 0xcf, 0x0d, 0x3c, 0x8c, 0x00, 0x46, 0xfa, 0x81,
-  0x35, 0x75, 0xc3, 0xf4, 0xc2, 0xa9, 0x06, 0x53, 0xd1, 0x17, 0x12, 0x47,
-  0x5b, 0x56, 0x0d, 0x94, 0xf3, 0x7e, 0x25, 0x4e, 0x4f, 0xf7, 0x30, 0x9e,
-  0xff, 0x9e, 0x0e, 0xf5, 0x5c, 0x3a, 0x87, 0x21, 0xba, 0xf6, 0xaa, 0x4b,
-  0x3a, 0xb8, 0x1e, 0xaf, 0x26, 0xa0, 0x03, 0x6b, 0x7e, 0xe9, 0x44, 0xfc,
-  0xf0, 0x16, 0x5d, 0x78, 0x83, 0x5b, 0x7d, 0x1b, 0x9d, 0xf1, 0xb4, 0x77,
-  0x02, 0x3e, 0xd0, 0x1a, 0xc4, 0x2b, 0x9a, 0x90, 0xe9, 0xde, 0xa5, 0xd0,
-  0x88, 0xd7, 0x9e, 0x2b, 0xf2, 0x96, 0xd8, 0x2b, 0xd5, 0x9a, 0x31, 0xc3,
-  0xf0, 0xb5, 0x11, 0xa8, 0x13, 0x4e, 0xad, 0x87, 0x91, 0xb3, 0x64, 0x6c,
-  0x0c, 0x6a, 0xa8, 0x0b, 0x59, 0x60, 0xd4, 0xc2, 0xac, 0x8b, 0x3a, 0x29,
-  0x58, 0x18, 0xda, 0x01, 0x21, 0x22, 0x57, 0x5d, 0xdb, 0x51, 0xa0, 0x5e,
-  0x73, 0xc7, 0x1c, 0x39, 0x77, 0xf6, 0x00, 0x1f, 0x7f, 0xe2, 0x11, 0xa0,
-  0xd8, 0x46, 0x1d, 0x80, 0x04, 0xa2, 0xa2, 0xa2, 0xae, 0xa5, 0x4a, 0x49,
-  0x14, 0x2d, 0x80, 0x84, 0xd5, 0x64, 0x8f, 0x1b, 0xc8, 0x4b, 0x85, 0xd7,
-  0xe1, 0x06, 0xec, 0x6c, 0x55, 0xd8, 0x90, 0x31, 0x1c, 0xec, 0xd4, 0x6f,
-  0x8e, 0xe2, 0xc4, 0x9b, 0xaf, 0x41, 0xa9, 0xcb, 0x2d, 0x94, 0xf9, 0xd4,
-  0xaa, 0x06, 0xdb, 0x96, 0x9e, 0x8f, 0x46, 0x54, 0xf2, 0xeb, 0xae, 0x63,
-  0x9e, 0x9d, 0xd2, 0x76, 0x61, 0x14, 0x1b, 0xe7, 0x23, 0xe3, 0xcd, 0xe4,
-  0x62, 0xf2, 0xe1, 0xc0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x7f, 0xff,
-  0x1f, 0x73, 0x83, 0x23, 0x58, 0x66, 0x10, 0x47, 0x1d, 0xd7, 0xe0, 0xea,
-  0x96, 0xab, 0xb5, 0x5b, 0x2c, 0x4a, 0xc8, 0x4a, 0xbc, 0x94, 0x35, 0xfe,
-  0x41, 0x1c, 0x98, 0x79, 0x3c, 0x59, 0x09, 0xa4, 0x4a, 0x4f, 0xda, 0xd8,
-  0xcb, 0xad, 0x87, 0xf6, 0x9b, 0xdb, 0xa7, 0xee, 0x19, 0x07, 0x98, 0x3e,
-  0xa1, 0x8e, 0xe6, 0x1c, 0xe2, 0xe7, 0xd7, 0xd7, 0x0f, 0xa6, 0x70, 0xdc,
-  0x47, 0xa8, 0xed, 0x61, 0x7b, 0x6c, 0x8d, 0x13, 0xa7, 0xf6, 0x26, 0x2f,
-  0xf9, 0xce, 0xa2, 0xf1, 0xa9, 0x30, 0x32, 0xcc, 0x49, 0xa6, 0x8f, 0x7d,
-  0x72, 0xfb, 0xfa, 0x6e, 0x8e, 0xbb, 0x0f, 0x55, 0x55, 0xf2, 0x04, 0x62,
-  0xab, 0xa1, 0xe9, 0xe7, 0xf2, 0xd7, 0xa1, 0xa8, 0x40, 0x9c, 0x27, 0x24,
-  0xe5, 0x5c, 0xd8, 0x12, 0xd7, 0xa0, 0x67, 0x91, 0xb9, 0xb0, 0x40, 0xbe,
-  0x4f, 0x44, 0x7e, 0x48, 0x49, 0xed, 0xbb, 0x9c, 0x73, 0x4d, 0xf1, 0x6d,
-  0x5a, 0xd2, 0x55, 0x65, 0x6f, 0xe9, 0x50, 0x46, 0xe2, 0x74, 0x57, 0xac,
-  0x9d, 0x1e, 0x32, 0x57, 0x87, 0x09, 0x67, 0x8d, 0x36, 0xfa, 0x93, 0x29,
-  0x36, 0xcf, 0x2b, 0xeb, 0xdb, 0x9f, 0xb3, 0xbe, 0xe6, 0x77, 0xc3, 0x74,
-  0x50, 0x40, 0x35, 0xe2, 0xb2, 0x94, 0xf8, 0x22, 0xd4, 0x69, 0xae, 0x37,
-  0xe4, 0x17, 0x4f, 0x4a, 0x4e, 0x25, 0xdd, 0x7d, 0x70, 0x13, 0xf8, 0xc1,
-  0xbf, 0x10, 0xf7, 0x1a, 0x2c, 0xc9, 0xa3, 0x5e, 0x83, 0x01, 0x67, 0x2f,
-  0xdb, 0x2d, 0x27, 0xb2, 0x74, 0x75, 0xf2, 0x5c, 0x00, 0x91, 0xf5, 0xcc,
-  0x71, 0xd3, 0x41, 0xf3, 0xe6, 0xe3, 0xb3, 0xb1, 0x51, 0x84, 0xe1, 0x36,
-  0x54, 0x0a, 0xe8, 0xdd, 0x72, 0xf4, 0x4d, 0x68, 0xd3, 0x52, 0xeb, 0x94,
-  0xb0, 0x74, 0x23, 0xb4, 0x28, 0x78, 0x91, 0xa2, 0x4c, 0x71, 0xb3, 0x09,
-  0x96, 0x89, 0xf7, 0xa0, 0x18, 0xca, 0x12, 0x35, 0xf6, 0x50, 0x96, 0x60,
-  0x73, 0x50, 0xb7, 0xdc, 0xf4, 0xbc, 0xf2, 0xd7, 0x33, 0xfd, 0xa8, 0xe5,
-  0x4f, 0xff, 0x27, 0x07, 0x42, 0xf2, 0xe0, 0xf9, 0xfc, 0x14, 0x21, 0x18,
-  0x2a, 0x1d, 0x80, 0x54, 0x0a, 0x95, 0x00, 0xbc, 0x80, 0x04, 0x40, 0x11,
-  0xf1, 0x00, 0x15, 0x32, 0x45, 0x62, 0x70, 0xb5, 0x08, 0xec, 0x94, 0xab,
-  0xfb, 0x42, 0x9d, 0xe7, 0x14, 0xee, 0xc1, 0x34, 0xf2, 0x5f, 0xc2, 0x9a,
-  0xbc, 0x2d, 0xde, 0x38, 0xc7, 0xac, 0x72, 0x97, 0xae, 0x67, 0x85, 0xb5,
-  0xe9, 0x15, 0xa1, 0x0f, 0xba, 0xc7, 0xa2, 0x71, 0xfe, 0xa5, 0xab, 0xf9,
-  0xf8, 0x15, 0x29, 0xba, 0x0f, 0x1d, 0x1e, 0x45, 0x7b, 0x3f, 0x85, 0x12,
-  0xe3, 0x4a, 0x4b, 0x10, 0xab, 0x94, 0xd8, 0xc6, 0x2d, 0xef, 0xe3, 0xac,
-  0x7d, 0xfd, 0x6e, 0x00, 0x1c, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x03, 0xff,
-  0xff, 0x21, 0x73, 0xa3, 0x23, 0x98, 0x46, 0x10, 0x34, 0xd3, 0x86, 0x6b,
-  0x4d, 0x1a, 0x52, 0xe6, 0x54, 0x08, 0xa4, 0x0c, 0x13, 0xe9, 0x48, 0x24,
-  0xf5, 0xa4, 0x7f, 0xd2, 0xe3, 0xd2, 0xfc, 0x86, 0xb6, 0xb3, 0x99, 0xfa,
-  0xec, 0x6d, 0x25, 0xbc, 0x2e, 0x5c, 0x71, 0xf6, 0x2d, 0x1f, 0x16, 0x4b,
-  0x45, 0x9d, 0xcf, 0xae, 0xa6, 0x73, 0x79, 0xc6, 0xb6, 0xc8, 0xf0, 0x0b,
-  0x2b, 0xb0, 0x39, 0x5d, 0xb3, 0x4b, 0xb9, 0x55, 0x1c, 0x98, 0xcc, 0xc1,
-  0xea, 0x12, 0xa3, 0xb5, 0x22, 0xde, 0xe6, 0xd0, 0xee, 0x26, 0xc4, 0xe3,
-  0x9c, 0x9a, 0x5c, 0xda, 0xf2, 0x6c, 0x56, 0xc7, 0x31, 0x1b, 0x0b, 0x2e,
-  0x78, 0x61, 0x50, 0x96, 0x35, 0xf3, 0xf6, 0x73, 0x76, 0xf0, 0x66, 0x04,
-  0xce, 0xf2, 0x51, 0x4d, 0x8c, 0x50, 0x6a, 0xaa, 0xae, 0x14, 0xc8, 0xcf,
-  0x54, 0x31, 0x7b, 0x06, 0x3d, 0x6d, 0x61, 0xec, 0x7f, 0x2a, 0x7d, 0x67,
-  0x0d, 0x95, 0x4e, 0x19, 0x84, 0x50, 0xb8, 0xd2, 0xb5, 0x8f, 0xf0, 0xb2,
-  0xd9, 0x4c, 0x24, 0x28, 0x7c, 0xd0, 0x10, 0xc3, 0x0a, 0x00, 0xf0, 0x85,
-  0x0a, 0x49, 0x52, 0xac, 0xf7, 0x3a, 0x23, 0xe7, 0x21, 0x18, 0x2b, 0x95,
-  0x92, 0xbd, 0x08, 0xa9, 0xd6, 0x49, 0x7d, 0x43, 0xf3, 0x3f, 0x4a, 0x24,
-  0x00, 0x16, 0x57, 0x8e, 0xcf, 0xc8, 0x14, 0x45, 0x15, 0xba, 0x17, 0x5f,
-  0x78, 0xd1, 0x1b, 0x63, 0x46, 0xe5, 0xd3, 0xc9, 0x6a, 0x60, 0x9d, 0xa3,
-  0xc2, 0x87, 0xeb, 0x2d, 0xa9, 0xc7, 0x9d, 0x83, 0x86, 0x86, 0xb5, 0xbe,
-  0x4c, 0x08, 0xd7, 0x75, 0xf4, 0x3c, 0x1e, 0x35, 0xfe, 0xdc, 0x47, 0x44,
-  0x44, 0x4c, 0x0e, 0x72, 0x9d, 0xc6, 0xe1, 0x8a, 0x5b, 0x8f, 0xde, 0x28,
-  0x69, 0x3f, 0x90, 0x74, 0x7f, 0x83, 0xd2, 0x28, 0x12, 0x52, 0x21, 0x95,
-  0xec, 0x75, 0xcd, 0xdf, 0x5c, 0xfc, 0x30, 0xb1, 0x2b, 0xdb, 0x45, 0xc4,
-  0x25, 0x62, 0x71, 0x1e, 0x85, 0xd2, 0xbf, 0x63, 0x96, 0x0f, 0xbf, 0x91,
-  0x5a, 0x09, 0x86, 0x61, 0xd8, 0x00, 0x14, 0x2d, 0x52, 0x80, 0x40, 0x55,
-  0xa6, 0xc3, 0xf7, 0x58, 0x0b, 0x6d, 0x29, 0x7a, 0x9a, 0xc1, 0xb9, 0x54,
-  0x50, 0x00, 0x13, 0x0b, 0x6f, 0xba, 0x10, 0xb7, 0x2e, 0x94, 0x5a, 0x70,
-  0xbc, 0xfc, 0xc3, 0xf5, 0x9a, 0x34, 0x89, 0x0d, 0xcf, 0x4c, 0x77, 0x16,
-  0xaf, 0x72, 0x99, 0x88, 0x5f, 0x37, 0x8b, 0x9a, 0x54, 0x0b, 0xa8, 0x98,
-  0xe0, 0xa0, 0x5c, 0x95, 0xd4, 0xcd, 0x12, 0xa2, 0x37, 0x09, 0x2b, 0x41,
-  0x76, 0xad, 0x2c, 0xae, 0x1c, 0x8b, 0x28, 0xca, 0x2c, 0xdd, 0x67, 0x09,
-  0x2a, 0xbb, 0x04, 0xe0, 0xbd, 0xf8, 0x75, 0xd9, 0xc5, 0xb9, 0xd0, 0xf4,
-  0x61, 0xc0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x03, 0xff, 0x19, 0x6f,
-  0xa4, 0xb0, 0xd1, 0x02, 0x14, 0x23, 0x04, 0xc2, 0x02, 0x53, 0xcc, 0x0d,
-  0x26, 0xa0, 0x80, 0x0c, 0x42, 0x4d, 0x8d, 0x78, 0x49, 0x6b, 0x94, 0x12,
-  0x42, 0xd9, 0x25, 0x39, 0x74, 0x1a, 0x49, 0x3c, 0x04, 0x63, 0x84, 0x98,
-  0xdd, 0xc5, 0xf9, 0x8e, 0xc5, 0x26, 0x84, 0xa0, 0x87, 0xfa, 0x1d, 0x27,
-  0xa4, 0xae, 0x3f, 0xd4, 0x1f, 0xc8, 0xf9, 0xfb, 0xef, 0x30, 0x5e, 0x0f,
-  0x55, 0x73, 0x4f, 0x30, 0x66, 0x49, 0x27, 0x74, 0x43, 0x99, 0x7e, 0x6d,
-  0x34, 0xd3, 0x4f, 0x50, 0x86, 0xa4, 0x33, 0x2e, 0x02, 0xc5, 0xa4, 0x98,
-  0xfa, 0xbb, 0x5d, 0x8e, 0x4e, 0xc4, 0xc9, 0x3f, 0x5a, 0xb9, 0x47, 0x17,
-  0xe7, 0x75, 0x56, 0xa6, 0xc0, 0x64, 0xae, 0x3d, 0x72, 0x89, 0x18, 0x0b,
-  0x32, 0xa2, 0x01, 0x36, 0x65, 0xee, 0xe3, 0x34, 0x17, 0x49, 0xaa, 0xb7,
-  0xf7, 0xc0, 0x5f, 0xa1, 0x84, 0x8f, 0x37, 0x1d, 0x77, 0x59, 0x48, 0xc6,
-  0x96, 0xa3, 0x08, 0x9c, 0xdf, 0xd3, 0xaf, 0x85, 0x57, 0x14, 0xb0, 0x94,
-  0x2e, 0x82, 0xce, 0x0c, 0x6a, 0xa2, 0x22, 0xd4, 0x47, 0x27, 0x3a, 0x5b,
-  0x1a, 0xe5, 0x97, 0x66, 0xa1, 0x5b, 0x06, 0x00, 0x28, 0xfd, 0xbb, 0x8e,
-  0x5c, 0x80, 0x30, 0x5a, 0x7b, 0xbd, 0xfd, 0x47, 0x2d, 0x09, 0x8a, 0xbf,
-  0x5d, 0xd1, 0x70, 0xd7, 0x6d, 0x53, 0x73, 0x2c, 0x15, 0xcc, 0xb9, 0x5a,
-  0x73, 0xc1, 0xf7, 0xbe, 0x2c, 0x16, 0x5f, 0x47, 0xdc, 0xbf, 0xc2, 0xdf,
-  0xa1, 0x79, 0x59, 0xd3, 0x3b, 0x09, 0x06, 0x20, 0x43, 0x8e, 0x19, 0xbd,
-  0xa0, 0x97, 0xa1, 0x23, 0x29, 0x50, 0xd7, 0xad, 0x96, 0x31, 0x64, 0xcd,
-  0x3f, 0x67, 0xe9, 0x12, 0x08, 0x60, 0x58, 0x03, 0x4c, 0x6b, 0x70, 0xc1,
-  0xde, 0xbe, 0x37, 0xa4, 0x32, 0x5e, 0xcc, 0x4d, 0x9b, 0x57, 0xd5, 0x13,
-  0xea, 0x37, 0xb8, 0x75, 0x17, 0x54, 0xb6, 0xf8, 0x75, 0x3f, 0xff, 0x57,
-  0x9e, 0x7b, 0x20, 0x7e, 0xff, 0x88, 0x44, 0x43, 0x40, 0xb0, 0x84, 0x3b,
-  0x00, 0x00, 0x94, 0x52, 0x02, 0x54, 0xa4, 0x08, 0xc0, 0x00, 0xb2, 0xb4,
-  0x50, 0x83, 0xa7, 0xb8, 0xc6, 0x88, 0x33, 0x46, 0xec, 0x48, 0x2d, 0x2f,
-  0xf2, 0xc1, 0x0f, 0xd5, 0xc3, 0x3d, 0x19, 0xa9, 0x77, 0x34, 0x0d, 0xc6,
-  0x26, 0x84, 0x24, 0x37, 0xc8, 0x0a, 0xc2, 0x93, 0x9d, 0xa3, 0x2d, 0x6a,
-  0x11, 0x35, 0x83, 0x12, 0x45, 0xaa, 0x94, 0x63, 0x97, 0xd3, 0x37, 0x5c,
-  0x21, 0xc0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x31, 0x72,
-  0x84, 0x23, 0x18, 0x6a, 0x16, 0xa2, 0xea, 0xfa, 0x3d, 0x91, 0x8e, 0x95,
-  0x72, 0x54, 0xd5, 0x05, 0x20, 0x08, 0x18, 0x9f, 0x3e, 0xe8, 0xad, 0x17,
-  0xef, 0x6d, 0xe0, 0x7c, 0xc4, 0x37, 0x25, 0x73, 0xcd, 0xcd, 0x9b, 0x23,
-  0x2b, 0xde, 0x76, 0x25, 0xcd, 0x2d, 0x1a, 0x62, 0xee, 0x3c, 0x2b, 0x1d,
-  0xe5, 0xbe, 0x35, 0xd2, 0x37, 0x41, 0x2d, 0x30, 0xee, 0x1e, 0xb6, 0xbd,
-  0x5f, 0x94, 0x9d, 0xf3, 0xb1, 0x38, 0x65, 0xfe, 0x73, 0xcb, 0x1e, 0xcb,
-  0x7a, 0xa6, 0x5b, 0x72, 0xc9, 0x65, 0x64, 0x7c, 0xf1, 0xa4, 0xf3, 0xe6,
-  0x15, 0x71, 0x28, 0xd2, 0xd1, 0xeb, 0xc7, 0x07, 0xdc, 0xac, 0x5d, 0xca,
-  0x74, 0x6d, 0xa1, 0x78, 0x3b, 0x99, 0xd3, 0x70, 0x57, 0x8f, 0x8c, 0x24,
-  0x6b, 0xc4, 0xc3, 0x47, 0xb1, 0xe2, 0xaf, 0x8d, 0x16, 0xb6, 0x5e, 0x46,
-  0xee, 0x5e, 0x7f, 0x95, 0x30, 0x74, 0x2c, 0x36, 0x4c, 0x1a, 0xe1, 0x37,
-  0xda, 0x04, 0xbc, 0x2d, 0x40, 0xfa, 0xcf, 0x42, 0x68, 0x56, 0xb4, 0x5c,
-  0x82, 0x96, 0x60, 0x4f, 0xef, 0x7d, 0x83, 0x28, 0xcb, 0xb2, 0x00, 0x2c,
-  0x0b, 0xf3, 0xe3, 0xc8, 0xb8, 0x5c, 0x35, 0xbd, 0x75, 0x6f, 0x6c, 0x09,
-  0x23, 0x45, 0xea, 0xa8, 0x05, 0x4e, 0x42, 0x0c, 0x99, 0x00, 0x5f, 0x21,
-  0x08, 0xaf, 0x8b, 0x97, 0x31, 0x84, 0x1b, 0xaf, 0xec, 0x36, 0x72, 0x1b,
-  0xef, 0xf7, 0x84, 0x4b, 0x41, 0x93, 0x88, 0x62, 0x8b, 0xc1, 0xef, 0xb1,
-  0x96, 0xdf, 0xc5, 0x77, 0x38, 0x6a, 0x0a, 0x3a, 0x75, 0xbb, 0xb8, 0x1f,
-  0x84, 0xb0, 0x61, 0x9d, 0x63, 0xb6, 0x43, 0x7f, 0x9e, 0x8b, 0xf5, 0xde,
-  0x12, 0xb2, 0x44, 0xa8, 0x87, 0x75, 0x25, 0x73, 0x8d, 0xae, 0x75, 0x4b,
-  0x70, 0x95, 0xcb, 0x1a, 0x56, 0xc5, 0x66, 0x0a, 0xb4, 0xd0, 0xba, 0x59,
-  0xe8, 0x9d, 0xac, 0x8e, 0x39, 0x59, 0x9c, 0xfd, 0x76, 0x8a, 0xd5, 0x36,
-  0x3b, 0x57, 0x3c, 0x7a, 0x37, 0xc7, 0xbf, 0xfd, 0x00, 0x0f, 0x9f, 0xf3,
-  0x20, 0x58, 0x6a, 0x1d, 0x80, 0x0a, 0x40, 0x02, 0x65, 0xd0, 0x8a, 0x95,
-  0x0a, 0x0a, 0x2f, 0x85, 0xbd, 0x48, 0x58, 0x15, 0x0a, 0xcc, 0xc8, 0x28,
-  0x89, 0xa7, 0x0e, 0x30, 0xd8, 0xd3, 0x5a, 0x5d, 0x77, 0x6d, 0x35, 0xd1,
-  0x49, 0x2d, 0x76, 0x35, 0x55, 0xb4, 0x06, 0xaa, 0x46, 0x04, 0x98, 0x96,
-  0x2b, 0x49, 0x97, 0xe5, 0xa4, 0x47, 0xdd, 0xf2, 0xe0, 0x01, 0xc0, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x71, 0xa4, 0x30, 0x50,
-  0x8c, 0x14, 0x33, 0x11, 0x02, 0x05, 0xeb, 0x6e, 0x9a, 0xb1, 0x1d, 0x15,
-  0xa3, 0x26, 0x58, 0x2a, 0xca, 0xbe, 0x45, 0x62, 0x62, 0x10, 0x25, 0x93,
-  0x38, 0xbb, 0x8a, 0x57, 0x6f, 0x9e, 0x13, 0x97, 0x37, 0xf1, 0x5d, 0x85,
-  0x90, 0x99, 0xaa, 0x30, 0x22, 0x70, 0x83, 0xaf, 0x96, 0xb4, 0xc6, 0xa3,
-  0xc6, 0x54, 0x84, 0x9b, 0xaa, 0x8d, 0x43, 0xde, 0xdc, 0x3a, 0x4a, 0xe4,
-  0xbe, 0xbd, 0x9c, 0x7a, 0xcb, 0xd8, 0x3c, 0xe2, 0xf0, 0x9b, 0x21, 0xfb,
-  0x42, 0x46, 0x93, 0x0d, 0x44, 0xbb, 0x83, 0x57, 0x02, 0xc6, 0xee, 0xb9,
-  0xe5, 0xc5, 0x8a, 0xd2, 0x7c, 0x31, 0x8b, 0x08, 0x38, 0xb7, 0xf9, 0x8d,
-  0x49, 0x37, 0x4f, 0xae, 0xab, 0x94, 0x62, 0xa8, 0xa0, 0x32, 0xec, 0x10,
-  0x6f, 0xf9, 0x2a, 0xc3, 0x47, 0xc1, 0x72, 0xee, 0x39, 0xa0, 0x3d, 0x54,
-  0xf2, 0x57, 0x00, 0x24, 0x30, 0x79, 0x29, 0x57, 0x36, 0x25, 0xd5, 0x3c,
-  0xc1, 0x42, 0xcd, 0x5a, 0x04, 0x9d, 0x66, 0xcd, 0x62, 0xda, 0x1d, 0x19,
-  0x92, 0x69, 0x5d, 0x40, 0xb6, 0x40, 0x90, 0x6d, 0x77, 0x9b, 0x4e, 0xab,
-  0x2c, 0x66, 0x38, 0xff, 0x05, 0x46, 0xad, 0xbe, 0x1a, 0x43, 0x6c, 0xd7,
-  0xf6, 0x8f, 0xeb, 0x8f, 0xc7, 0x47, 0x74, 0x31, 0x01, 0xd1, 0xda, 0x76,
-  0xb4, 0x7d, 0x06, 0x69, 0xb9, 0xe3, 0x9b, 0xa8, 0xb8, 0x94, 0x39, 0x40,
-  0x3f, 0x65, 0x49, 0x8d, 0x8d, 0x31, 0xd6, 0xd3, 0x36, 0xc8, 0x5f, 0xc9,
-  0xb6, 0xca, 0x82, 0x39, 0x4f, 0x59, 0x9d, 0x52, 0xb9, 0x8f, 0xca, 0xf7,
-  0x45, 0xde, 0x6f, 0x01, 0x50, 0xd5, 0x15, 0x41, 0x33, 0xae, 0x6e, 0x18,
-  0x01, 0x4a, 0x48, 0x17, 0x6f, 0x88, 0x1a, 0xbe, 0xbf, 0x78, 0xdb, 0xfc,
-  0x16, 0x1c, 0xf8, 0x3b, 0x38, 0x67, 0x7c, 0x47, 0x96, 0x89, 0x6e, 0x85,
-  0x09, 0x2d, 0x15, 0x92, 0x66, 0xc2, 0xe8, 0x7b, 0x62, 0x1d, 0xce, 0x61,
-  0x61, 0x39, 0x80, 0xaf, 0xa4, 0xa7, 0x29, 0x0b, 0x56, 0x2f, 0x17, 0x68,
-  0xc7, 0xc7, 0xfa, 0xf3, 0xdd, 0xdb, 0x6f, 0x7e, 0xf9, 0xf1, 0x1f, 0xad,
-  0x7d, 0xd1, 0xdd, 0xdd, 0xf3, 0xf6, 0x29, 0x41, 0x30, 0xd4, 0x3b, 0x00,
-  0x0a, 0x84, 0xc8, 0xa9, 0x51, 0x60, 0x08, 0x4a, 0x0a, 0x7d, 0xa3, 0x85,
-  0x32, 0x73, 0xfb, 0xd2, 0x15, 0xb5, 0x9d, 0x62, 0x25, 0x5c, 0x13, 0xc8,
-  0xfd, 0xda, 0xb8, 0x10, 0x8e, 0x4c, 0xd8, 0xe8, 0xad, 0xa1, 0x3a, 0xbc,
-  0x37, 0x6e, 0xc9, 0xc2, 0x08, 0x15, 0x26, 0xc7, 0x78, 0x4e, 0xde, 0x93,
-  0xd5, 0xa4, 0x84, 0x80, 0xd0, 0xba, 0x0c, 0xdf, 0xa9, 0xde, 0xb0, 0x15,
-  0xb2, 0xfd, 0xc6, 0x34, 0xd1, 0x43, 0xc4, 0x95, 0x89, 0xd2, 0x42, 0xb4,
-  0xab, 0x21, 0x55, 0x74, 0xf5, 0xe2, 0xce, 0x73, 0x6b, 0x8c, 0xa6, 0x00,
-  0x01, 0x98, 0xd2, 0xc1, 0x79, 0xe6, 0xa1, 0x4d, 0xfe, 0xff, 0x67, 0x46,
-  0x39, 0x70, 0xed, 0x80, 0x0e, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0xff,
-  0xff, 0x1d, 0x6c, 0xa6, 0x30, 0xd0, 0xe4, 0x34, 0x0b, 0x0c, 0xc2, 0x01,
-  0xa5, 0xdb, 0x8f, 0x57, 0xae, 0x1a, 0x45, 0xee, 0xf2, 0x12, 0x92, 0x89,
-  0x2a, 0x6c, 0x5a, 0x46, 0x24, 0xd1, 0x91, 0x4a, 0xec, 0x42, 0x65, 0x65,
-  0x50, 0xa8, 0x99, 0x42, 0x4d, 0x50, 0x78, 0x1b, 0xf7, 0xb5, 0x3e, 0xa3,
-  0xc5, 0x7a, 0xc2, 0xa3, 0x17, 0x44, 0x64, 0x00, 0xb9, 0x7f, 0x3f, 0x3d,
-  0x47, 0x7d, 0x93, 0x58, 0x0e, 0x97, 0xea, 0x28, 0xdf, 0x13, 0xff, 0xb5,
-  0x9f, 0xd4, 0xfe, 0x7e, 0x8b, 0xcf, 0x75, 0xac, 0xf5, 0xac, 0x97, 0xb0,
-  0x54, 0x09, 0x9b, 0x91, 0xb7, 0xcf, 0x74, 0xda, 0xfe, 0x23, 0x37, 0xd8,
-  0x7c, 0x6d, 0x9e, 0x1e, 0x05, 0xe6, 0x2f, 0x38, 0xd9, 0x59, 0x40, 0x26,
-  0x26, 0xb9, 0x03, 0x81, 0xa7, 0xd1, 0xc5, 0x25, 0x5f, 0x4e, 0x2a, 0xd0,
-  0x1d, 0xd8, 0x3c, 0x78, 0x5b, 0x70, 0xf8, 0xf9, 0xe9, 0xe8, 0x2c, 0xae,
-  0xe5, 0x6e, 0x49, 0x13, 0xed, 0xfd, 0x52, 0x9a, 0x41, 0xe0, 0xd0, 0xb5,
-  0xb6, 0xf2, 0x25, 0x82, 0x1c, 0x82, 0x83, 0x25, 0x98, 0x2a, 0xe7, 0x8d,
-  0x08, 0xd0, 0x24, 0x8c, 0x04, 0xa5, 0x74, 0x4a, 0x84, 0x6b, 0x82, 0x27,
-  0x84, 0xeb, 0x45, 0xb3, 0x79, 0x5c, 0x7e, 0x58, 0x5c, 0x5e, 0x2f, 0x1c,
-  0x45, 0x80, 0x35, 0x7e, 0x04, 0x31, 0xef, 0x67, 0x82, 0x30, 0xfc, 0xa5,
-  0xbc, 0xee, 0x2f, 0x47, 0x63, 0x43, 0x6d, 0x36, 0x4e, 0x59, 0xc0, 0x21,
-  0xe1, 0xba, 0x84, 0x75, 0x6c, 0xa8, 0x21, 0x21, 0x96, 0x24, 0x70, 0xde,
-  0x4f, 0xb8, 0x10, 0x84, 0x0c, 0x17, 0x87, 0x09, 0x82, 0xce, 0x99, 0xee,
-  0xeb, 0x21, 0x7b, 0xa5, 0xad, 0xa8, 0x69, 0x96, 0xf3, 0x3b, 0xe7, 0x79,
-  0x88, 0x78, 0xb4, 0x8c, 0xf3, 0x63, 0x34, 0xe0, 0xfb, 0x03, 0x73, 0x18,
-  0xa1, 0x5c, 0xd7, 0x15, 0xd2, 0xf7, 0x86, 0xbe, 0xfe, 0xde, 0x17, 0x11,
-  0x7d, 0x1e, 0x1c, 0xbe, 0x78, 0x3e, 0x7e, 0xc5, 0x28, 0x36, 0x12, 0x87,
-  0x60, 0x00, 0x54, 0x15, 0x15, 0x6c, 0x00, 0x04, 0x0d, 0x8b, 0xd8, 0x98,
-  0x0b, 0xa4, 0x29, 0x6a, 0x22, 0xaa, 0xe0, 0x05, 0x6d, 0xaf, 0x77, 0xbd,
-  0x19, 0xf6, 0xbc, 0xa5, 0x5d, 0x2d, 0xb6, 0x0c, 0x33, 0x9b, 0x16, 0xe7,
-  0x0a, 0xf4, 0x5d, 0xe7, 0xc2, 0x45, 0x04, 0xcd, 0x56, 0x3b, 0xd7, 0x03,
-  0x59, 0xc7, 0xfd, 0x9a, 0x74, 0x3f, 0x7d, 0x57, 0xf8, 0x80, 0x6e, 0x78,
-  0x4e, 0xc6, 0x33, 0x6f, 0x5c, 0x28, 0x5b, 0x25, 0xb7, 0x4c, 0xcd, 0x20,
-  0xce, 0x67, 0x7e, 0x52, 0x59, 0x14, 0xd3, 0x6e, 0xf8, 0x6d, 0xb9, 0xcf,
-  0x7f, 0xb7, 0xa7, 0x80, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x7f,
-  0xff, 0x21, 0x6f, 0xa3, 0xc0, 0xd8, 0x68, 0xa6, 0x1a, 0x84, 0x13, 0xc8,
-  0x27, 0x14, 0xe1, 0x5a, 0x8a, 0xd4, 0xa8, 0xc4, 0xc5, 0x84, 0x30, 0x6b,
-  0xf2, 0x61, 0x95, 0x83, 0xb6, 0xde, 0x25, 0x0c, 0x0c, 0xa8, 0xaa, 0x84,
-  0x4b, 0x1c, 0xdf, 0xcb, 0x38, 0x57, 0xf7, 0x2c, 0x22, 0x11, 0x87, 0xf4,
-  0x04, 0xd4, 0xae, 0xe2, 0xfb, 0x36, 0x00, 0x89, 0x69, 0x17, 0x85, 0x08,
-  0x1e, 0x49, 0x43, 0x98, 0xe7, 0xb9, 0x1b, 0xa7, 0xaf, 0xb8, 0xfa, 0x44,
-  0x6b, 0xc2, 0x35, 0xfe, 0x8c, 0xb0, 0xf8, 0x65, 0x00, 0xb8, 0x66, 0x6f,
-  0xd8, 0x7a, 0x05, 0xd3, 0xc6, 0x63, 0xdc, 0x3b, 0x84, 0x75, 0xaa, 0xa3,
-  0x5b, 0xe8, 0x5a, 0xe6, 0x35, 0xcf, 0xf4, 0xde, 0xd7, 0x1a, 0x81, 0x6f,
-  0x96, 0x4c, 0x9b, 0x19, 0x52, 0xc5, 0x3a, 0x9b, 0x0c, 0xf8, 0xd1, 0xa2,
-  0xc7, 0x0c, 0x2c, 0x95, 0xdc, 0x28, 0xaf, 0x65, 0x66, 0xe8, 0x34, 0x1e,
-  0x02, 0x9f, 0x6a, 0x0a, 0x60, 0x4e, 0x9e, 0x26, 0xb3, 0x2b, 0x67, 0x5c,
-  0x19, 0xb6, 0x02, 0x02, 0x4c, 0x68, 0xd3, 0x24, 0xf3, 0xc2, 0x88, 0x3a,
-  0x80, 0xbf, 0x0c, 0x63, 0x81, 0x09, 0x6f, 0xaa, 0xa2, 0x3a, 0xb4, 0x74,
-  0xed, 0x88, 0x0b, 0x02, 0x9b, 0x75, 0x5d, 0x2f, 0x37, 0x8f, 0x37, 0x01,
-  0x75, 0x2d, 0xeb, 0x5a, 0x86, 0xbb, 0xb8, 0xfe, 0x9b, 0x13, 0x66, 0xd2,
-  0x52, 0x04, 0x26, 0x6c, 0x9d, 0x2b, 0x2f, 0xa9, 0x03, 0xd1, 0xc7, 0x1b,
-  0x23, 0x78, 0x2b, 0x88, 0x49, 0x90, 0x02, 0x47, 0xbd, 0xdb, 0xcd, 0x5d,
-  0x3e, 0x21, 0xc4, 0x47, 0x90, 0x1f, 0x95, 0x91, 0x51, 0xa1, 0xcd, 0xf5,
-  0xc7, 0x80, 0x02, 0x05, 0x18, 0x6c, 0x11, 0xe7, 0x58, 0xc4, 0x0d, 0x07,
-  0xeb, 0x0b, 0xaf, 0x7e, 0x6f, 0xc7, 0x09, 0x95, 0x4a, 0xf3, 0x6b, 0x8c,
-  0x00, 0x09, 0x3b, 0xc6, 0x46, 0x50, 0x9d, 0xbd, 0x71, 0xd7, 0xbb, 0x8a,
-  0x09, 0x63, 0x67, 0x77, 0x50, 0xe3, 0xdf, 0x85, 0x25, 0x12, 0x74, 0x63,
-  0x1c, 0xfd, 0x9d, 0x95, 0x00, 0x0f, 0x9f, 0xc0, 0x28, 0x91, 0x02, 0xc3,
-  0x50, 0xec, 0xa9, 0x51, 0x28, 0xa8, 0x05, 0x49, 0x49, 0x80, 0x20, 0x39,
-  0x4c, 0xda, 0xc9, 0xb5, 0x6a, 0x94, 0x7f, 0x3a, 0x72, 0xbb, 0x8d, 0x6b,
-  0xca, 0x8c, 0x99, 0xa7, 0xe5, 0xc1, 0x32, 0x03, 0x7b, 0xd6, 0x8c, 0x3c,
-  0xce, 0x4d, 0x29, 0xec, 0x9e, 0x60, 0x15, 0x16, 0x64, 0xbe, 0x40, 0x02,
-  0x69, 0x45, 0xe7, 0x3c, 0x6c, 0xbb, 0x2c, 0x96, 0xc2, 0x17, 0x61, 0xea,
-  0xb5, 0x0f, 0x84, 0x91, 0x4d, 0xad, 0x37, 0x87, 0x73, 0x96, 0xf5, 0xec,
-  0x90, 0x0e, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1b, 0x6f,
-  0xa5, 0x30, 0x91, 0x6c, 0x33, 0x08, 0x05, 0xd9, 0xc1, 0x1a, 0x6a, 0x93,
-  0x54, 0x05, 0x10, 0x48, 0xc1, 0x58, 0x1b, 0xd7, 0xb2, 0x1d, 0x1c, 0x7a,
-  0xde, 0x92, 0x26, 0x41, 0x79, 0x15, 0x0f, 0x74, 0x80, 0x90, 0x49, 0x20,
-  0x98, 0x2e, 0xf5, 0xd7, 0x43, 0x95, 0x4d, 0xd4, 0x7f, 0xe8, 0xe3, 0xdc,
-  0xa0, 0x4d, 0xa8, 0x97, 0x51, 0xe7, 0x7f, 0xa4, 0xfa, 0x0f, 0xcf, 0xb8,
-  0xf0, 0x99, 0x1f, 0x9c, 0xa9, 0x5e, 0xf3, 0xed, 0x5d, 0xfc, 0xe8, 0xf9,
-  0x5c, 0xf5, 0xbf, 0x31, 0x29, 0x50, 0xd1, 0x5c, 0x27, 0x61, 0xff, 0x73,
-  0xcf, 0x82, 0xdb, 0xb3, 0x6e, 0x6d, 0x7f, 0xb1, 0x6d, 0x4f, 0x8a, 0xb2,
-  0x59, 0x0d, 0x53, 0xe2, 0x32, 0x4d, 0xcd, 0x98, 0xa3, 0xb8, 0xd8, 0xb6,
-  0x3d, 0x3a, 0xdb, 0x08, 0x30, 0xb2, 0x40, 0xaa, 0x04, 0xfc, 0x35, 0x9a,
-  0x16, 0xa1, 0x80, 0x46, 0xa3, 0x1b, 0xb7, 0xae, 0xa9, 0xd4, 0x5d, 0xe6,
-  0xd5, 0x54, 0xe9, 0x68, 0xf5, 0xe7, 0x99, 0x6a, 0xd4, 0xbc, 0x3f, 0xd1,
-  0xbc, 0x4c, 0x4d, 0x3e, 0xf8, 0x02, 0xe5, 0x97, 0x1f, 0xf3, 0x5d, 0x93,
-  0x98, 0x9d, 0x31, 0x6c, 0xa8, 0x31, 0x2a, 0xd7, 0x50, 0xd2, 0xeb, 0x38,
-  0x54, 0x75, 0x5e, 0xf6, 0xc1, 0x70, 0x54, 0x21, 0x92, 0x51, 0xe5, 0x66,
-  0xac, 0x63, 0x3e, 0x90, 0xb4, 0x86, 0x65, 0xeb, 0x43, 0xc5, 0xff, 0x7a,
-  0x0c, 0x37, 0x1c, 0xf3, 0xb7, 0x23, 0x2b, 0x57, 0x9d, 0x5f, 0x8b, 0x10,
-  0xa2, 0x41, 0x22, 0x00, 0x99, 0x0c, 0x62, 0x06, 0x3e, 0x5b, 0xe3, 0x34,
-  0xa0, 0x5a, 0xec, 0x38, 0x04, 0x61, 0x26, 0x7d, 0x9d, 0xfd, 0x65, 0xbc,
-  0x9b, 0x7f, 0x1c, 0x0f, 0x4c, 0x9f, 0xdf, 0x6a, 0xa0, 0x75, 0x06, 0x77,
-  0x33, 0xb7, 0x56, 0x64, 0xda, 0xaa, 0x5b, 0x64, 0xae, 0xfe, 0xf1, 0xae,
-  0xe7, 0x77, 0xbb, 0x0d, 0xe1, 0xa6, 0x27, 0xf2, 0x3c, 0xbe, 0x13, 0xec,
-  0xa3, 0xa7, 0xa6, 0xb2, 0x57, 0xfe, 0xba, 0xf7, 0xea, 0xea, 0xd9, 0xd5,
-  0x5d, 0xd5, 0x71, 0x5e, 0xfd, 0x79, 0x0f, 0xd3, 0x7c, 0x9f, 0xdc, 0x9f,
-  0x04, 0x1f, 0x7f, 0x62, 0x72, 0x1a, 0x09, 0x84, 0xa1, 0xe6, 0x0a, 0x89,
-  0x42, 0xa0, 0x15, 0x2a, 0x20, 0x08, 0x10, 0x0f, 0x0c, 0x40, 0x37, 0x2e,
-  0x00, 0xef, 0xa5, 0xea, 0xac, 0xa7, 0x22, 0xb5, 0x1a, 0x6b, 0xb6, 0x50,
-  0x8e, 0xeb, 0xe0, 0xd6, 0x2b, 0xa1, 0xa9, 0x82, 0x3b, 0x84, 0xb5, 0xa4,
-  0xc2, 0x60, 0x47, 0xf4, 0x71, 0x75, 0xee, 0x90, 0x7e, 0xff, 0xf0, 0x2e,
-  0x8c, 0xf1, 0x25, 0xfe, 0x30, 0x91, 0xb6, 0x9c, 0xb3, 0x66, 0xb8, 0x03,
-  0xaa, 0xa4, 0x93, 0x37, 0x77, 0x02, 0xda, 0x8d, 0x7b, 0xce, 0xab, 0x3b,
-  0x57, 0xdf, 0x24, 0xde, 0x14, 0xa8, 0x1e, 0xcb, 0xea, 0x9d, 0xf5, 0xd3,
-  0x9e, 0xaf, 0x9f, 0xa7, 0xbb, 0x80, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00,
-  0x00, 0xff, 0xff, 0x27, 0x6f, 0xa5, 0x30, 0x90, 0x6c, 0x14, 0x3b, 0x0d,
-  0x42, 0x04, 0xd5, 0xac, 0xf3, 0x97, 0x5a, 0x9c, 0x5f, 0x7a, 0x6a, 0x90,
-  0x52, 0x23, 0x7a, 0x60, 0x4a, 0x40, 0xca, 0xc6, 0xb9, 0x30, 0x50, 0x11,
-  0x2a, 0xbb, 0x3f, 0x3a, 0x97, 0xec, 0xde, 0x21, 0xcd, 0x35, 0x67, 0xe5,
-  0x66, 0x0e, 0x6b, 0xfa, 0x7c, 0xf5, 0xe0, 0xba, 0x22, 0x54, 0x1e, 0xec,
-  0xae, 0x46, 0x3d, 0x35, 0xd6, 0x49, 0xb0, 0x82, 0xf4, 0xac, 0x3a, 0x9b,
-  0xa7, 0x23, 0x89, 0x8a, 0xf9, 0xd1, 0x6d, 0xa9, 0xb2, 0x58, 0x2e, 0x88,
-  0xda, 0x11, 0xbb, 0x3b, 0xe6, 0xd7, 0x3d, 0x6e, 0xb8, 0x1f, 0xb5, 0xbe,
-  0xb5, 0xd7, 0x9a, 0x65, 0xb9, 0x0e, 0x87, 0x76, 0xfc, 0xda, 0xf3, 0x65,
-  0x7c, 0xad, 0x9a, 0xd1, 0xc3, 0xbc, 0x43, 0x3c, 0x47, 0x49, 0x0d, 0x20,
-  0xb6, 0x41, 0x79, 0x0e, 0xf4, 0x74, 0x91, 0x2c, 0xd5, 0x32, 0x99, 0xdb,
-  0x66, 0x95, 0x03, 0x47, 0x5a, 0x82, 0xa5, 0x83, 0x14, 0x74, 0x57, 0x0b,
-  0x3e, 0x68, 0x6b, 0x65, 0x41, 0xdc, 0x49, 0xc5, 0x7e, 0x94, 0x21, 0x63,
-  0x85, 0x42, 0x72, 0x9c, 0x95, 0x0c, 0xb6, 0xd0, 0x95, 0x41, 0x56, 0xf1,
-  0xd5, 0xe9, 0x73, 0x1e, 0x28, 0x77, 0xa0, 0xfe, 0x81, 0x2b, 0xe0, 0xba,
-  0x7f, 0x8d, 0x84, 0x77, 0x12, 0xda, 0x4d, 0x0e, 0xa8, 0x27, 0xa5, 0xa9,
-  0xa6, 0x01, 0x5c, 0xa9, 0x3d, 0xb3, 0x25, 0x47, 0x0a, 0x1b, 0xee, 0xf1,
-  0x5a, 0xcd, 0x4c, 0x23, 0x5c, 0x41, 0x44, 0x95, 0x63, 0x05, 0x38, 0xe7,
-  0x67, 0xcb, 0xb2, 0xc0, 0xe6, 0xbb, 0xbc, 0xbe, 0xff, 0x10, 0xb2, 0x7c,
-  0x7e, 0xdb, 0x85, 0x00, 0x36, 0xbd, 0xaf, 0x67, 0xad, 0xfe, 0x29, 0xfe,
-  0x25, 0xf6, 0x9d, 0x63, 0x77, 0x79, 0xe5, 0x7f, 0xba, 0x7d, 0x55, 0xf5,
-  0x0b, 0xd4, 0x43, 0xef, 0x57, 0x46, 0x21, 0x0d, 0x74, 0x9c, 0x69, 0xd1,
-  0x5d, 0x32, 0xaf, 0x3f, 0x24, 0xe5, 0x25, 0x37, 0x31, 0xe6, 0xcc, 0xb1,
-  0x74, 0x82, 0xd2, 0x67, 0x35, 0xd4, 0xc1, 0x9b, 0x5c, 0x68, 0xba, 0xdb,
-  0xef, 0x8b, 0x45, 0xd7, 0x8f, 0x47, 0x3f, 0x6e, 0x7a, 0xa0, 0x01, 0xfb,
-  0xf6, 0x2b, 0x61, 0xa8, 0x76, 0x54, 0xa8, 0x15, 0x2a, 0x29, 0x13, 0x25,
-  0x25, 0x00, 0x10, 0xa0, 0x00, 0xac, 0x15, 0x90, 0x02, 0x80, 0x85, 0x85,
-  0x05, 0x9e, 0x74, 0x0e, 0x02, 0x08, 0xa9, 0x18, 0xc5, 0x54, 0xd0, 0x84,
-  0x3f, 0x5d, 0xb5, 0x93, 0x1c, 0x59, 0xd4, 0x0f, 0xce, 0x3c, 0xde, 0x49,
-  0x78, 0x41, 0xc9, 0xe4, 0x59, 0xf3, 0xc0, 0x1a, 0x0d, 0x12, 0xdb, 0x8a,
-  0x4b, 0x96, 0x78, 0xb6, 0xb7, 0x10, 0xb9, 0x29, 0x7a, 0xd4, 0x11, 0x9a,
-  0xa1, 0x8a, 0x49, 0x6d, 0x40, 0x9e, 0xfb, 0x71, 0xee, 0xf8, 0x5f, 0x08,
-  0x00, 0xe0, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x23, 0x71,
-  0xa1, 0xc1, 0x52, 0x02, 0x10, 0x12, 0xe4, 0xb4, 0x9a, 0xb3, 0x49, 0x57,
-  0x29, 0x39, 0xe0, 0x32, 0xd4, 0x8e, 0x47, 0x8c, 0xe5, 0x76, 0x7c, 0xae,
-  0x3f, 0x2e, 0x3e, 0x49, 0x16, 0x12, 0xde, 0x25, 0x64, 0x4c, 0x43, 0xa7,
-  0xfe, 0xbf, 0x51, 0x1b, 0xa4, 0x36, 0xcd, 0x4b, 0xdd, 0xbc, 0x13, 0x47,
-  0xfe, 0xdf, 0x4c, 0xb1, 0x72, 0xc7, 0x1a, 0x53, 0x01, 0xd5, 0x46, 0x73,
-  0x04, 0x4d, 0xb1, 0x57, 0x67, 0x1d, 0x3e, 0x95, 0x6a, 0x8c, 0xb0, 0xba,
-  0x10, 0x98, 0xe5, 0xf9, 0x31, 0x22, 0x72, 0x71, 0x1a, 0x7b, 0xea, 0xf9,
-  0x9d, 0x68, 0xc7, 0xd6, 0xf1, 0xa7, 0xe3, 0xa9, 0xbb, 0x0f, 0x58, 0xb5,
-  0x0e, 0x6c, 0x02, 0x16, 0x33, 0xe0, 0x9c, 0x56, 0x94, 0xf9, 0xd1, 0x76,
-  0xca, 0x35, 0xd6, 0x6f, 0x69, 0x4b, 0xb3, 0x99, 0x83, 0x67, 0x2d, 0x9d,
-  0xfe, 0x1f, 0xe1, 0x93, 0x2b, 0xb2, 0x6b, 0x6c, 0xdd, 0x1b, 0x27, 0xcb,
-  0x98, 0xc2, 0x77, 0x7b, 0x91, 0xa4, 0x17, 0x45, 0x66, 0x3e, 0xef, 0xda,
-  0x6e, 0x41, 0xbf, 0xb9, 0x3c, 0x5b, 0x10, 0xd4, 0xb1, 0x65, 0x36, 0xb1,
-  0x0c, 0x03, 0x2a, 0x3c, 0x0e, 0xd7, 0x7b, 0x61, 0xc5, 0x7c, 0x48, 0x12,
-  0x2d, 0xf4, 0x7b, 0x17, 0xc5, 0x8c, 0xd2, 0x80, 0xdc, 0xd0, 0x50, 0x2f,
-  0x71, 0x5a, 0x6a, 0x3f, 0xd8, 0x21, 0xbf, 0x02, 0xf3, 0x7e, 0xf6, 0xf8,
-  0xbc, 0xc4, 0x05, 0xe3, 0xb5, 0xb2, 0x20, 0x16, 0xcf, 0x18, 0x59, 0x9c,
-  0xff, 0x0d, 0x76, 0x5c, 0x06, 0x67, 0x5c, 0xbb, 0x83, 0xd4, 0xd9, 0x89,
-  0x9b, 0x20, 0xfb, 0x28, 0xce, 0xb5, 0x2f, 0x89, 0xc0, 0x55, 0x23, 0xd7,
-  0x98, 0x76, 0x6b, 0x48, 0x2c, 0x3c, 0x34, 0xf5, 0x93, 0x86, 0x00, 0x77,
-  0x38, 0xf1, 0x0f, 0x96, 0xb7, 0x12, 0xb5, 0x96, 0x97, 0x83, 0x5d, 0x6d,
-  0x9a, 0x12, 0x5e, 0x84, 0xce, 0xb4, 0xf9, 0x9f, 0xff, 0x6f, 0xda, 0xf0,
-  0xc8, 0x0f, 0xff, 0xf3, 0x21, 0x14, 0x3b, 0x54, 0xa8, 0xa4, 0x54, 0x99,
-  0x28, 0x8a, 0x44, 0x94, 0xc8, 0x99, 0x73, 0x25, 0x58, 0x10, 0x3d, 0xaf,
-  0x06, 0x00, 0x92, 0x49, 0x66, 0x33, 0x98, 0x40, 0x9c, 0x13, 0x1f, 0x54,
-  0x34, 0xfd, 0x19, 0xb4, 0x63, 0x76, 0xfe, 0xd4, 0xe7, 0x42, 0xd3, 0x3f,
-  0x92, 0x03, 0xd3, 0x7a, 0xe9, 0x9f, 0x5e, 0x6b, 0x8c, 0x80, 0x70, 0x21,
-  0x1a, 0x88, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x70, 0xa2, 0x41, 0x58,
-  0x48, 0xd6, 0x08, 0x84, 0x00, 0xe3, 0x56, 0x44, 0xbb, 0xa8, 0x97, 0x50,
-  0x00, 0xa9, 0x53, 0xb1, 0x83, 0x22, 0xdd, 0x0e, 0x56, 0x35, 0x15, 0x58,
-  0x81, 0x55, 0xe7, 0x44, 0x98, 0x3f, 0x3f, 0xf9, 0x2a, 0xc8, 0x7f, 0xa2,
-  0x4c, 0x01, 0x99, 0x01, 0x9a, 0xff, 0x15, 0xe6, 0xbc, 0x6d, 0x20, 0x73,
-  0x02, 0x3c, 0xce, 0xd2, 0x34, 0xc1, 0xad, 0x34, 0x6f, 0xfb, 0x5d, 0x42,
-  0xeb, 0x5e, 0x65, 0xe4, 0xc6, 0xd4, 0xf1, 0xcf, 0xb3, 0xdd, 0xf2, 0xff,
-  0x97, 0x0b, 0x55, 0xec, 0xa8, 0x1b, 0xb3, 0xef, 0x4b, 0xf4, 0xad, 0x37,
-  0x51, 0x93, 0xa8, 0xf4, 0x1a, 0x5c, 0x53, 0x3a, 0x83, 0x0e, 0x56, 0xf1,
-  0x8e, 0x5e, 0xff, 0x22, 0xf0, 0xff, 0x5a, 0xd3, 0x50, 0x3d, 0x62, 0x8b,
-  0x0a, 0x33, 0x1a, 0x2e, 0x8a, 0x0f, 0xd4, 0x65, 0x46, 0x2b, 0x2a, 0xbf,
-  0x5c, 0x5f, 0xe3, 0x71, 0x74, 0x44, 0x59, 0x7b, 0x99, 0x41, 0x89, 0xa1,
-  0xc2, 0x06, 0x54, 0x14, 0x2d, 0xe1, 0x64, 0x87, 0xef, 0x48, 0x77, 0x48,
-  0x72, 0x92, 0xb0, 0x31, 0xbb, 0x8d, 0xa9, 0x20, 0x06, 0xb4, 0x92, 0x6d,
-  0x1d, 0x1f, 0x2c, 0x06, 0xd6, 0x3c, 0x43, 0x3d, 0xf3, 0x88, 0xfe, 0x10,
-  0x8d, 0x6c, 0xc8, 0xc1, 0x26, 0xac, 0xb8, 0x45, 0x1f, 0x15, 0x42, 0x28,
-  0x3f, 0x0f, 0x1a, 0xb5, 0xf1, 0xe1, 0x0b, 0xe8, 0xbe, 0x48, 0x86, 0x02,
-  0x76, 0x6f, 0xcb, 0x7c, 0x2f, 0xd5, 0xf1, 0xad, 0xbb, 0xa1, 0x00, 0x7d,
-  0x9a, 0x14, 0x6d, 0xc1, 0x4a, 0xef, 0xa7, 0x7b, 0xc8, 0x68, 0x97, 0x1f,
-  0x45, 0xa1, 0x44, 0x6c, 0x05, 0x3e, 0xd6, 0x2b, 0xe7, 0x44, 0x8d, 0x57,
-  0xd3, 0x1f, 0x6c, 0x6f, 0xb5, 0x13, 0xa3, 0xb5, 0xcf, 0xe4, 0x4f, 0x1c,
-  0x3c, 0x18, 0x38, 0x73, 0x48, 0x3b, 0xa1, 0x22, 0xc2, 0x74, 0xf7, 0x40,
-  0xd0, 0x5d, 0xdd, 0x6f, 0xb1, 0xcc, 0x8b, 0xa8, 0xd2, 0xd6, 0x7a, 0x1d,
-  0x1d, 0x92, 0x5c, 0x4c, 0x56, 0xc1, 0x3e, 0x4d, 0x3c, 0x5b, 0x5c, 0x9f,
-  0x5f, 0xcf, 0x6d, 0xc1, 0xfb, 0xfe, 0x74, 0x26, 0xd1, 0x55, 0x2a, 0x2a,
-  0x51, 0x00, 0x10, 0xa1, 0x51, 0x52, 0x42, 0x81, 0x6a, 0xe3, 0xb7, 0xfe,
-  0x31, 0xf7, 0x01, 0x3f, 0x22, 0x20, 0x60, 0x52, 0x99, 0x70, 0x06, 0x80,
-  0x43, 0xad, 0x6a, 0x3d, 0x37, 0x1e, 0xc2, 0x45, 0x17, 0x0a, 0x69, 0xdd,
-  0xdf, 0x07, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x07, 0xff, 0xff, 0x2b, 0x6f,
-  0xa5, 0x30, 0x50, 0x8c, 0x54, 0x1b, 0x0d, 0x42, 0x0e, 0x35, 0x6d, 0x2d,
-  0x69, 0x75, 0xa9, 0x63, 0x55, 0x42, 0x03, 0x2d, 0x57, 0x43, 0xbf, 0x70,
-  0x62, 0x52, 0xb5, 0x92, 0xbe, 0x83, 0xf4, 0xb0, 0x49, 0x50, 0x3c, 0xa9,
-  0x70, 0xa1, 0xd8, 0x35, 0xed, 0xcb, 0xdb, 0xfe, 0x05, 0xe3, 0x3e, 0x10,
-  0x99, 0x0b, 0x32, 0x0f, 0x4b, 0xfe, 0x8f, 0xd2, 0x78, 0xc7, 0x48, 0xe1,
-  0x4c, 0x5f, 0x17, 0xaa, 0x79, 0x97, 0x83, 0xc6, 0x9d, 0x31, 0xa4, 0xa3,
-  0x2a, 0x5b, 0xb9, 0xe5, 0xc4, 0xc8, 0x2e, 0x70, 0x52, 0xd9, 0x0e, 0xcd,
-  0x27, 0xfe, 0x3e, 0x48, 0x67, 0x7a, 0x6d, 0x62, 0xc2, 0xbf, 0x45, 0xea,
-  0x12, 0x0b, 0xe3, 0x74, 0x50, 0xd7, 0x3f, 0x43, 0x03, 0x0f, 0x38, 0x41,
-  0xb7, 0x9d, 0x1e, 0x9a, 0x10, 0x88, 0x4d, 0xfb, 0x95, 0x34, 0xae, 0x42,
-  0x41, 0x48, 0x67, 0x3c, 0x5d, 0x28, 0x1d, 0x4f, 0x3e, 0xc5, 0x70, 0xb0,
-  0xf7, 0x2b, 0xa9, 0x66, 0x1d, 0xaa, 0x12, 0x02, 0xbc, 0x64, 0xeb, 0x6b,
-  0x64, 0x49, 0x92, 0x46, 0x13, 0x4d, 0x65, 0xdc, 0x52, 0x9d, 0x89, 0x4c,
-  0x4b, 0x56, 0x33, 0x84, 0x83, 0x13, 0x89, 0xe2, 0x40, 0x59, 0x4a, 0x47,
-  0xbd, 0xd0, 0xd8, 0x79, 0xe9, 0xe9, 0xfb, 0xf7, 0xc0, 0x76, 0xeb, 0xb6,
-  0x0b, 0xac, 0x22, 0x80, 0xdd, 0x74, 0xae, 0x54, 0x46, 0x43, 0x73, 0x23,
-  0xd4, 0xc2, 0x0c, 0x09, 0x7c, 0x91, 0x51, 0x33, 0x83, 0x9e, 0x1c, 0x54,
-  0x86, 0x53, 0x29, 0x6e, 0x46, 0xbd, 0x09, 0x4a, 0x44, 0x66, 0x96, 0xb0,
-  0xc1, 0xc6, 0x2d, 0x3a, 0xd9, 0xe0, 0xb8, 0x2c, 0x01, 0xa3, 0x05, 0x86,
-  0x10, 0xf1, 0x7a, 0x5b, 0x4d, 0xf2, 0xe4, 0x51, 0x7d, 0x7b, 0x4c, 0x36,
-  0xe0, 0x66, 0x7b, 0xb2, 0x38, 0x10, 0xfc, 0x19, 0xde, 0x5a, 0x66, 0x8f,
-  0xa6, 0x44, 0x52, 0xe8, 0x70, 0x9e, 0x74, 0xae, 0xeb, 0x9c, 0xb9, 0x00,
-  0x03, 0xb9, 0x45, 0x62, 0x6d, 0x20, 0xa9, 0x09, 0x5f, 0x3e, 0x1d, 0xff,
-  0x5c, 0x67, 0x2d, 0x58, 0xcf, 0x94, 0xb3, 0x5f, 0x7f, 0xb7, 0xed, 0xf4,
-  0xc7, 0xd1, 0xdb, 0xdb, 0xe0, 0x00, 0xfb, 0xf8, 0x36, 0x10, 0x98, 0x66,
-  0x1d, 0x94, 0x84, 0xa5, 0x45, 0x5e, 0x42, 0x92, 0x80, 0x40, 0x22, 0x6c,
-  0x6d, 0x4f, 0x14, 0x90, 0x9d, 0x61, 0x2d, 0x80, 0x12, 0x82, 0x84, 0x02,
-  0xf6, 0x43, 0x54, 0xbc, 0x34, 0xef, 0x66, 0x38, 0xf3, 0xed, 0x0d, 0x8c,
-  0x4e, 0xae, 0xf4, 0x68, 0x46, 0xd8, 0x36, 0x2f, 0xa9, 0x39, 0xd7, 0x76,
-  0xb5, 0xb7, 0xb4, 0xb6, 0x3f, 0xdc, 0x5b, 0x6e, 0x53, 0x74, 0xec, 0x64,
-  0xb8, 0x51, 0xe9, 0x85, 0xa7, 0x3a, 0xac, 0x45, 0xd5, 0x72, 0x78, 0x29,
-  0xde, 0x7f, 0x79, 0x4c, 0x8e, 0x44, 0xba, 0x5b, 0x85, 0x4e, 0x66, 0xe6,
-  0x60, 0xd6, 0x90, 0x27, 0x1a, 0x6a, 0xa8, 0xb7, 0x64, 0xc8, 0xd3, 0x65,
-  0xca, 0x89, 0x4f, 0x83, 0xcb, 0x5e, 0x76, 0x4c, 0x52, 0x69, 0xfb, 0x8f,
-  0x9f, 0xf4, 0xde, 0x48, 0x70, 0x21, 0x1a, 0x88, 0x00, 0x00, 0x00, 0x03,
-  0xff, 0x23, 0x6f, 0xa5, 0x22, 0x08, 0x68, 0x60, 0x43, 0xac, 0xb7, 0x01,
-  0xc2, 0xd1, 0x2e, 0x92, 0x91, 0x41, 0x29, 0x30, 0x5a, 0x4a, 0x9d, 0xd7,
-  0x76, 0x28, 0x91, 0xd3, 0x5c, 0x43, 0xc7, 0xa7, 0x9f, 0x4f, 0x58, 0x1a,
-  0xd4, 0x46, 0xf7, 0x9e, 0xed, 0x22, 0x63, 0xa8, 0xf3, 0xa4, 0x34, 0x27,
-  0x4d, 0xd8, 0x1c, 0x89, 0xcc, 0x5a, 0x86, 0x8e, 0xed, 0xde, 0xb2, 0x85,
-  0x57, 0xd5, 0x4c, 0x73, 0x46, 0xf4, 0x1c, 0xf6, 0xef, 0x82, 0x66, 0xbd,
-  0xa9, 0x64, 0x6c, 0xfa, 0x24, 0x9b, 0x63, 0x76, 0xff, 0x2a, 0xbf, 0xda,
-  0x6a, 0x0f, 0xb6, 0xb2, 0x5d, 0x48, 0xdf, 0x0d, 0xd4, 0x69, 0xe4, 0xb8,
-  0xdb, 0x69, 0x8e, 0x0a, 0x33, 0x7a, 0xa8, 0x8d, 0x5e, 0x75, 0xa7, 0x01,
-  0x81, 0xca, 0x4c, 0x55, 0xd3, 0xa5, 0x90, 0x65, 0x5a, 0xd3, 0x0d, 0x54,
-  0x85, 0x62, 0xc1, 0x33, 0xd0, 0x98, 0x89, 0xfc, 0x31, 0x86, 0x60, 0x31,
-  0x5c, 0x59, 0x2e, 0x1a, 0x42, 0x18, 0x2c, 0x03, 0x9a, 0xcc, 0x18, 0x7d,
-  0x7f, 0xf5, 0xbe, 0x65, 0x8f, 0xa2, 0x93, 0x49, 0xe8, 0xb6, 0x15, 0xea,
-  0xe7, 0xb5, 0x8b, 0x7e, 0x41, 0x3f, 0x14, 0xf4, 0x74, 0xc0, 0x7f, 0x95,
-  0x1a, 0x0c, 0xbc, 0xc1, 0x3c, 0xe0, 0x8d, 0x4b, 0x67, 0x40, 0x98, 0x2b,
-  0x96, 0xe8, 0x73, 0x0a, 0x06, 0xec, 0x02, 0xfb, 0x9b, 0x87, 0x04, 0x06,
-  0xfd, 0x53, 0x4b, 0xf8, 0x5f, 0x07, 0xdd, 0xd9, 0xbb, 0x49, 0xea, 0x85,
-  0xa0, 0xf0, 0xfe, 0x1d, 0x03, 0xa6, 0xe8, 0x09, 0xa3, 0x25, 0x77, 0x19,
-  0xf1, 0xa0, 0xfe, 0x81, 0x80, 0x83, 0x5e, 0x24, 0x71, 0xd9, 0x62, 0xe5,
-  0x20, 0x2a, 0xe8, 0xce, 0x8f, 0xf4, 0x8a, 0xf8, 0x66, 0xf7, 0xd1, 0xeb,
-  0x62, 0xd4, 0x0a, 0xe5, 0xe3, 0x7d, 0x8a, 0xad, 0xea, 0xaa, 0x1d, 0xa5,
-  0x41, 0xc1, 0xa2, 0x3b, 0xc8, 0x37, 0x8a, 0x57, 0x10, 0x1d, 0x77, 0x77,
-  0x7e, 0xff, 0x88, 0x48, 0x82, 0x61, 0x18, 0x79, 0xba, 0x04, 0x55, 0x25,
-  0x25, 0x42, 0xa2, 0x55, 0x44, 0xa5, 0x81, 0x01, 0x3f, 0xfd, 0x5d, 0x52,
-  0xc9, 0x05, 0xef, 0x35, 0x9d, 0x28, 0x8c, 0xb9, 0xa2, 0xa8, 0x02, 0xaa,
-  0x63, 0x21, 0xaa, 0xa5, 0xa2, 0xad, 0x0a, 0x9e, 0x0e, 0x3a, 0x9e, 0x61,
-  0x22, 0x95, 0x45, 0x86, 0xf3, 0xdc, 0x15, 0xb2, 0x03, 0x99, 0x9d, 0x84,
-  0x04, 0x74, 0xd5, 0x41, 0x0d, 0xa9, 0xe5, 0x36, 0x3d, 0x9e, 0xd0, 0x70,
-  0x21, 0x1a, 0x8f, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0x19, 0x6f, 0xa4, 0xb1,
-  0x11, 0x6c, 0x25, 0x08, 0x15, 0x5d, 0x25, 0xca, 0xab, 0x5d, 0x71, 0x65,
-  0xa8, 0x22, 0x81, 0x57, 0x54, 0x28, 0x95, 0x77, 0x89, 0x25, 0x28, 0x92,
-  0x46, 0x4c, 0x24, 0x20, 0x91, 0x5d, 0x83, 0xe3, 0xb3, 0xbe, 0xbb, 0x65,
-  0xd6, 0x61, 0xfb, 0x5f, 0x66, 0x51, 0x9e, 0xe7, 0x4d, 0x3f, 0xa5, 0xc0,
-  0x7f, 0x6e, 0x90, 0xca, 0x81, 0x8d, 0x3e, 0xe1, 0x61, 0xe1, 0x1c, 0xa9,
-  0x0d, 0xf5, 0x08, 0x96, 0x84, 0xfa, 0xd7, 0x17, 0x69, 0x2c, 0x27, 0x78,
-  0xba, 0xe3, 0x9a, 0xc0, 0xcf, 0xfc, 0xe7, 0x21, 0xba, 0xf5, 0x9e, 0x34,
-  0xf5, 0xb5, 0xf1, 0x96, 0xb3, 0x9f, 0x6e, 0x08, 0xa9, 0x38, 0x41, 0x7a,
-  0x83, 0xf5, 0x2a, 0xd8, 0x49, 0xf2, 0xf2, 0x55, 0x52, 0xab, 0xea, 0x5f,
-  0x95, 0x31, 0x5f, 0x4e, 0x5d, 0x36, 0x32, 0xbd, 0x8f, 0xae, 0x45, 0x38,
-  0x75, 0xc9, 0x56, 0xa1, 0xb4, 0x52, 0xce, 0x47, 0xc1, 0x57, 0xdc, 0xa7,
-  0xb3, 0x60, 0x1e, 0x1b, 0xc5, 0x0b, 0x96, 0xfd, 0x05, 0xa2, 0xcc, 0xe5,
-  0xdf, 0x7f, 0x3a, 0x58, 0xb3, 0x17, 0x87, 0x57, 0x59, 0x61, 0xd1, 0x29,
-  0x70, 0x24, 0xb5, 0x81, 0xc0, 0x41, 0x55, 0xc8, 0x3a, 0xd6, 0xb2, 0x0f,
-  0x37, 0xf3, 0xa1, 0xe0, 0x77, 0xe0, 0x09, 0x53, 0x85, 0xb9, 0x1e, 0xe2,
-  0xbf, 0x37, 0xe7, 0x64, 0x6a, 0xea, 0x3e, 0x3d, 0xef, 0xfd, 0x1c, 0x13,
-  0xe8, 0x22, 0xc2, 0xf4, 0x07, 0x41, 0xd4, 0xba, 0xe7, 0x59, 0x54, 0x5b,
-  0x74, 0xd5, 0x08, 0x6c, 0xbd, 0x56, 0x59, 0x67, 0xa9, 0x67, 0xa9, 0x31,
-  0x81, 0xde, 0xfc, 0x21, 0x45, 0x75, 0x58, 0xb5, 0x63, 0x7e, 0xe8, 0x8e,
-  0xc2, 0x3e, 0x32, 0x53, 0x17, 0x10, 0xae, 0xda, 0xdf, 0x1b, 0xf6, 0x3c,
-  0x76, 0xa7, 0x50, 0x5f, 0xd5, 0xa0, 0x3b, 0x82, 0x4e, 0xf6, 0xfa, 0x7b,
-  0x4e, 0x13, 0xa9, 0xcd, 0x75, 0xd4, 0x51, 0x8b, 0x03, 0xec, 0xc2, 0x52,
-  0x40, 0x76, 0x14, 0xa6, 0x50, 0xa5, 0x02, 0xfb, 0x51, 0x3a, 0x7d, 0x5f,
-  0x3c, 0xf7, 0xec, 0x00, 0x8c, 0xb2, 0x52, 0xbc, 0xc8, 0x53, 0x73, 0x08,
-  0xc2, 0x01, 0xb4, 0xf8, 0xd3, 0x98, 0x8c, 0xb8, 0x58, 0x00, 0x2b, 0x54,
-  0x1b, 0x27, 0x74, 0x7c, 0x14, 0x7d, 0xb8, 0xf1, 0x4e, 0x68, 0xfa, 0xcd,
-  0x74, 0x89, 0x0c, 0x5e, 0x81, 0x5c, 0xcb, 0xdd, 0x39, 0x94, 0x11, 0xc6,
-  0x01, 0x30, 0x19, 0x88, 0x57, 0x1a, 0xa3, 0x6a, 0xa6, 0xad, 0x25, 0x92,
-  0xbf, 0xf5, 0xf3, 0xfa, 0xb8, 0xf3, 0x13, 0xf1, 0x2f, 0xc2, 0x00, 0x79,
-  0xd5, 0x34, 0x09, 0x9b, 0xa0, 0xa5, 0x0c, 0xdd, 0xb5, 0xe9, 0x2a, 0xf6,
-  0x8e, 0x59, 0x92, 0x83, 0x88, 0x05, 0xe9, 0x81, 0x0b, 0xa7, 0x55, 0xc6,
-  0xdd, 0x89, 0x0a, 0x4d, 0xd9, 0xc8, 0x2d, 0xb3, 0x30, 0x08, 0x52, 0x02,
-  0x85, 0x13, 0xcb, 0xc5, 0x12, 0x13, 0x87, 0x62, 0x10, 0x4e, 0xba, 0x6a,
-  0xb8, 0xb1, 0xd2, 0x3c, 0xcc, 0xee, 0x66, 0xa9, 0xdf, 0xe9, 0xda, 0xfc,
-  0xa4, 0xb1, 0x18, 0x6e, 0x58, 0x44, 0x6c, 0x67, 0xa5, 0x54, 0x45, 0xe9,
-  0x53, 0x4e, 0xa1, 0x32, 0x82, 0x7c, 0xd7, 0x83, 0xec, 0xfa, 0x9b, 0x41,
-  0xc0, 0x21, 0x1a, 0x8f, 0xf0, 0x07, 0xff, 0xff, 0xff, 0x1d, 0x6f, 0xa4,
-  0xb0, 0xd1, 0xac, 0x17, 0x08, 0x12, 0xa8, 0xf2, 0xb4, 0x70, 0x95, 0x6d,
-  0x28, 0x82, 0x92, 0x89, 0x50, 0x09, 0x09, 0x64, 0x9c, 0xda, 0x89, 0x84,
-  0x02, 0x32, 0x61, 0xab, 0x77, 0x86, 0xa4, 0x49, 0x0a, 0x6e, 0x24, 0x21,
-  0x70, 0x5c, 0x4b, 0xdb, 0x3f, 0x3f, 0x84, 0xf5, 0xaf, 0xd4, 0x3b, 0x22,
-  0x9f, 0xf1, 0x0b, 0x93, 0xc0, 0x72, 0x36, 0xbc, 0x7c, 0xfd, 0xa3, 0x9b,
-  0xd5, 0x21, 0x17, 0xaf, 0x5e, 0xd2, 0x6d, 0xdc, 0xb9, 0x31, 0x3c, 0x4c,
-  0x7c, 0x8f, 0x58, 0x26, 0xa8, 0x70, 0x76, 0x2f, 0x16, 0xbd, 0x93, 0x7b,
-  0x16, 0xc1, 0x5d, 0x62, 0xe9, 0xa4, 0x8e, 0xf7, 0x89, 0x55, 0xb2, 0xc0,
-  0x92, 0xa6, 0xab, 0x1a, 0xbe, 0x31, 0x80, 0xe1, 0xac, 0xb6, 0x0a, 0x64,
-  0x74, 0xf5, 0x7b, 0x1b, 0xb6, 0xf1, 0x28, 0x79, 0xbe, 0x97, 0x24, 0xff,
-  0x23, 0x56, 0xd1, 0x3e, 0x38, 0x87, 0x63, 0x35, 0xd4, 0xd5, 0x35, 0xa5,
-  0x55, 0xbb, 0x56, 0x71, 0x5e, 0xfe, 0xb9, 0x38, 0x51, 0x1c, 0xaf, 0x27,
-  0x0c, 0xce, 0x62, 0x37, 0xa9, 0xc2, 0x24, 0x73, 0x94, 0xe9, 0x6c, 0xed,
-  0x4d, 0xe0, 0x31, 0x4f, 0x53, 0xdd, 0xaf, 0x43, 0x53, 0x97, 0x0d, 0xd5,
-  0x12, 0x83, 0x7e, 0x99, 0x39, 0x96, 0x2e, 0x49, 0xa1, 0x5d, 0xae, 0xdc,
-  0x06, 0x3c, 0x77, 0x82, 0xe3, 0x06, 0xb4, 0x83, 0x58, 0x6d, 0x1e, 0x90,
-  0x1f, 0xd0, 0xcd, 0x22, 0x44, 0x72, 0xdb, 0x7d, 0xf7, 0xaa, 0xfa, 0xfb,
-  0x0a, 0x04, 0x24, 0x7f, 0x5f, 0xfe, 0x21, 0x3d, 0x26, 0x6f, 0xa9, 0xa5,
-  0x8d, 0x1d, 0xf7, 0xaa, 0x90, 0x80, 0xc4, 0x39, 0x01, 0x12, 0x38, 0x19,
-  0xd2, 0xc8, 0x68, 0x3c, 0x8d, 0x77, 0xa0, 0xbb, 0x3a, 0xf3, 0x16, 0x03,
-  0x7d, 0xcf, 0xf8, 0x67, 0xd9, 0xda, 0x00, 0x34, 0x17, 0x46, 0x79, 0xcc,
-  0x9c, 0xad, 0x11, 0x2a, 0x09, 0x27, 0xb0, 0xa5, 0x47, 0x5e, 0x0f, 0x0a,
-  0xeb, 0x78, 0x1e, 0x0e, 0x8f, 0x30, 0x04, 0x85, 0x9f, 0xd2, 0xc7, 0x12,
-  0x80, 0x85, 0x08, 0x31, 0x08, 0x1e, 0xdb, 0x4a, 0x45, 0x65, 0xf5, 0x56,
-  0x16, 0x09, 0x41, 0x6a, 0xe4, 0x5b, 0x5b, 0x3b, 0x82, 0x43, 0x3e, 0x7e,
-  0x3f, 0xe2, 0x8a, 0xc0, 0x0e, 0xe6, 0x4f, 0xad, 0x53, 0xc8, 0xdf, 0xd9,
-  0xae, 0xbb, 0xdd, 0xf5, 0xff, 0xcf, 0x14, 0xed, 0xcf, 0x71, 0x5e, 0x1b,
-  0x27, 0xdd, 0xc6, 0xf1, 0x56, 0x46, 0x86, 0x99, 0x86, 0xb8, 0x91, 0xe8,
-  0x58, 0x69, 0xde, 0x81, 0x11, 0x95, 0xf2, 0x78, 0x7e, 0x30, 0x87, 0xaa,
-  0xa9, 0xd7, 0x9c, 0x45, 0x99, 0xa2, 0xb6, 0x49, 0xb6, 0x69, 0x65, 0x95,
-  0x95, 0x46, 0x91, 0x22, 0xe9, 0x67, 0xef, 0x39, 0x65, 0xb6, 0x50, 0x12,
-  0xfd, 0xc0, 0x04, 0x77, 0x89, 0xa8, 0x34, 0x2d, 0x23, 0xfa, 0xf4, 0xdf,
-  0xa7, 0xfb, 0xfd, 0x99, 0xea, 0x51, 0x2f, 0xf4, 0x45, 0x1a, 0x12, 0x9f,
-  0x93, 0x82, 0x7c, 0xe4, 0x24, 0xdf, 0xcb, 0xf4, 0x7c, 0xfd, 0x83, 0x4f,
-  0x6f, 0x0d, 0xd4, 0xf0, 0x6a, 0x07, 0x21, 0x1a, 0x8f, 0xe0, 0x2f, 0x7f,
-  0xff, 0xff, 0x1d, 0x6f, 0xa2, 0xc1, 0x52, 0x06, 0x10, 0x06, 0x8d, 0x2a,
-  0xeb, 0x89, 0x75, 0x6b, 0x55, 0x42, 0x54, 0x50, 0x45, 0x02, 0x22, 0x59,
-  0x38, 0xa8, 0xc8, 0x45, 0x27, 0x2e, 0xbe, 0x06, 0x58, 0x31, 0x00, 0x3a,
-  0xdd, 0x9f, 0x5a, 0x8e, 0x5c, 0x16, 0x04, 0x1c, 0x08, 0x7d, 0x73, 0xfd,
-  0x7c, 0xaa, 0x3f, 0x9f, 0xda, 0xb8, 0x71, 0x31, 0x97, 0xf3, 0x2c, 0x0b,
-  0x66, 0x12, 0xcb, 0x86, 0xd8, 0x5d, 0x01, 0x8b, 0xda, 0xa1, 0x98, 0x24,
-  0x2d, 0xbd, 0x9f, 0x24, 0x49, 0x2a, 0x6f, 0xdd, 0x72, 0xa8, 0x67, 0x88,
-  0x15, 0x73, 0x13, 0xac, 0x3d, 0xe4, 0x1b, 0xcf, 0xc0, 0x6b, 0x84, 0xa2,
-  0x5f, 0x27, 0x3e, 0xf3, 0xd6, 0x39, 0x3a, 0x8b, 0x09, 0x3d, 0x38, 0xe4,
-  0x26, 0x83, 0x78, 0x7a, 0x5c, 0xd1, 0xb8, 0xd6, 0x64, 0xdc, 0xd1, 0xbf,
-  0xd6, 0x20, 0x6e, 0xd2, 0x47, 0x43, 0x4c, 0x39, 0x38, 0x34, 0x74, 0x63,
-  0x87, 0xf1, 0x6e, 0xea, 0xd3, 0x0c, 0xfb, 0x71, 0x4e, 0x2c, 0x24, 0x28,
-  0x70, 0xb2, 0xaf, 0xd7, 0xa9, 0x6f, 0x54, 0x2e, 0x1b, 0xca, 0xb5, 0x7b,
-  0x6b, 0x5b, 0x90, 0x11, 0x96, 0x28, 0xd4, 0x5e, 0xdc, 0x3a, 0xe6, 0x54,
-  0x23, 0x1f, 0x21, 0x8c, 0x0c, 0x97, 0xba, 0xb0, 0x84, 0x3c, 0x8a, 0xdd,
-  0x5e, 0xd4, 0xd9, 0x0f, 0x0e, 0x9a, 0xbb, 0xe6, 0x2a, 0xb1, 0x90, 0x3b,
-  0x47, 0xe8, 0xf3, 0x41, 0x0d, 0xe3, 0x56, 0x5d, 0x94, 0x5b, 0xd4, 0x43,
-  0x73, 0x3f, 0x71, 0x79, 0xda, 0x1e, 0xab, 0x6d, 0x61, 0x55, 0xec, 0x76,
-  0x54, 0x6a, 0xb7, 0x63, 0x41, 0x3d, 0x30, 0x72, 0xf7, 0xa7, 0x05, 0x8e,
-  0x99, 0x9a, 0xad, 0x71, 0x1d, 0x25, 0xbf, 0xd4, 0x86, 0x99, 0x6e, 0x70,
-  0x10, 0x15, 0x37, 0x4b, 0xd6, 0xdd, 0x35, 0xf0, 0x24, 0x9c, 0x5b, 0xb6,
-  0xfe, 0xfa, 0x02, 0xdc, 0xd9, 0xf7, 0x9e, 0xd6, 0x00, 0x93, 0xb3, 0xf9,
-  0xa8, 0x50, 0x74, 0x18, 0x84, 0x04, 0x2a, 0x41, 0x88, 0x43, 0x46, 0x4a,
-  0xca, 0x9f, 0x10, 0xb8, 0xe0, 0x41, 0x03, 0x6b, 0x25, 0x09, 0x16, 0xe7,
-  0x9b, 0xbd, 0xbe, 0x0e, 0xdd, 0x84, 0x61, 0xb7, 0x2e, 0x58, 0x74, 0x43,
-  0xbd, 0xfc, 0x55, 0xd8, 0x72, 0x14, 0x3b, 0x5f, 0xaa, 0xf3, 0xd6, 0xa8,
-  0xd5, 0x40, 0x21, 0xcc, 0x3d, 0x20, 0xba, 0xc0, 0x4f, 0x1a, 0x50, 0x4b,
-  0x98, 0x5c, 0x54, 0x3e, 0x78, 0xd1, 0xcc, 0xe8, 0x1a, 0xde, 0x62, 0xb8,
-  0xb6, 0xc4, 0x8b, 0x05, 0x32, 0x1b, 0xc5, 0xf9, 0x45, 0xfb, 0x92, 0x49,
-  0x66, 0x4e, 0x10, 0x3c, 0xf8, 0x5a, 0xb2, 0xac, 0xba, 0xb4, 0x37, 0xd1,
-  0x7e, 0xd3, 0x00, 0x84, 0xbc, 0xfa, 0x33, 0x89, 0x7e, 0x68, 0x96, 0x00,
-  0x57, 0x83, 0x81, 0xd2, 0x18, 0xbe, 0x74, 0x4b, 0x8b, 0x8d, 0x6e, 0x00,
-  0x44, 0x69, 0x62, 0xa6, 0x3d, 0xb9, 0xb2, 0xd0, 0x50, 0xd7, 0x15, 0xc5,
-  0x73, 0x77, 0x70, 0x95, 0x3d, 0xa1, 0xb7, 0x07, 0x1e, 0x39, 0xc4, 0x84,
-  0x20, 0xf8, 0x6a, 0xb5, 0xbf, 0xaf, 0xdc, 0x38, 0x21, 0x1a, 0x94, 0x75,
-  0xbe, 0x92, 0xc1, 0x44, 0x11, 0x10, 0xa0, 0x47, 0x04, 0x17, 0x13, 0x55,
-  0x5a, 0x8c, 0x84, 0x14, 0x80, 0xa1, 0x59, 0x0e, 0x66, 0x1c, 0x46, 0xa7,
-  0xab, 0x9d, 0x23, 0x65, 0x68, 0x96, 0x8a, 0xe8, 0x20, 0xd9, 0xe5, 0x9f,
-  0xd5, 0x83, 0x96, 0x9f, 0xcc, 0x7f, 0x7f, 0xd9, 0x3b, 0x25, 0x2d, 0x9a,
-  0x62, 0x10, 0x09, 0x98, 0xe2, 0xfd, 0xa5, 0xad, 0x3a, 0x1b, 0xb8, 0xe6,
-  0x1c, 0xf9, 0xdf, 0xf4, 0xca, 0x74, 0xd0, 0x6b, 0xfd, 0xb0, 0xf1, 0x8d,
-  0xa3, 0xe5, 0x40, 0x3f, 0xb4, 0x3e, 0x9b, 0x0f, 0x41, 0x07, 0xac, 0xd7,
-  0x31, 0x59, 0x55, 0x64, 0xb9, 0x9a, 0x81, 0x1a, 0x29, 0x55, 0xf1, 0x8a,
-  0x5c, 0xdb, 0x06, 0x92, 0x9d, 0x68, 0xd0, 0xab, 0x18, 0x12, 0x64, 0x18,
-  0x31, 0xa8, 0xdf, 0x14, 0x9b, 0x36, 0x60, 0x3b, 0xa2, 0xcc, 0xa9, 0xf5,
-  0xd9, 0x0a, 0x9d, 0x5e, 0x45, 0x38, 0xa0, 0xbc, 0x1d, 0x06, 0xa6, 0x1c,
-  0xdf, 0xa9, 0xbc, 0x39, 0xd6, 0xee, 0x17, 0x9a, 0x30, 0x62, 0xeb, 0x5d,
-  0xdb, 0xd0, 0x87, 0x2d, 0xd9, 0x9c, 0xc8, 0xe1, 0x27, 0x5a, 0x0e, 0x12,
-  0x09, 0x6d, 0xb8, 0xa9, 0xa0, 0x90, 0xb2, 0x15, 0xb5, 0xb2, 0x7b, 0xcc,
-  0x66, 0x91, 0xb0, 0x3a, 0x25, 0x54, 0x80, 0xda, 0x84, 0xd6, 0xb9, 0xa9,
-  0xbb, 0x72, 0xda, 0x74, 0x55, 0xea, 0xe7, 0x63, 0x27, 0x42, 0xbc, 0x03,
-  0x9f, 0x6a, 0x5e, 0x83, 0x2b, 0x75, 0x24, 0xc9, 0xc8, 0xae, 0xde, 0x6e,
-  0x97, 0x07, 0x1b, 0xc8, 0xe3, 0xb2, 0xfe, 0x51, 0x25, 0x8c, 0x2d, 0xeb,
-  0x1c, 0x19, 0x4b, 0xfb, 0x58, 0x3e, 0x1d, 0xd4, 0x5a, 0xd0, 0x19, 0x25,
-  0x90, 0x3d, 0x44, 0x9d, 0xd7, 0x6d, 0xcd, 0x8c, 0x3f, 0x6f, 0xc8, 0xb8,
-  0x7f, 0xee, 0x16, 0xe4, 0x9a, 0x9d, 0x7a, 0x6a, 0xb3, 0xbb, 0xbc, 0x8d,
-  0x8e, 0x99, 0x08, 0x12, 0x01, 0x04, 0xe8, 0x31, 0x08, 0x17, 0x75, 0x38,
-  0x41, 0x23, 0xa2, 0xa1, 0x00, 0x42, 0x83, 0xfa, 0x94, 0x50, 0xe4, 0xee,
-  0x0d, 0xc7, 0xd9, 0x16, 0x57, 0xbe, 0x8b, 0xdf, 0xb4, 0xae, 0xd0, 0xa0,
-  0xa7, 0xd5, 0x18, 0x9a, 0x44, 0x4c, 0x58, 0x1d, 0xd4, 0xb4, 0x2d, 0x39,
-  0x31, 0xfa, 0x9d, 0x1a, 0x7a, 0xb4, 0x98, 0xcb, 0xd9, 0x27, 0xf2, 0xc4,
-  0xac, 0x5f, 0x4c, 0x51, 0x63, 0xbf, 0x56, 0x1a, 0xe4, 0x77, 0x8c, 0xfc,
-  0xa1, 0x57, 0xb6, 0x68, 0x66, 0xea, 0xb3, 0xea, 0x6a, 0xcb, 0x8d, 0x2f,
-  0x7f, 0x8e, 0x44, 0xe0, 0x32, 0x40, 0x8c, 0x5a, 0x2a, 0x2d, 0xe5, 0xb8,
-  0xc9, 0x21, 0x09, 0xfb, 0xca, 0x79, 0x34, 0x47, 0xb0, 0x90, 0x94, 0x6f,
-  0xde, 0x91, 0x12, 0x1e, 0xd5, 0x15, 0x82, 0x69, 0x4e, 0xf0, 0x70, 0x0a,
-  0xcc, 0x47, 0x50, 0x02, 0x4b, 0xf8, 0xae, 0xf1, 0x12, 0xc5, 0xc6, 0xee,
-  0x73, 0x21, 0x0d, 0x3e, 0xaf, 0x33, 0x59, 0x44, 0x5b, 0x33, 0xe7, 0xde,
-  0xc5, 0xdd, 0x05, 0xfd, 0x9e, 0x5d, 0x1b, 0x52, 0xbe, 0xe9, 0xe2, 0xe6,
-  0x93, 0x48, 0x70, 0x21, 0x1a, 0x94, 0x95, 0xbe, 0x8b, 0x05, 0x41, 0x92,
-  0xd8, 0x46, 0x10, 0x5f, 0x41, 0x70, 0x68, 0xe3, 0x12, 0xc0, 0x00, 0x2f,
-  0x91, 0x93, 0x42, 0x40, 0x45, 0xee, 0xd2, 0x6f, 0x83, 0xb4, 0x2c, 0xd1,
-  0x10, 0x10, 0xfe, 0xc5, 0xa4, 0x3f, 0x1b, 0x92, 0x32, 0x51, 0x29, 0x2a,
-  0xba, 0x5d, 0xfc, 0x5b, 0x44, 0x16, 0x6b, 0xff, 0x37, 0x03, 0x33, 0xee,
-  0x35, 0xc5, 0x75, 0xae, 0x15, 0xd2, 0x30, 0xd6, 0x5d, 0xe0, 0xe6, 0x8c,
-  0x72, 0x2b, 0x99, 0x54, 0x66, 0xa9, 0xeb, 0x5f, 0xcb, 0x02, 0xc7, 0x3b,
-  0xa1, 0x1c, 0x4a, 0x84, 0xf6, 0xfb, 0x3a, 0x4b, 0x76, 0x83, 0x2f, 0xa4,
-  0x60, 0x81, 0x48, 0x46, 0x71, 0x93, 0x74, 0x50, 0xec, 0xa9, 0xf8, 0xe5,
-  0x56, 0x09, 0xbb, 0x7a, 0x94, 0xc0, 0x8c, 0xf6, 0x99, 0x7c, 0xd5, 0x35,
-  0x04, 0x45, 0x46, 0x40, 0x34, 0x92, 0xc4, 0xa1, 0xc1, 0x32, 0x61, 0x50,
-  0x67, 0xde, 0xbc, 0xdb, 0xd2, 0x85, 0x2e, 0x01, 0x42, 0xa3, 0x29, 0x77,
-  0xc1, 0xa5, 0x42, 0xe4, 0x84, 0x84, 0x55, 0x38, 0x10, 0xdd, 0x98, 0x6c,
-  0xf5, 0xae, 0xf9, 0xe8, 0x84, 0x72, 0x9b, 0xec, 0xb9, 0x1f, 0xa2, 0x07,
-  0x42, 0xa5, 0xbe, 0x73, 0x69, 0x7b, 0x1c, 0xd4, 0xf7, 0x19, 0x4d, 0x2b,
-  0x62, 0x4b, 0x44, 0xe6, 0xa5, 0x66, 0xab, 0xfc, 0xaa, 0x63, 0x3c, 0xb1,
-  0xe2, 0x62, 0xa4, 0xd7, 0xcb, 0x2e, 0xe8, 0x80, 0x7d, 0x0c, 0xb6, 0x68,
-  0xc0, 0xc7, 0xf4, 0xfb, 0x81, 0x65, 0xb4, 0x6f, 0x58, 0xb2, 0x11, 0xd8,
-  0x7f, 0x63, 0xdd, 0x57, 0x13, 0xb4, 0x23, 0x58, 0x44, 0x36, 0x88, 0x90,
-  0xae, 0xc1, 0x4c, 0x0e, 0xc7, 0x10, 0x25, 0xc3, 0x07, 0x69, 0x25, 0x0c,
-  0x9c, 0x7c, 0xdf, 0x74, 0x24, 0x6d, 0x10, 0xe1, 0x92, 0x04, 0x42, 0x0a,
-  0x90, 0xe3, 0x82, 0x93, 0x4d, 0x31, 0x08, 0xe3, 0x94, 0x10, 0xc5, 0xe0,
-  0xa6, 0x74, 0x43, 0xa7, 0xbc, 0xe7, 0x8c, 0x55, 0xd9, 0x8e, 0xed, 0x88,
-  0xaf, 0x6b, 0x58, 0x81, 0xf2, 0xdd, 0x2b, 0x11, 0x69, 0xd6, 0x93, 0x6e,
-  0xfc, 0xde, 0x1d, 0x4d, 0x1e, 0xaf, 0x95, 0x43, 0x32, 0x4c, 0x3c, 0x3c,
-  0xae, 0x5e, 0xbf, 0x5d, 0xab, 0xcb, 0xfb, 0xa8, 0xdd, 0xaf, 0x04, 0x6c,
-  0xa5, 0x54, 0xc2, 0x9e, 0x8d, 0x0e, 0xef, 0x57, 0x2e, 0x8d, 0xd9, 0xb6,
-  0x14, 0xdb, 0x5b, 0x1a, 0x0f, 0x6e, 0x75, 0xc8, 0x99, 0xd4, 0x08, 0xb4,
-  0x62, 0xc0, 0x76, 0x0b, 0x41, 0x47, 0x33, 0x6f, 0x67, 0xcf, 0x56, 0x36,
-  0x8c, 0x37, 0x00, 0x50, 0x4c, 0x02, 0x5d, 0x3b, 0x68, 0xc6, 0xd6, 0x04,
-  0x00, 0x16, 0x8c, 0x98, 0x43, 0x3d, 0xb8, 0xf4, 0xac, 0x85, 0x29, 0x0b,
-  0xc4, 0x9a, 0xe0, 0x26, 0x34, 0x1c, 0xb9, 0x7b, 0xa6, 0x62, 0xc9, 0xd5,
-  0x82, 0x7f, 0x3d, 0x3e, 0x38, 0x46, 0x22, 0xdf, 0x6d, 0x6c, 0x0f, 0x3f,
-  0xcc, 0x3d, 0x75, 0x7f, 0x04, 0x90, 0x96, 0xb9, 0xe4, 0x3b, 0xad, 0x10,
-  0x70, 0x21, 0x1a, 0x94, 0xad, 0xbe, 0x8c, 0xc4, 0x22, 0x98, 0x89, 0x41,
-  0x5d, 0x1d, 0x0e, 0x80, 0xe1, 0x56, 0x00, 0x00, 0x06, 0xbe, 0xa9, 0xc5,
-  0xdd, 0xb4, 0x39, 0x68, 0x23, 0x56, 0xa3, 0xef, 0x3c, 0xff, 0xbd, 0x75,
-  0x6c, 0xf8, 0xc8, 0x27, 0xe5, 0x23, 0x9f, 0x21, 0x8e, 0xec, 0xd1, 0xee,
-  0x5d, 0xeb, 0xd2, 0x78, 0x5d, 0x97, 0xd1, 0x96, 0x16, 0x21, 0x94, 0x1e,
-  0xb0, 0xca, 0xc0, 0x22, 0xe9, 0x0f, 0x0c, 0xaa, 0xb2, 0xc4, 0xc1, 0xc7,
-  0x91, 0xac, 0xb2, 0x8b, 0x06, 0x94, 0xe9, 0x17, 0xbf, 0x1a, 0x9b, 0x8c,
-  0x19, 0xac, 0xde, 0xa9, 0x64, 0x25, 0xd9, 0x28, 0x16, 0x36, 0x2d, 0x45,
-  0x96, 0x19, 0xef, 0x0d, 0xd0, 0x5e, 0x9e, 0x1f, 0x74, 0xad, 0x01, 0x9d,
-  0x2a, 0x9a, 0xbb, 0xe5, 0xb9, 0xe9, 0xef, 0xf7, 0xbb, 0xd7, 0xfc, 0x3f,
-  0x87, 0x17, 0xd6, 0xbd, 0x97, 0xf2, 0xed, 0xe7, 0x9c, 0xf9, 0x3c, 0xc5,
-  0x9a, 0x38, 0x39, 0x9b, 0xab, 0xe4, 0x1a, 0x6c, 0xca, 0xbd, 0x86, 0x38,
-  0x90, 0xc7, 0xc6, 0x15, 0xe0, 0x8f, 0x24, 0x93, 0x69, 0x02, 0xbe, 0x94,
-  0x20, 0xfb, 0x83, 0xe8, 0xcc, 0x47, 0xe6, 0x2d, 0x2a, 0x23, 0x05, 0x89,
-  0x76, 0x5a, 0x09, 0x96, 0x2d, 0xa6, 0x5d, 0x24, 0x6a, 0x5c, 0x81, 0x8d,
-  0x99, 0x54, 0x91, 0xd6, 0x61, 0x7a, 0x62, 0x68, 0xc1, 0x64, 0x50, 0x0a,
-  0xe5, 0x23, 0x3e, 0xb4, 0x6f, 0x33, 0x3a, 0x80, 0x00, 0x60, 0x30, 0x76,
-  0xde, 0xef, 0x44, 0xc0, 0x73, 0xa9, 0x33, 0xbe, 0x64, 0xa6, 0xae, 0x82,
-  0x6d, 0x80, 0x01, 0x1f, 0x67, 0xa4, 0x41, 0xd4, 0x63, 0x10, 0x02, 0x49,
-  0xa6, 0x5d, 0xb4, 0xcb, 0x5a, 0x52, 0x20, 0xc1, 0x96, 0x17, 0x35, 0x9e,
-  0x06, 0x75, 0x91, 0x26, 0x61, 0xff, 0x76, 0xc7, 0x0e, 0x7a, 0xb8, 0xea,
-  0x70, 0x13, 0x2b, 0xc9, 0x20, 0x39, 0x3c, 0x3e, 0x45, 0x8c, 0xb0, 0xc2,
-  0xd5, 0x4c, 0x8f, 0x01, 0xa0, 0x4e, 0xd8, 0xf4, 0x3a, 0x3e, 0x1f, 0x5f,
-  0x3c, 0x85, 0xef, 0xf7, 0x5b, 0x5d, 0xcf, 0x4f, 0xd4, 0xea, 0x89, 0xe7,
-  0xc3, 0x45, 0x0f, 0xc2, 0xae, 0x72, 0x98, 0x56, 0x3c, 0xfc, 0x75, 0x0e,
-  0xef, 0x76, 0xcd, 0x50, 0x58, 0xda, 0xfd, 0xec, 0x3b, 0x75, 0x4d, 0xe3,
-  0x4a, 0xb9, 0x44, 0x42, 0x58, 0xca, 0x60, 0x00, 0x72, 0xf9, 0xe5, 0x77,
-  0x4a, 0x5f, 0x2a, 0x96, 0x86, 0xe3, 0x01, 0x32, 0x0e, 0x6b, 0xde, 0xc9,
-  0x15, 0x15, 0x45, 0x54, 0x26, 0x2e, 0x45, 0x40, 0x5e, 0xcb, 0x29, 0xd5,
-  0x95, 0x4d, 0x2a, 0x08, 0x22, 0xf9, 0x5e, 0x74, 0xe8, 0xe1, 0x25, 0x27,
-  0xf0, 0x62, 0x95, 0x93, 0x4a, 0x0a, 0x2c, 0x26, 0xac, 0xd0, 0x68, 0x60,
-  0x95, 0xd9, 0x1b, 0x7f, 0x3b, 0xac, 0x0a, 0x77, 0xf0, 0x21, 0xe5, 0xc6,
-  0x7e, 0x5d, 0x7c, 0x7e, 0x70, 0xe0, 0x21, 0x1a, 0x8d, 0xcf, 0x90, 0x1f,
-  0xff, 0xff, 0x2d, 0x6f, 0x82, 0x31, 0x50, 0x46, 0xb2, 0x1a, 0x04, 0x42,
-  0x1e, 0x63, 0x9b, 0xb6, 0xa2, 0xc9, 0x69, 0xbe, 0x10, 0x04, 0xa1, 0x00,
-  0x2a, 0x49, 0xe1, 0x74, 0x7a, 0xf7, 0x71, 0xfd, 0x25, 0xc5, 0xf8, 0x32,
-  0x01, 0x17, 0x63, 0xac, 0x49, 0xa4, 0xbe, 0xe9, 0xd8, 0x9f, 0xf3, 0xf5,
-  0xa4, 0xf5, 0x89, 0xb9, 0x72, 0xe5, 0xb0, 0xc5, 0x88, 0x69, 0x19, 0xc4,
-  0x43, 0x57, 0xc7, 0x33, 0x8a, 0xa6, 0x72, 0xda, 0xb6, 0x15, 0x9c, 0xb3,
-  0x7c, 0xb0, 0x6c, 0x26, 0x10, 0x86, 0x5e, 0x75, 0x56, 0xa3, 0x80, 0x4b,
-  0xaf, 0xca, 0xf9, 0xa3, 0xc4, 0x9d, 0x6f, 0xec, 0x4e, 0x68, 0x68, 0x58,
-  0xb9, 0x44, 0x37, 0x6c, 0x76, 0x80, 0x09, 0x84, 0xa1, 0x7c, 0xa3, 0x1d,
-  0xf4, 0xd6, 0x83, 0x85, 0xd7, 0x79, 0x04, 0x73, 0x82, 0x0d, 0x08, 0xc6,
-  0x98, 0x15, 0xb2, 0xf9, 0x94, 0x63, 0x07, 0xa5, 0x6e, 0x19, 0x34, 0xd7,
-  0x4c, 0xbe, 0x91, 0x6b, 0xce, 0x31, 0x9b, 0x5a, 0x13, 0xc8, 0x16, 0x84,
-  0x6e, 0xd1, 0xd6, 0xb6, 0xcd, 0xa3, 0x3b, 0xc8, 0xdd, 0x64, 0xef, 0xf5,
-  0x08, 0x75, 0x7a, 0x50, 0xb1, 0x77, 0x2e, 0x92, 0xc1, 0xd8, 0xaa, 0x83,
-  0x14, 0x11, 0x88, 0x5b, 0x42, 0x57, 0x52, 0x52, 0xf9, 0xba, 0xa8, 0x31,
-  0xeb, 0x82, 0x81, 0x3d, 0x29, 0x37, 0x7d, 0x38, 0x48, 0xab, 0x0d, 0xa3,
-  0x50, 0x2b, 0x28, 0xcd, 0x29, 0xfc, 0x46, 0xf3, 0x39, 0x65, 0x58, 0x24,
-  0x6c, 0xf4, 0x5b, 0x15, 0x21, 0x04, 0x2e, 0x22, 0x00, 0x68, 0x80, 0xd4,
-  0x5c, 0x68, 0xb1, 0x42, 0x61, 0x00, 0x39, 0xba, 0xef, 0x4e, 0xe2, 0xea,
-  0xbe, 0x61, 0xc7, 0x5e, 0x26, 0xde, 0xe2, 0x82, 0x2e, 0x41, 0x22, 0xa8,
-  0x89, 0x93, 0xe5, 0xce, 0xef, 0xe9, 0x94, 0xd0, 0xd0, 0x36, 0x09, 0x76,
-  0x1e, 0xd9, 0x9d, 0xeb, 0xec, 0x67, 0xd1, 0xeb, 0x9c, 0xbb, 0x92, 0x36,
-  0x3f, 0xa2, 0x53, 0x71, 0x34, 0xe4, 0x8d, 0x2a, 0xcc, 0xef, 0xcf, 0xb9,
-  0x24, 0xd6, 0xca, 0x1c, 0xcf, 0xeb, 0xe5, 0x70, 0x1f, 0x4e, 0xd6, 0xfd,
-  0x4d, 0xb0, 0x6e, 0xba, 0x34, 0xf2, 0x31, 0xef, 0x1b, 0x14, 0x26, 0x8a,
-  0x3d, 0xca, 0xaa, 0x27, 0xf7, 0xf5, 0x06, 0x1e, 0x4d, 0x66, 0xae, 0xc1,
-  0x48, 0x07, 0x4e, 0xb4, 0x37, 0xc1, 0x40, 0x33, 0x55, 0x3f, 0x14, 0xdd,
-  0x02, 0x8b, 0x13, 0x9b, 0x94, 0x60, 0x04, 0x3c, 0x15, 0xb8, 0x9d, 0x50,
-  0x8f, 0x31, 0x7b, 0x05, 0xa3, 0x68, 0x29, 0xaa, 0xbf, 0x39, 0x7d, 0xed,
-  0x28, 0x11, 0x4b, 0x3a, 0xa7, 0x0d, 0x3c, 0x7d, 0xe3, 0x7a, 0x48, 0xa6,
-  0x16, 0xaa, 0xe8, 0x25, 0x11, 0x7b, 0x5a, 0xab, 0x4d, 0xed, 0x0c, 0x5e,
-  0x67, 0x6e, 0x28, 0x84, 0xf4, 0x2b, 0x18, 0x38, 0x56, 0x21, 0xea, 0x34,
-  0x2d, 0x56, 0xc0, 0x00, 0xe0, 0x21, 0x1a, 0x8f, 0xce, 0x18, 0x1f, 0xff,
-  0xff, 0x2d, 0x70, 0x81, 0xb1, 0x88, 0x82, 0xa3, 0x1b, 0x58, 0xb8, 0xd4,
-  0x2e, 0xb8, 0x11, 0x2c, 0xb0, 0x00, 0x05, 0x07, 0x2f, 0xa9, 0x71, 0xff,
-  0xd4, 0x31, 0xf8, 0xf7, 0x9f, 0x42, 0x70, 0x0b, 0x9e, 0x95, 0xa4, 0x33,
-  0x45, 0xcb, 0xde, 0x78, 0x4e, 0x17, 0xec, 0xbc, 0xdd, 0xf8, 0x8e, 0x3f,
-  0xb2, 0xe2, 0xf0, 0xf3, 0xfb, 0x77, 0x34, 0xa2, 0xa4, 0x71, 0x8f, 0xb6,
-  0x39, 0xc6, 0x98, 0x91, 0x1b, 0xaf, 0xfe, 0x7b, 0x97, 0x50, 0xe3, 0xd0,
-  0xb0, 0x78, 0x3b, 0x2b, 0xcd, 0xc4, 0x5d, 0xab, 0xe4, 0xe3, 0x55, 0x5d,
-  0xca, 0xe9, 0x6c, 0xe3, 0x41, 0x79, 0x89, 0xf6, 0x54, 0x32, 0xb3, 0xa4,
-  0x60, 0x7b, 0xe7, 0x32, 0x76, 0xf2, 0xd2, 0x25, 0x55, 0xa5, 0x61, 0xd4,
-  0x9a, 0xe7, 0xca, 0x4c, 0xd6, 0xa2, 0x08, 0xba, 0xf8, 0x8a, 0x2b, 0xb0,
-  0xc4, 0x0a, 0x48, 0xe1, 0xb9, 0x44, 0x5c, 0x81, 0x90, 0xa4, 0x20, 0x1d,
-  0x47, 0x2b, 0x1c, 0xce, 0x65, 0xa0, 0x65, 0xf8, 0xcc, 0xe1, 0x77, 0x9e,
-  0x6b, 0x12, 0xaf, 0xad, 0xb9, 0x47, 0xfe, 0xcd, 0x38, 0x7b, 0xa7, 0x60,
-  0x86, 0xe4, 0xb7, 0x66, 0x8c, 0x60, 0x8a, 0x48, 0xa1, 0x96, 0x53, 0xc8,
-  0x27, 0xb7, 0xdf, 0xa7, 0x0a, 0x5a, 0x51, 0xa4, 0xa0, 0xe1, 0x6e, 0x71,
-  0x63, 0xbd, 0xab, 0x5f, 0xeb, 0xe1, 0xa0, 0x9a, 0x58, 0xbb, 0x28, 0x6e,
-  0x34, 0x36, 0xeb, 0x38, 0x04, 0xb5, 0xba, 0x90, 0x66, 0x17, 0x24, 0x1b,
-  0xbb, 0xd1, 0x76, 0x85, 0x96, 0xb6, 0xa1, 0x44, 0xa5, 0x00, 0x14, 0x6c,
-  0x26, 0x17, 0x47, 0x1b, 0xc1, 0xf4, 0x93, 0x5b, 0xaa, 0xa9, 0xfd, 0x44,
-  0xab, 0x1a, 0x5a, 0x26, 0x28, 0x74, 0xf3, 0xb8, 0x47, 0x64, 0x22, 0x27,
-  0xaf, 0x58, 0xe3, 0x8b, 0x07, 0x9f, 0x50, 0xe4, 0x8e, 0xe6, 0xed, 0xe9,
-  0x0d, 0x7f, 0xde, 0x5d, 0xae, 0xd9, 0x02, 0x61, 0x15, 0x5b, 0x34, 0x6c,
-  0x20, 0x2d, 0x39, 0x35, 0x69, 0x3b, 0xde, 0xe1, 0x61, 0xc8, 0xdc, 0xb0,
-  0x75, 0x37, 0x82, 0x4a, 0x82, 0xdd, 0xa8, 0x39, 0x85, 0x47, 0x80, 0x4b,
-  0x48, 0xbe, 0xda, 0xf4, 0xc5, 0x79, 0x82, 0x92, 0xbf, 0x0a, 0x84, 0x9d,
-  0x66, 0x58, 0x3f, 0xd6, 0xc0, 0xe3, 0xd7, 0xe0, 0xe1, 0x1c, 0x66, 0x08,
-  0xd5, 0x49, 0x94, 0x69, 0x00, 0x2d, 0x3c, 0x61, 0x61, 0x21, 0x14, 0x4c,
-  0xe9, 0x2a, 0x1d, 0x4c, 0xd7, 0xd9, 0x07, 0xd4, 0x42, 0x09, 0xb5, 0x47,
-  0xba, 0x77, 0x86, 0x52, 0xa2, 0xa8, 0x5e, 0xa5, 0x6b, 0x40, 0x6a, 0x73,
-  0x47, 0x2e, 0x0c, 0x2b, 0x4b, 0x87, 0xbf, 0x65, 0x1f, 0xd6, 0x44, 0xf7,
-  0x2b, 0x4e, 0x9f, 0x69, 0xff, 0x0c, 0x57, 0x0e, 0x21, 0x2a, 0x8f, 0x87,
-  0x5d, 0x3b, 0xff, 0xff, 0x25, 0x67, 0xa6, 0x40, 0xd8, 0x62, 0xb2, 0x28,
-  0x84, 0x19, 0xa5, 0xaf, 0x3a, 0x55, 0x6a, 0x22, 0xc8, 0x04, 0xa1, 0x00,
-  0x21, 0xd5, 0x00, 0x33, 0x01, 0x28, 0x46, 0x24, 0x13, 0xdd, 0x6e, 0x97,
-  0x49, 0xd0, 0x96, 0x13, 0x4d, 0x19, 0x4d, 0xfb, 0x8e, 0x50, 0xb5, 0x74,
-  0x59, 0x0f, 0x94, 0xde, 0xdf, 0x6c, 0xd8, 0x9e, 0x5b, 0xbb, 0x54, 0x81,
-  0x34, 0xf5, 0x60, 0xb0, 0xdb, 0x6f, 0xed, 0x72, 0x33, 0xf9, 0xdf, 0xf0,
-  0xfb, 0xe6, 0x8b, 0x62, 0x8b, 0x2c, 0x7e, 0x78, 0x4c, 0x6c, 0x8b, 0xa6,
-  0x15, 0xb8, 0x09, 0xf5, 0x74, 0xee, 0xb3, 0x40, 0x94, 0x9f, 0x5d, 0x11,
-  0x03, 0x7c, 0x4f, 0x1d, 0xb2, 0xc9, 0x68, 0x4b, 0x5e, 0xfe, 0x1d, 0x00,
-  0xe4, 0x13, 0x73, 0xac, 0xfb, 0xe6, 0xbe, 0x75, 0x14, 0x7e, 0xf2, 0x9c,
-  0xe4, 0x09, 0xe6, 0x11, 0x4b, 0x66, 0x34, 0x4e, 0xf0, 0xfd, 0x22, 0x8b,
-  0xa6, 0x7c, 0x60, 0xf5, 0xc2, 0x5d, 0xa6, 0xe7, 0x6b, 0x4a, 0x31, 0x41,
-  0xa6, 0x6d, 0x32, 0x92, 0x42, 0xdf, 0x3b, 0xd5, 0xda, 0x5d, 0xbd, 0xbc,
-  0xe9, 0xbd, 0x09, 0x87, 0x6e, 0x28, 0xb1, 0x52, 0x15, 0xb2, 0x94, 0xa1,
-  0x24, 0x41, 0xbc, 0x5d, 0x5e, 0x6c, 0x3f, 0xa0, 0xc0, 0x4a, 0xcb, 0x9c,
-  0x53, 0x7c, 0x92, 0x53, 0xe9, 0x31, 0xbe, 0x7f, 0xe5, 0x46, 0xd8, 0x91,
-  0x5b, 0x6a, 0xea, 0xcb, 0x3f, 0x11, 0xe6, 0x6b, 0xe0, 0x12, 0xf6, 0x8a,
-  0x69, 0x98, 0x04, 0x2d, 0x33, 0x4e, 0x88, 0xb5, 0xd1, 0x6b, 0x4a, 0xd2,
-  0x34, 0x28, 0x8d, 0xd8, 0x06, 0xaa, 0x09, 0xaf, 0xa0, 0x5a, 0x7c, 0x90,
-  0xa9, 0xbc, 0x75, 0xdd, 0xa6, 0x9c, 0x0c, 0xe4, 0x27, 0x0e, 0x5d, 0x5e,
-  0x88, 0xdf, 0x2a, 0x89, 0x5e, 0x23, 0xd4, 0x9d, 0xaa, 0x97, 0xb7, 0xe7,
-  0xfc, 0xfe, 0x71, 0x1e, 0xde, 0x4e, 0x32, 0xcd, 0x8d, 0xf4, 0x6f, 0x37,
-  0x57, 0x8c, 0x0a, 0xaa, 0xea, 0x18, 0xa3, 0x70, 0x8e, 0x18, 0x17, 0xc8,
-  0x41, 0x4c, 0xcc, 0xa8, 0x68, 0x14, 0xab, 0xcc, 0x01, 0x32, 0x35, 0xf1,
-  0xf1, 0xbe, 0x61, 0xa3, 0x8e, 0x81, 0xd3, 0xbb, 0x76, 0xee, 0x9e, 0xf3,
-  0x6e, 0x1c, 0x6d, 0xf0, 0xc7, 0x16, 0xdb, 0x3c, 0x40, 0x52, 0x2b, 0xcd,
-  0x48, 0xb8, 0xa0, 0x33, 0x9d, 0x81, 0x48, 0xb1, 0x28, 0x10, 0x56, 0xf7,
-  0x2f, 0x0b, 0x80, 0x52, 0x09, 0x41, 0x91, 0x65, 0x6e, 0x00, 0x0b, 0x4b,
-  0x1a, 0xd9, 0xf1, 0x41, 0x74, 0x54, 0xa7, 0x91, 0xa7, 0xdc, 0x1a, 0xf5,
-  0xc9, 0x3e, 0x41, 0xc0, 0x21, 0x4c, 0xd8, 0xfb, 0xff, 0x5f, 0xff, 0xff,
-  0x00, 0xc6, 0xda, 0xa4, 0xc8, 0x75, 0xb8, 0x68, 0x8a, 0x5c, 0x44, 0x3a,
-  0xf0, 0xe7, 0xb3, 0x87, 0x00, 0x7e, 0x8e, 0x3a, 0xb6, 0x90, 0xaf, 0xe1,
-  0xd6, 0x9a, 0x15, 0xa7, 0xb9, 0xc1, 0x61, 0x42, 0xd5, 0x3f, 0x98, 0xf2,
-  0x2f, 0xc0, 0x65, 0x70, 0xfd, 0x76, 0x6c, 0x6e, 0xdd, 0x86, 0xc7, 0x7c,
-  0x4f, 0x8d, 0x36, 0xf5, 0x6c, 0x0c, 0x59, 0x1a, 0xb2, 0x0f, 0x5f, 0x1e,
-  0x39, 0x27, 0x5c, 0x08, 0xad, 0x32, 0x28, 0x14, 0x94, 0x0d, 0x1f, 0x31,
-  0x06, 0xf8, 0x2e, 0x86, 0x19, 0xdd, 0x2f, 0x4f, 0xba, 0x6d, 0x15, 0xa6,
-  0x38, 0x2a, 0x94, 0xee, 0x2a, 0xaa, 0x0b, 0xea, 0x42, 0xc0, 0x99, 0x6b,
-  0x16, 0x73, 0x7c, 0xaa, 0xa6, 0x95, 0x2e, 0x2b, 0x01, 0x4f, 0xab, 0xf7,
-  0xb5, 0x91, 0x78, 0x62, 0xbc, 0x9e, 0xb3, 0xd5, 0x22, 0x62, 0x03, 0x38,
-  0xa2, 0xb3, 0x65, 0xc9, 0x2c, 0x8a, 0xca, 0x86, 0x82, 0xfc, 0xe6, 0xd2,
-  0x6c, 0x22, 0x7e, 0x5b, 0x46, 0x2e, 0x7c, 0xf8, 0x5c, 0xd7, 0x6b, 0x66,
-  0x99, 0x85, 0x6c, 0x9c, 0x5f, 0x66, 0xd7, 0x0f, 0x8c, 0x59, 0x5e, 0xee,
-  0x73, 0x57, 0x12, 0xa1, 0xcf, 0xd6, 0x8e, 0xf6, 0x5b, 0xea, 0xd4, 0x29,
-  0xf5, 0x7e, 0x55, 0xa6, 0x52, 0x03, 0x6a, 0xc0, 0xe9, 0x8c, 0xc3, 0xd7,
-  0x49, 0x1c, 0x3b, 0x0d, 0x47, 0x09, 0x90, 0x96, 0xdc, 0x3b, 0x17, 0x3b,
-  0xb7, 0x01, 0x45, 0x95, 0x55, 0x64, 0x7a, 0x45, 0x95, 0xe7, 0x93, 0x7c,
-  0x2e, 0x32, 0xbc, 0xa4, 0x26, 0xea, 0x9b, 0x6d, 0x8c, 0xe0, 0xea, 0xc0,
-  0x55, 0x79, 0xc5, 0x4e, 0xf5, 0x86, 0xb7, 0xb9, 0x3f, 0xaf, 0x18, 0xa2,
-  0xac, 0xc4, 0x52, 0x3a, 0xcc, 0x53, 0xd7, 0x07, 0x9b, 0xb5, 0x8c, 0xa4,
-  0xf2, 0xd3, 0x46, 0x3f, 0xab, 0xae, 0x1f, 0x54, 0x52, 0xfe, 0x40, 0xfa,
-  0xa6, 0xdd, 0x83, 0xca, 0x41, 0x01, 0x4a, 0x65, 0x72, 0xe0, 0x41, 0xdb,
-  0xb4, 0xbe, 0xc7, 0xfe, 0x6a, 0x56, 0x22, 0x95, 0x77, 0x1f, 0x62, 0x44,
-  0xc4, 0x6d, 0xca, 0x6c, 0x32, 0x60, 0xc3, 0x56, 0x40, 0x35, 0xc4, 0x60,
-  0xb7, 0x82, 0xf5, 0x08, 0xea, 0x12, 0x04, 0x62, 0x09, 0x9b, 0xdc, 0xd1,
-  0x30, 0xa4, 0xe5, 0x41, 0x3a, 0x82, 0xb4, 0x2f, 0x01, 0x28, 0xac, 0xae,
-  0x43, 0xd4, 0x5d, 0x7e, 0xc1, 0xa7, 0xa7, 0x2d, 0x36, 0x02, 0x45, 0x59,
-  0x76, 0x4e, 0xd3, 0xb4, 0x97, 0x69, 0x5d, 0xd7, 0xd3, 0xd8, 0x04, 0xe2,
-  0x08, 0x96, 0xf8, 0xdc, 0x21, 0x36, 0x92, 0xaa, 0xf1, 0xf1, 0xe5, 0xc6,
-  0x08, 0xdd, 0xc1, 0x86, 0x98, 0x80, 0x45, 0x30, 0xb2, 0x21, 0x59, 0xc7,
-  0x24, 0x6e, 0x5b, 0xf1, 0x31, 0x75, 0xb3, 0x39, 0x9b, 0x51, 0x64, 0x19,
-  0x79, 0xd4, 0x36, 0xd6, 0x5f, 0x61, 0x6e, 0x0a, 0x8e, 0x00, 0xa2, 0xb2,
-  0x65, 0x14, 0xb0, 0xb5, 0x44, 0x49, 0x2f, 0x5a, 0x4e, 0xf7, 0x8c, 0xda,
-  0x5f, 0x29, 0x83, 0x83, 0xa1, 0xbf, 0xf1, 0xb5, 0x18, 0x51, 0xb8, 0xe9,
-  0x6a, 0x1a, 0x0a, 0x80, 0xeb, 0xe2, 0x30, 0xee, 0x7d, 0xb8, 0xd1, 0x93,
-  0x84, 0x3b, 0x1c, 0xdb, 0x35, 0x6a, 0xd7, 0x1e, 0xa3, 0xec, 0xff, 0x1d,
-  0xab, 0x07, 0x21, 0x4c, 0x6c, 0xc0, 0x00, 0xbf, 0xc0, 0x04, 0x1f, 0xc6,
-  0xdc, 0x46, 0x36, 0xf1, 0xeb, 0x50, 0x8d, 0x8d, 0xb6, 0x50, 0x6b, 0x48,
-  0xd7, 0x91, 0x8f, 0xda, 0xe9, 0xed, 0x79, 0x2d, 0x6f, 0xeb, 0xc6, 0xad,
-  0x77, 0xd3, 0x31, 0xfb, 0x6a, 0x9f, 0x0e, 0x27, 0x00, 0x10, 0x21, 0x64,
-  0xe4, 0xe6, 0x35, 0x2d, 0x3d, 0xde, 0x89, 0x8e, 0xae, 0xd7, 0x97, 0x5b,
-  0x97, 0x55, 0x3a, 0xbc, 0x71, 0x33, 0xc5, 0x73, 0xaa, 0xf5, 0xb7, 0x19,
-  0x44, 0xb2, 0x08, 0x33, 0xc2, 0x59, 0xe9, 0x33, 0x93, 0x0b, 0xee, 0x17,
-  0xa6, 0x76, 0xe0, 0x71, 0xe1, 0x69, 0x1f, 0x82, 0x70, 0xba, 0xae, 0xb2,
-  0xf6, 0x13, 0xb1, 0xad, 0x22, 0x3c, 0x9a, 0x0e, 0xca, 0x7c, 0x34, 0xd2,
-  0x49, 0xe7, 0x0a, 0xb9, 0x6f, 0x70, 0x77, 0x3e, 0x85, 0x3a, 0x79, 0x68,
-  0xdf, 0x1d, 0xfa, 0xd0, 0x20, 0x49, 0x10, 0x22, 0xd4, 0xeb, 0xb6, 0xf7,
-  0xa0, 0x8e, 0x91, 0x02, 0xf6, 0x2e, 0x02, 0x8e, 0xb6, 0x2d, 0x8d, 0xf3,
-  0xf9, 0x5a, 0xa2, 0xe4, 0x9a, 0x5e, 0xab, 0xdc, 0x06, 0xd0, 0xa5, 0xf3,
-  0xa3, 0x32, 0x7f, 0x8a, 0x70, 0x4e, 0x41, 0x5d, 0x83, 0x67, 0xa6, 0x57,
-  0xe3, 0xef, 0x16, 0x25, 0x86, 0x9b, 0xba, 0x68, 0xbf, 0x28, 0xd2, 0x3d,
-  0x23, 0x3c, 0xfa, 0x02, 0xe6, 0xde, 0xca, 0x09, 0x87, 0x32, 0x82, 0x13,
-  0x3f, 0xb0, 0xf9, 0x85, 0x9e, 0x52, 0x3b, 0x9b, 0xd1, 0xf3, 0x4e, 0x65,
-  0x28, 0x89, 0xc2, 0xbf, 0xf7, 0x5f, 0xd7, 0xd2, 0x49, 0x5e, 0x64, 0x52,
-  0xeb, 0x90, 0x43, 0x2e, 0xb1, 0x1d, 0x74, 0x98, 0xc2, 0x9c, 0x5d, 0xf1,
-  0x96, 0x7e, 0x4f, 0x39, 0xe7, 0xd9, 0x1f, 0xd0, 0x9c, 0x69, 0xa0, 0xfc,
-  0x9d, 0x1f, 0x82, 0xd1, 0xa5, 0x08, 0x85, 0x28, 0xa6, 0x92, 0x21, 0xdb,
-  0x8b, 0xe2, 0x7d, 0x09, 0xca, 0xa4, 0xcc, 0x9a, 0xe0, 0x95, 0xc8, 0x38,
-  0x75, 0x27, 0x0d, 0x7d, 0x2f, 0x59, 0xaf, 0xcc, 0xe3, 0x2b, 0x57, 0x44,
-  0x1a, 0xbe, 0x3a, 0x25, 0x49, 0xd1, 0x68, 0x17, 0x08, 0xdb, 0xf6, 0x51,
-  0xc9, 0x8a, 0xcb, 0x24, 0x80, 0x0f, 0xbc, 0x92, 0x8d, 0x0e, 0xee, 0x6b,
-  0x8b, 0x53, 0x4f, 0x24, 0x98, 0x7e, 0xa2, 0xd2, 0x1e, 0xc0, 0xf1, 0x73,
-  0x41, 0xa0, 0xaf, 0x22, 0x8a, 0x24, 0x93, 0xdb, 0xb6, 0xcb, 0xb9, 0x20,
-  0xbd, 0xee, 0x47, 0x6b, 0x64, 0x09, 0x4f, 0x05, 0x81, 0x5b, 0x20, 0x0a,
-  0xc1, 0x14, 0xc5, 0xa0, 0x40, 0x06, 0x1d, 0xca, 0xd8, 0x84, 0x2a, 0x30,
-  0xd1, 0xeb, 0x07, 0x16, 0x11, 0xcd, 0xa4, 0xc0, 0x25, 0xc7, 0x98, 0x9f,
-  0x96, 0x83, 0xee, 0x93, 0x08, 0x7b, 0x3f, 0xe8, 0x3d, 0x61, 0xfb, 0x65,
-  0xc3, 0x59, 0x08, 0xfa, 0x16, 0x3b, 0x23, 0x00, 0xf8, 0x52, 0x07, 0xad,
-  0xae, 0xa8, 0x86, 0x0f, 0x35, 0x0f, 0x47, 0xe2, 0xa8, 0xef, 0x7b, 0xe8,
-  0xf5, 0xdc, 0x85, 0xf8, 0xda, 0xf0, 0xa8, 0x46, 0x75, 0x97, 0x05, 0x8c,
-  0x02, 0x63, 0x56, 0x4f, 0x56, 0xfb, 0x08, 0x90, 0x1e, 0x1a, 0xc4, 0xdb,
-  0x4a, 0x3e, 0x1b, 0x39, 0xb9, 0xe2, 0x71, 0x35, 0x41, 0xa0, 0xd1, 0x39,
-  0x58, 0xe0, 0x21, 0x4c, 0xd9, 0x46, 0x5c, 0x44, 0x3a, 0xea, 0x21, 0xb7,
-  0x29, 0xa4, 0x03, 0x7a, 0x93, 0x23, 0x47, 0xb0, 0xb5, 0xa3, 0xf8, 0xd3,
-  0x8e, 0x01, 0x7d, 0x7f, 0x01, 0xc4, 0x54, 0x7e, 0x47, 0xb1, 0x73, 0x62,
-  0x05, 0x29, 0x92, 0xc6, 0x57, 0xa7, 0x63, 0xe0, 0xcb, 0xc5, 0x95, 0xcb,
-  0x9e, 0x3f, 0x74, 0x5b, 0xfd, 0x8e, 0x2a, 0xa5, 0xc6, 0x66, 0xe8, 0x89,
-  0x0a, 0x9e, 0x82, 0x39, 0x2f, 0xc3, 0x30, 0xbb, 0xe9, 0x2d, 0x97, 0xaa,
-  0x07, 0x99, 0x75, 0x9b, 0x49, 0xd3, 0x49, 0x52, 0x85, 0x86, 0x54, 0x95,
-  0xdd, 0x68, 0x05, 0x57, 0x81, 0xda, 0x0e, 0xe9, 0xc1, 0xa5, 0x84, 0x22,
-  0x88, 0xf2, 0xdd, 0x1f, 0xb7, 0x6c, 0xf0, 0xdb, 0xe3, 0xc4, 0xa9, 0xb9,
-  0x2f, 0x79, 0x93, 0x10, 0xb4, 0x64, 0xf7, 0xa4, 0xe7, 0xbf, 0xf8, 0x7d,
-  0xf9, 0xfb, 0xa6, 0xd6, 0x35, 0x47, 0xcd, 0x55, 0x5b, 0x43, 0x93, 0x96,
-  0x13, 0x5f, 0xec, 0x85, 0x89, 0x18, 0xa7, 0x7e, 0x0b, 0xa8, 0xbf, 0x68,
-  0x48, 0x21, 0x8c, 0xdc, 0xbd, 0x3b, 0x2e, 0xb2, 0x50, 0x48, 0x48, 0x0b,
-  0x9f, 0x10, 0x21, 0xf2, 0x65, 0x00, 0x1d, 0xbc, 0x4e, 0x5a, 0x5f, 0xbb,
-  0x22, 0x23, 0xf0, 0x04, 0x08, 0x9c, 0xd1, 0x20, 0x41, 0x0a, 0xea, 0x02,
-  0x49, 0xcf, 0xae, 0x94, 0x61, 0x4f, 0x50, 0x00, 0xcd, 0x9f, 0xf0, 0x00,
-  0x06, 0xad, 0xc9, 0xf5, 0xe5, 0x9d, 0xae, 0x4c, 0xef, 0x24, 0x11, 0xca,
-  0xad, 0x26, 0x74, 0xcb, 0x6f, 0xe5, 0xcf, 0xcf, 0xe1, 0xf5, 0xb0, 0xbd,
-  0x76, 0x47, 0x50, 0xf6, 0xaa, 0xd8, 0xe9, 0x61, 0x89, 0x54, 0xa7, 0x13,
-  0xa3, 0x01, 0x64, 0xfb, 0xdb, 0xfd, 0xd7, 0x94, 0x50, 0x8c, 0xb5, 0x08,
-  0xf9, 0xba, 0x48, 0x6d, 0xa5, 0xa1, 0x69, 0x87, 0x4d, 0x38, 0x34, 0xb2,
-  0xbf, 0x6d, 0x38, 0xb5, 0xb4, 0x9a, 0xfe, 0xc1, 0xd3, 0xd0, 0x81, 0x4a,
-  0x64, 0xd7, 0xf3, 0xf9, 0x36, 0x74, 0xba, 0xaf, 0xa5, 0x53, 0xf6, 0x6a,
-  0x56, 0x29, 0xd3, 0xd2, 0x01, 0xed, 0x5b, 0x08, 0x43, 0x29, 0xe6, 0x74,
-  0x68, 0xd2, 0x3f, 0x9b, 0xd1, 0x7e, 0x07, 0xa2, 0xf8, 0x28, 0x63, 0x93,
-  0x59, 0x7a, 0xda, 0xb5, 0x01, 0x30, 0xb9, 0x41, 0x64, 0x13, 0xa5, 0x4b,
-  0x04, 0x02, 0x25, 0x4a, 0x51, 0xfd, 0x8a, 0x3a, 0x7a, 0x1f, 0x39, 0xb7,
-  0xd5, 0xb4, 0x41, 0x00, 0x0f, 0xe3, 0xea, 0xdb, 0x61, 0xf1, 0xf6, 0x7f,
-  0xb4, 0xb5, 0x0a, 0xdb, 0x8b, 0xfd, 0xf7, 0x1c, 0xbd, 0x52, 0x29, 0xc4,
-  0x6c, 0xc5, 0xd1, 0x39, 0x77, 0x8d, 0xa5, 0x70, 0xee, 0xea, 0xc8, 0x12,
-  0xb0, 0x29, 0xa0, 0xbc, 0xd3, 0x46, 0xe8, 0x04, 0x4a, 0x9b, 0x62, 0xef,
-  0x92, 0xe0, 0x49, 0x15, 0x33, 0xe6, 0xd1, 0xf1, 0x5c, 0xb7, 0x3e, 0x85,
-  0xb0, 0xc2, 0x0c, 0xcb, 0xe5, 0xd2, 0x22, 0x58, 0x1c, 0x21, 0x4c, 0x6d,
-  0x49, 0xdf, 0x08, 0xc5, 0x5d, 0x82, 0x88, 0x65, 0xaa, 0x6c, 0x4a, 0x0a,
-  0xba, 0x8c, 0x62, 0xf9, 0x7d, 0x70, 0x74, 0x3f, 0x49, 0x4e, 0xbc, 0xe9,
-  0x57, 0xbd, 0x7f, 0x06, 0xb8, 0xea, 0x25, 0xd6, 0xfd, 0x06, 0xb4, 0x58,
-  0x3b, 0x73, 0x63, 0xdd, 0x61, 0xb6, 0xa1, 0xbd, 0xab, 0xfc, 0x1d, 0xc5,
-  0xaa, 0xb2, 0x4b, 0x75, 0xef, 0x3a, 0x8b, 0xaa, 0x5f, 0x26, 0xb9, 0x95,
-  0xbf, 0x1b, 0x5a, 0xda, 0x1a, 0x70, 0x29, 0x2d, 0xee, 0x85, 0x11, 0x54,
-  0x04, 0x20, 0x40, 0xf4, 0x4e, 0xd7, 0x9f, 0xc1, 0xc8, 0xd0, 0x2c, 0xf7,
-  0xdd, 0xd4, 0x6f, 0x13, 0xd6, 0x7e, 0x52, 0x45, 0xac, 0xb0, 0x63, 0xeb,
-  0x94, 0x89, 0x41, 0x62, 0x87, 0x64, 0xb1, 0x68, 0xcf, 0x57, 0xb2, 0x6f,
-  0x15, 0x6b, 0x20, 0x67, 0xc8, 0xd9, 0x34, 0xa9, 0x3f, 0x47, 0x55, 0xf7,
-  0x13, 0x67, 0x7b, 0x74, 0x57, 0x42, 0xe3, 0x75, 0xdb, 0xe4, 0xd3, 0x93,
-  0x52, 0xc8, 0x93, 0x20, 0x54, 0x88, 0x94, 0x3d, 0xbd, 0xd9, 0xbd, 0xac,
-  0x36, 0x00, 0xac, 0x4e, 0x19, 0xce, 0x1b, 0x4f, 0x9e, 0x73, 0x64, 0x14,
-  0x56, 0x21, 0xf9, 0x2d, 0xf9, 0xc8, 0xbb, 0x37, 0xaa, 0x71, 0x18, 0x9b,
-  0xaf, 0xdd, 0xa3, 0xfd, 0xbf, 0x80, 0x63, 0xd2, 0x61, 0xae, 0xbf, 0x3b,
-  0x6b, 0xf8, 0x8b, 0x36, 0xa3, 0x55, 0x5f, 0x7f, 0xbc, 0xae, 0xaf, 0x94,
-  0x55, 0x22, 0x52, 0xb7, 0x5d, 0xc8, 0xfb, 0x68, 0xbe, 0x4e, 0x35, 0x07,
-  0x49, 0x21, 0x86, 0x07, 0x60, 0xae, 0x48, 0x73, 0xe2, 0xfe, 0x77, 0x79,
-  0xd7, 0x57, 0x1d, 0x22, 0x77, 0xdb, 0x5c, 0x95, 0x00, 0xa4, 0x8b, 0xba,
-  0x37, 0xf4, 0x2e, 0x6b, 0x97, 0xa5, 0x2e, 0x69, 0x31, 0xb6, 0x22, 0xf6,
-  0xc4, 0x9f, 0x7e, 0x50, 0xeb, 0xa5, 0xa9, 0x16, 0xd7, 0x3e, 0x59, 0xe4,
-  0x44, 0x8b, 0xa4, 0x1c, 0x8a, 0xa8, 0x3c, 0xd1, 0x5a, 0x30, 0x15, 0x40,
-  0xc2, 0x8f, 0xc3, 0xfb, 0xd9, 0xd6, 0xbc, 0xb4, 0x3f, 0xa0, 0xf3, 0xec,
-  0xb3, 0xfb, 0x70, 0x7f, 0x21, 0xc0, 0x7e, 0x69, 0xf4, 0x88, 0xc0, 0xee,
-  0x6f, 0x96, 0xf3, 0x7b, 0x63, 0x1a, 0x37, 0x61, 0xb7, 0xc0, 0x65, 0xa4,
-  0xb0, 0x7c, 0x8e, 0x34, 0x74, 0x43, 0xdd, 0x94, 0xb1, 0x07, 0x7d, 0xbd,
-  0xd1, 0x8a, 0x2c, 0x35, 0xf1, 0x0d, 0x77, 0x03, 0xe9, 0xd2, 0x72, 0xaa,
-  0x14, 0xcf, 0x37, 0xc8, 0x18, 0x5e, 0x58, 0x82, 0x56, 0x88, 0xa0, 0xf0,
-  0x1e, 0xd5, 0x3b, 0x49, 0xc8, 0xab, 0x44, 0xf0, 0x7c, 0xf7, 0xa7, 0x5e,
-  0xb5, 0x1d, 0xaa, 0xf6, 0x3d, 0x4f, 0xa2, 0x1b, 0x18, 0x62, 0x26, 0x38,
-  0x91, 0x73, 0xf0, 0xd4, 0xa4, 0x02, 0x73, 0x99, 0x68, 0x0b, 0x84, 0xa6,
-  0x15, 0xbc, 0x41, 0x64, 0x1c, 0xac, 0x86, 0xc5, 0xc3, 0x07, 0x21, 0x4c,
-  0x37, 0x47, 0x5d, 0x64, 0x76, 0xe5, 0x35, 0xb7, 0x21, 0x31, 0x38, 0x06,
-  0xec, 0x23, 0xa4, 0x48, 0xb5, 0xba, 0xfd, 0x28, 0x49, 0x9a, 0x5b, 0xe4,
-  0x57, 0x5a, 0x9a, 0x3e, 0xc2, 0xf5, 0x7a, 0x94, 0xe9, 0xa5, 0xb1, 0x94,
-  0x91, 0x52, 0x58, 0x3f, 0xbb, 0xac, 0x16, 0x70, 0x96, 0xdc, 0xca, 0x0f,
-  0xc5, 0x6f, 0x1a, 0x72, 0x0b, 0xa2, 0x5d, 0x2e, 0x36, 0xf9, 0x67, 0xfe,
-  0x05, 0x0e, 0x9f, 0xd3, 0xf7, 0x1c, 0x8a, 0xcd, 0xdb, 0x8e, 0x86, 0xe8,
-  0xca, 0x37, 0x0b, 0x26, 0x64, 0xda, 0x14, 0x08, 0xc7, 0x6d, 0x8f, 0x17,
-  0xa1, 0xb5, 0x1b, 0x17, 0xf5, 0x79, 0xc6, 0x6f, 0xa7, 0xae, 0xaa, 0xe1,
-  0xe7, 0x20, 0xea, 0xac, 0xcf, 0x77, 0xca, 0xf5, 0xe3, 0x57, 0x70, 0x41,
-  0x02, 0x9d, 0x4d, 0x07, 0xd3, 0x18, 0xb7, 0x19, 0x5b, 0xe0, 0x89, 0x57,
-  0x00, 0xf3, 0xae, 0xf2, 0x98, 0x63, 0x9e, 0x3a, 0x3d, 0x66, 0xf9, 0xb1,
-  0xc1, 0x56, 0x10, 0x10, 0xac, 0x61, 0x66, 0xbb, 0x0a, 0xa5, 0x0e, 0x9b,
-  0xea, 0xc5, 0x7a, 0xd5, 0x97, 0xf8, 0x3b, 0x99, 0x12, 0x9a, 0x6b, 0x6a,
-  0x05, 0x34, 0x01, 0xfc, 0x32, 0x4b, 0xa8, 0x42, 0x68, 0x80, 0x3f, 0x4f,
-  0xc2, 0x8c, 0x50, 0xcd, 0xf7, 0xe9, 0xaa, 0x69, 0xfc, 0x53, 0xa5, 0x7d,
-  0x16, 0xcc, 0xf1, 0x3f, 0x05, 0x9c, 0x28, 0x7c, 0x57, 0x26, 0xed, 0xd9,
-  0x87, 0xea, 0x3b, 0x12, 0xaf, 0x49, 0xb9, 0xf9, 0xe5, 0xc9, 0xc3, 0xfc,
-  0xc6, 0x51, 0x0f, 0xcc, 0x54, 0xc3, 0xe9, 0xeb, 0x54, 0x33, 0x15, 0x06,
-  0x59, 0xf4, 0xdc, 0x5d, 0xd3, 0x5d, 0xd3, 0xe2, 0x83, 0xa5, 0x5b, 0x46,
-  0xda, 0x3c, 0x6f, 0x6f, 0xb1, 0x93, 0x93, 0x5a, 0x12, 0x9f, 0x38, 0x64,
-  0xbf, 0x06, 0x04, 0x42, 0x49, 0x96, 0xe7, 0x2b, 0xe4, 0xa3, 0xd5, 0x47,
-  0x64, 0xb5, 0x80, 0xd7, 0xea, 0x7e, 0x5d, 0x98, 0x36, 0x00, 0x14, 0x17,
-  0x02, 0x3e, 0x20, 0x73, 0x10, 0x39, 0x84, 0x68, 0x39, 0x82, 0x6c, 0x38,
-  0x12, 0x66, 0x5d, 0xf7, 0xc0, 0xe4, 0x3f, 0xea, 0xd2, 0xd8, 0xca, 0x48,
-  0xa9, 0x2c, 0x1c, 0x9f, 0xdb, 0x47, 0x6c, 0x90, 0xdc, 0x4f, 0xa9, 0xdd,
-  0x57, 0x16, 0xb0, 0x2e, 0xbe, 0xab, 0xd1, 0xb1, 0xdd, 0x74, 0xae, 0x5a,
-  0x19, 0x20, 0x51, 0xc4, 0xba, 0xdf, 0x6f, 0xa2, 0x3e, 0xff, 0x16, 0xb6,
-  0xcf, 0x71, 0xf0, 0xea, 0x3a, 0x47, 0x1f, 0xae, 0x1f, 0x61, 0xe9, 0xa1,
-  0xc0, 0x21, 0x7a, 0x94, 0xd5, 0x55, 0x88, 0x31, 0x51, 0x2a, 0x70, 0xd8,
-  0xe5, 0x47, 0x0b, 0x75, 0x2a, 0xd3, 0x8d, 0x45, 0x50, 0xe0, 0x85, 0xb2,
-  0x66, 0x6e, 0x39, 0x6a, 0x93, 0xbf, 0x13, 0xe3, 0xee, 0x38, 0xbf, 0xf8,
-  0xa4, 0xbc, 0x56, 0xad, 0x8b, 0xf3, 0x2e, 0x9b, 0x60, 0xf6, 0xcc, 0x6e,
-  0x8b, 0x8c, 0x01, 0x94, 0x4a, 0x51, 0xb1, 0xdb, 0x75, 0x57, 0xec, 0xcb,
-  0x84, 0xda, 0x34, 0xd3, 0x7c, 0xe8, 0x57, 0x12, 0x19, 0x8b, 0xe3, 0x5a,
-  0x31, 0xa1, 0xba, 0xee, 0x7d, 0x73, 0xe7, 0x78, 0x38, 0xdf, 0x7e, 0xf4,
-  0x0a, 0x5b, 0xd2, 0xfe, 0xdd, 0x87, 0xa9, 0x7f, 0x53, 0x64, 0xe5, 0x13,
-  0xac, 0x92, 0xdf, 0x48, 0x2d, 0xe4, 0x92, 0xbd, 0x3c, 0xc9, 0x64, 0x21,
-  0xa3, 0x36, 0x55, 0xa7, 0xb1, 0x67, 0x9d, 0x37, 0x65, 0x96, 0x5d, 0x54,
-  0x25, 0xbd, 0xf9, 0x4e, 0x79, 0xd4, 0xf7, 0x0c, 0xb5, 0xac, 0xf0, 0x22,
-  0xe2, 0x27, 0x4f, 0x6f, 0xd6, 0xb5, 0x97, 0x2e, 0x92, 0xee, 0x94, 0x24,
-  0x05, 0x26, 0xa4, 0xe7, 0xd3, 0x5f, 0x20, 0x85, 0xe7, 0x30, 0xc7, 0x32,
-  0xf2, 0x5d, 0x7a, 0x46, 0x85, 0xc7, 0x2a, 0xd5, 0x00, 0x64, 0x98, 0xa7,
-  0x0b, 0x40, 0x24, 0xa8, 0x6e, 0xe5, 0x4a, 0x2e, 0x48, 0x85, 0x2f, 0x8e,
-  0x5b, 0x4a, 0xaf, 0xad, 0x83, 0xe9, 0xcb, 0x04, 0x76, 0x63, 0x6b, 0x47,
-  0x18, 0x4a, 0xd9, 0xd8, 0x80, 0x7a, 0x08, 0x30, 0x56, 0x00, 0x1f, 0xc9,
-  0x08, 0x0b, 0x65, 0x8d, 0xdf, 0x80, 0x00, 0x00, 0xa9, 0xc9, 0xb7, 0x64,
-  0xe8, 0xed, 0x0a, 0xfd, 0xfa, 0x75, 0xff, 0x0a, 0x9e, 0x7e, 0xf9, 0xd7,
-  0x32, 0xd0, 0x9d, 0xd8, 0x96, 0x74, 0x6e, 0xbe, 0xb9, 0x92, 0x02, 0x39,
-  0x01, 0x7d, 0x79, 0x53, 0x7a, 0x20, 0x18, 0x98, 0xe8, 0x01, 0x42, 0xb9,
-  0xa3, 0x8f, 0x18, 0x09, 0x5e, 0x4b, 0x8e, 0x75, 0x18, 0x2f, 0xe9, 0x89,
-  0xae, 0xbc, 0xee, 0x5c, 0xa4, 0x40, 0xbe, 0xa3, 0xa7, 0xef, 0x29, 0x4c,
-  0xc4, 0x0e, 0x21, 0x1a, 0x94, 0xad, 0x96, 0x95, 0x62, 0xa4, 0x11, 0x05,
-  0xa0, 0x37, 0x15, 0x82, 0x85, 0xde, 0xb5, 0x56, 0xd6, 0x8e, 0x2b, 0x6d,
-  0x02, 0xcb, 0x64, 0x8b, 0xc7, 0xba, 0x62, 0xdb, 0xf8, 0x6f, 0x55, 0xd2,
-  0x3f, 0x6e, 0xc4, 0x7f, 0x5b, 0x62, 0x02, 0x3f, 0xf7, 0xfc, 0xd3, 0x94,
-  0xb7, 0x55, 0x75, 0x4b, 0x96, 0x66, 0x54, 0xb8, 0x9b, 0x58, 0xda, 0xac,
-  0xcf, 0xcd, 0x58, 0x92, 0x6c, 0xfc, 0x77, 0x29, 0xde, 0x78, 0x98, 0x5c,
-  0xc2, 0xdd, 0x96, 0x74, 0xec, 0x2d, 0xc7, 0x98, 0xb4, 0x77, 0x15, 0xcb,
-  0x42, 0xcb, 0x5f, 0x5f, 0x9d, 0x85, 0xca, 0x5c, 0x33, 0xbf, 0x68, 0xf6,
-  0xef, 0xa9, 0xe6, 0x0a, 0x31, 0x50, 0x5f, 0xff, 0x3d, 0x17, 0x90, 0x48,
-  0xb1, 0xef, 0x14, 0x33, 0x16, 0x0f, 0xdd, 0x59, 0xfe, 0x4a, 0x02, 0xae,
-  0xfb, 0x71, 0x46, 0x16, 0x46, 0x36, 0x34, 0xc9, 0x92, 0x41, 0x24, 0x93,
-  0x8e, 0x04, 0x14, 0x47, 0x62, 0x2a, 0x59, 0xdc, 0x4d, 0xa5, 0x8a, 0xa2,
-  0xc5, 0x65, 0x11, 0xe3, 0x15, 0x6b, 0x83, 0xe1, 0x67, 0x4e, 0x87, 0x7d,
-  0xc4, 0x3d, 0xd2, 0x51, 0x5b, 0x6e, 0x5d, 0x61, 0x05, 0xa9, 0x3e, 0x90,
-  0x11, 0x1a, 0x26, 0xbd, 0x0b, 0x92, 0x9e, 0x0b, 0x65, 0x02, 0x8a, 0xa8,
-  0x4a, 0x65, 0xd5, 0x3a, 0x3f, 0x6b, 0xf7, 0x14, 0x9d, 0x12, 0xb4, 0xe0,
-  0xac, 0x0b, 0x48, 0xe1, 0xba, 0x6e, 0xd0, 0x89, 0x73, 0xa5, 0xc9, 0xde,
-  0x0c, 0xb2, 0xf4, 0x67, 0x73, 0x6d, 0x8c, 0xc2, 0x4a, 0xd6, 0x07, 0xb0,
-  0x82, 0x85, 0xa7, 0x36, 0xba, 0x07, 0xf2, 0x0a, 0x01, 0x6c, 0x91, 0x78,
-  0xf7, 0x48, 0x4c, 0x3b, 0x1b, 0x2f, 0xcd, 0x9a, 0x2a, 0x45, 0xc4, 0x1d,
-  0xef, 0xfa, 0x63, 0x9b, 0xf0, 0x60, 0x2c, 0xcc, 0x6b, 0x27, 0x2f, 0xc7,
-  0x1b, 0x05, 0x1a, 0x5a, 0x81, 0x40, 0x22, 0xbe, 0xb1, 0xbf, 0xc2, 0x61,
-  0xd8, 0x00, 0xaa, 0xc3, 0x31, 0x9e, 0xe0, 0x00, 0xba, 0x06, 0x5a, 0xa4,
-  0x8c, 0x0a, 0x12, 0xb2, 0x51, 0xfc, 0x8b, 0xcc, 0x4e, 0x82, 0xa6, 0x00,
-  0x49, 0x70, 0x3d, 0xb9, 0x0a, 0x03, 0x07, 0x21, 0x1a, 0x94, 0xc5, 0xb2,
-  0x94, 0xc4, 0x41, 0x09, 0x08, 0x42, 0x52, 0x10, 0x05, 0xb8, 0xa9, 0x2f,
-  0x81, 0xe1, 0x84, 0xae, 0xb5, 0x77, 0x77, 0xab, 0x41, 0x28, 0x0b, 0x1c,
-  0xcd, 0xc7, 0xbd, 0xbc, 0x72, 0x60, 0xfb, 0x9f, 0x31, 0x2d, 0xdc, 0xb2,
-  0x23, 0xeb, 0xa6, 0x2b, 0xe4, 0xd4, 0x9b, 0x14, 0xf6, 0x26, 0xc3, 0xe3,
-  0xdd, 0xeb, 0xb3, 0x28, 0xdb, 0x67, 0xc4, 0xbb, 0x56, 0x64, 0x0e, 0x59,
-  0x17, 0x68, 0xae, 0x7c, 0xed, 0xdf, 0xb4, 0x66, 0x3b, 0x6d, 0x6e, 0xa3,
-  0xf2, 0xf6, 0xd8, 0x77, 0x8d, 0xab, 0xc9, 0x7e, 0xe3, 0x94, 0xf1, 0x8a,
-  0x88, 0x5c, 0x1c, 0x86, 0xdf, 0xf2, 0xd4, 0x19, 0xbf, 0xd4, 0x36, 0xa0,
-  0xc5, 0xad, 0x61, 0x61, 0xca, 0x6f, 0x56, 0xb0, 0x72, 0xea, 0xae, 0xa9,
-  0x3b, 0xab, 0x13, 0x97, 0x56, 0x2f, 0x0f, 0x57, 0xef, 0xe6, 0x44, 0x9e,
-  0xc9, 0xf9, 0xca, 0xd7, 0x5d, 0xc9, 0x63, 0xe5, 0x87, 0xdf, 0x0a, 0x53,
-  0xa9, 0xeb, 0xf9, 0x5b, 0xe6, 0x9f, 0x6a, 0xba, 0xe3, 0xa6, 0x79, 0x60,
-  0x56, 0x08, 0x1d, 0x03, 0x68, 0xb1, 0x3d, 0x36, 0x57, 0x5e, 0x85, 0x02,
-  0x5a, 0x4a, 0xd9, 0xf8, 0x65, 0x64, 0xb3, 0x73, 0xc7, 0x24, 0x4e, 0xd2,
-  0x13, 0x2d, 0x33, 0x3e, 0x54, 0x65, 0xcc, 0xb9, 0x52, 0xcd, 0x2b, 0x54,
-  0xfb, 0xd4, 0x73, 0x1b, 0xe4, 0x8e, 0x1c, 0xad, 0x40, 0x68, 0x94, 0xea,
-  0x89, 0x0a, 0xfe, 0x90, 0xad, 0xef, 0x45, 0x3e, 0x1b, 0xdb, 0x5b, 0xf7,
-  0x94, 0x84, 0xed, 0xf7, 0x42, 0xd6, 0xd2, 0x62, 0x12, 0x3f, 0xdd, 0xb6,
-  0xc8, 0xa6, 0x3c, 0xa5, 0x9d, 0x88, 0x07, 0x70, 0x81, 0xc6, 0x00, 0x37,
-  0x6a, 0xaf, 0xf2, 0xd6, 0x0b, 0xda, 0xd4, 0xc0, 0xd1, 0x51, 0xcf, 0xa7,
-  0x30, 0xf1, 0x47, 0xaf, 0xf8, 0x93, 0x05, 0xe7, 0x87, 0xe2, 0x3d, 0xa3,
-  0x0f, 0x88, 0xb2, 0x52, 0xd1, 0x20, 0xa6, 0x12, 0x07, 0x5b, 0xed, 0xe2,
-  0xd4, 0xfa, 0x80, 0x29, 0xe5, 0xb6, 0x7b, 0x3a, 0x5a, 0xcd, 0x80, 0x80,
-  0x01, 0x14, 0x94, 0x4a, 0x21, 0x68, 0xde, 0x90, 0xdb, 0x4f, 0x50, 0x48,
-  0x48, 0x91, 0x55, 0x65, 0xef, 0x21, 0xa1, 0xea, 0xf2, 0x31, 0xc5, 0x71,
-  0xb5, 0xe4, 0xc3, 0x41, 0x4f, 0xa4, 0x5d, 0x80, 0xb8, 0x54, 0x1c, 0x21,
-  0x1a, 0x94, 0xb5, 0xa2, 0x9c, 0xc4, 0x41, 0x12, 0x04, 0xc0, 0x11, 0x55,
-  0x59, 0x51, 0x4d, 0x75, 0xc6, 0xa9, 0xc2, 0xd6, 0x01, 0x40, 0x6b, 0xc9,
-  0x49, 0xa4, 0x02, 0x3a, 0x7e, 0xa3, 0x1d, 0x12, 0x8f, 0xf9, 0x7d, 0xc4,
-  0x3d, 0xf5, 0x35, 0x43, 0x8b, 0xb4, 0x1d, 0x77, 0x1f, 0x3d, 0xe5, 0x4b,
-  0x3b, 0x66, 0x08, 0xb7, 0xdc, 0x15, 0x57, 0x6c, 0xe4, 0xf4, 0xae, 0xa1,
-  0x9a, 0x58, 0xb1, 0x73, 0x09, 0xea, 0x08, 0xf4, 0x7a, 0xce, 0x23, 0x30,
-  0xe7, 0xfe, 0x86, 0x37, 0x60, 0xf3, 0xbe, 0xf9, 0xc4, 0x6a, 0x46, 0x01,
-  0x9e, 0x82, 0x37, 0x39, 0xc6, 0x78, 0xbe, 0x55, 0x9e, 0x66, 0x01, 0xbb,
-  0x4b, 0xc9, 0x47, 0x5b, 0xa8, 0x74, 0xfa, 0xcb, 0x25, 0x38, 0x1a, 0x37,
-  0x8b, 0x3e, 0x98, 0x58, 0x76, 0x8d, 0x97, 0x31, 0xef, 0xd3, 0x8f, 0xdf,
-  0xb8, 0x6e, 0xa2, 0x94, 0xcc, 0xed, 0xf8, 0xe8, 0xe9, 0x87, 0xe8, 0x72,
-  0xee, 0xbf, 0xc1, 0x24, 0x11, 0x4c, 0xa6, 0xad, 0x6c, 0xbe, 0xc7, 0x88,
-  0xa1, 0x20, 0x00, 0x8d, 0xb4, 0xc2, 0x28, 0xdd, 0xee, 0xec, 0x23, 0x52,
-  0x72, 0x2f, 0xb5, 0x75, 0x79, 0x57, 0x4d, 0x91, 0xa0, 0x1d, 0x69, 0xb0,
-  0xf3, 0x62, 0x3d, 0x9e, 0x40, 0x43, 0x4a, 0xab, 0x62, 0xbc, 0x5b, 0x39,
-  0x22, 0x99, 0xc0, 0x78, 0x9e, 0x4a, 0x60, 0xb7, 0xc3, 0x4e, 0xf4, 0x18,
-  0x61, 0xdc, 0x8f, 0x16, 0x33, 0xa8, 0xc0, 0xae, 0x86, 0x17, 0x2d, 0x88,
-  0x9c, 0xbb, 0x4d, 0x5b, 0x49, 0xb3, 0xf0, 0xd4, 0x4e, 0x8a, 0x25, 0x6c,
-  0xe5, 0x18, 0x39, 0x21, 0xc1, 0xc7, 0x5d, 0x66, 0x13, 0x34, 0x94, 0x30,
-  0x0d, 0xc2, 0x03, 0x10, 0x80, 0xc4, 0x20, 0x21, 0x6a, 0x58, 0x2e, 0x8a,
-  0x63, 0xef, 0xe7, 0x82, 0x85, 0x9b, 0xd0, 0x24, 0xc3, 0x71, 0x86, 0xfd,
-  0xee, 0x33, 0xc9, 0xe2, 0xb8, 0x66, 0x7f, 0xe1, 0xfd, 0xee, 0xb7, 0xda,
-  0x1d, 0xdf, 0xd3, 0x03, 0xa0, 0xba, 0x5d, 0xc6, 0xbe, 0xb6, 0xab, 0x6b,
-  0xbf, 0x84, 0xa1, 0x5e, 0x93, 0xbf, 0xae, 0xd1, 0x59, 0xca, 0xda, 0xbc,
-  0x69, 0xc0, 0x43, 0xf7, 0x00, 0x04, 0xb4, 0x47, 0x87, 0x35, 0x82, 0x75,
-  0x54, 0x42, 0xdc, 0xa2, 0x09, 0x44, 0xb9, 0x9e, 0x60, 0x06, 0x37, 0x4b,
-  0xc6, 0x73, 0xa1, 0x72, 0xcc, 0xc1, 0x97, 0x35, 0x3f, 0xbe, 0xae, 0xb0,
-  0xa0, 0x07, 0x21, 0x2a, 0x94, 0xad, 0xd9, 0x03, 0x02, 0x61, 0x20, 0x85,
-  0xa0, 0x45, 0xec, 0xc5, 0x62, 0xeb, 0x5c, 0x69, 0x77, 0x20, 0xb1, 0x83,
-  0x4d, 0x86, 0xc7, 0xa8, 0x4b, 0x4d, 0x59, 0xad, 0x26, 0x71, 0xfe, 0x52,
-  0xb8, 0x47, 0x8f, 0xe1, 0x72, 0x98, 0x21, 0xd7, 0x48, 0xb8, 0xcf, 0xc3,
-  0xf8, 0xfe, 0x7b, 0xc2, 0x7a, 0xc2, 0x92, 0x69, 0x81, 0xe4, 0x9e, 0xfe,
-  0xda, 0x77, 0x0c, 0x7b, 0x96, 0xb9, 0x0f, 0x6a, 0xe6, 0x87, 0x14, 0x3b,
-  0x8b, 0xd6, 0xfb, 0xa5, 0xd1, 0x98, 0x34, 0x74, 0x73, 0xe2, 0x7b, 0x47,
-  0x2f, 0x61, 0x59, 0xb9, 0x53, 0x7e, 0xf6, 0xaf, 0xd7, 0xa0, 0xdd, 0x0b,
-  0x4e, 0x60, 0xc3, 0xa7, 0xc8, 0x88, 0x33, 0x67, 0x67, 0x65, 0xbc, 0xc1,
-  0x4b, 0xe1, 0xbb, 0x47, 0xa7, 0xac, 0xab, 0x8f, 0x1c, 0xc1, 0xad, 0xbc,
-  0xa7, 0x4e, 0x5e, 0x58, 0xd4, 0x39, 0xae, 0x07, 0x18, 0xc4, 0x15, 0xf4,
-  0x12, 0x92, 0xf3, 0x39, 0xdb, 0x0a, 0x10, 0x81, 0x53, 0x7a, 0xe2, 0xa8,
-  0xd4, 0x6a, 0x7a, 0xba, 0x96, 0xbe, 0xd6, 0x10, 0xc0, 0x55, 0xcd, 0xae,
-  0x7e, 0x62, 0x6c, 0x78, 0xee, 0xed, 0x19, 0x98, 0xe9, 0xc4, 0x30, 0x70,
-  0x03, 0xbd, 0xa8, 0x1b, 0xec, 0xd9, 0xff, 0xa8, 0x2d, 0xd3, 0x41, 0x79,
-  0x0d, 0x7c, 0xe2, 0x6a, 0x40, 0x3b, 0x52, 0x29, 0x42, 0x54, 0x54, 0xdc,
-  0xaa, 0xcf, 0xdf, 0x3f, 0xaa, 0xb2, 0x85, 0x15, 0xf3, 0x42, 0x3c, 0x62,
-  0xc8, 0x2c, 0x45, 0x18, 0x26, 0x48, 0x17, 0x4d, 0x5b, 0x04, 0xf0, 0x12,
-  0x9b, 0xd8, 0x67, 0x85, 0xd3, 0x12, 0x28, 0xa5, 0xac, 0x04, 0xa5, 0xa5,
-  0x08, 0x05, 0xa0, 0x80, 0xc4, 0x20, 0x61, 0x50, 0x0c, 0x01, 0x81, 0xd7,
-  0xee, 0xa0, 0x5e, 0x08, 0xbf, 0x30, 0xcf, 0x5d, 0x73, 0xce, 0x7f, 0x74,
-  0xf4, 0xcf, 0x27, 0xce, 0x51, 0xa7, 0x81, 0xf7, 0x96, 0x5d, 0xdb, 0xad,
-  0x28, 0xd6, 0x06, 0x01, 0xee, 0x10, 0x91, 0x2f, 0xb0, 0xde, 0x40, 0x01,
-  0x12, 0x79, 0xa4, 0xbb, 0x8b, 0xc5, 0x70, 0xde, 0xb6, 0x4e, 0xdc, 0x33,
-  0x92, 0x41, 0xe1, 0x42, 0x28, 0x2b, 0x88, 0x85, 0xea, 0x70, 0x26, 0x86,
-  0xf5, 0x80, 0x18, 0xef, 0x10, 0xb8, 0x33, 0x35, 0x5b, 0x10, 0x0e, 0x21,
-  0x4c, 0xc7, 0x47, 0x5d, 0x62, 0x3a, 0xe4, 0x41, 0x97, 0x9a, 0x1a, 0xbe,
-  0x5d, 0x89, 0x4c, 0xb9, 0xe7, 0x56, 0x1f, 0xc0, 0x79, 0xf2, 0x32, 0xff,
-  0x8a, 0x3c, 0x9d, 0x4d, 0x63, 0xf6, 0x52, 0xfc, 0xde, 0x95, 0xa2, 0x05,
-  0x25, 0x4e, 0xb8, 0xb3, 0x85, 0x83, 0x17, 0xfa, 0x3e, 0x53, 0xf6, 0x4b,
-  0xe3, 0x86, 0x52, 0x31, 0x0a, 0x3f, 0xcf, 0x1f, 0xc7, 0xf1, 0x0a, 0x80,
-  0x11, 0xdc, 0xaa, 0x5a, 0x6f, 0x22, 0x78, 0x4a, 0xb6, 0x74, 0x17, 0xd3,
-  0xa6, 0x20, 0x7f, 0xb2, 0xa3, 0x59, 0xe4, 0xd3, 0x54, 0xbc, 0xe7, 0x1d,
-  0x57, 0x77, 0x4f, 0x92, 0x1d, 0x6f, 0xc6, 0x5a, 0x21, 0xbc, 0xb2, 0x71,
-  0xa0, 0x86, 0x42, 0x37, 0x9c, 0xa7, 0xb7, 0x69, 0x19, 0xbb, 0x47, 0x7a,
-  0xc1, 0x03, 0xf3, 0x16, 0x61, 0x3d, 0x5e, 0xbd, 0xbe, 0xb2, 0x47, 0xe0,
-  0x3e, 0x62, 0x9f, 0xe1, 0x95, 0x68, 0x03, 0xd5, 0xfb, 0x7b, 0x76, 0xdf,
-  0x00, 0x10, 0x71, 0x27, 0xe2, 0x98, 0x89, 0xd5, 0xdf, 0xf6, 0xaf, 0xe0,
-  0xd9, 0xd1, 0x59, 0xdc, 0x6a, 0x9d, 0xcd, 0x9f, 0x20, 0x2b, 0x94, 0xe9,
-  0xdb, 0x0f, 0x61, 0x25, 0xbc, 0x73, 0x91, 0x00, 0x16, 0xdc, 0x93, 0xc7,
-  0xa7, 0xa3, 0xf6, 0xba, 0xdf, 0xa4, 0x72, 0x48, 0x17, 0xae, 0xcd, 0xfa,
-  0x4f, 0x79, 0xf5, 0x9e, 0xb3, 0x1a, 0x35, 0xf0, 0x7f, 0x27, 0x92, 0x7b,
-  0xaa, 0xa0, 0x1e, 0xef, 0xca, 0xc1, 0xa9, 0xc2, 0xee, 0xf6, 0xfa, 0x91,
-  0xf3, 0x3c, 0x09, 0xf8, 0xf6, 0x70, 0x2c, 0x18, 0xdf, 0x09, 0xb9, 0x39,
-  0xaa, 0x99, 0x31, 0x4a, 0xd1, 0x06, 0xfa, 0xdf, 0xbd, 0x81, 0x1b, 0x40,
-  0x7b, 0x29, 0x73, 0xca, 0x8b, 0x09, 0x57, 0x79, 0xb9, 0xbe, 0x36, 0xcd,
-  0x37, 0x33, 0x68, 0xb4, 0x23, 0x94, 0x59, 0x5a, 0x77, 0xfd, 0xe9, 0x04,
-  0x8a, 0xa8, 0x14, 0x59, 0x0c, 0x60, 0x15, 0x84, 0xd5, 0xdf, 0xab, 0x52,
-  0xf1, 0xeb, 0x40, 0xe0, 0x75, 0x3a, 0x3d, 0x77, 0x57, 0xe0, 0xc6, 0x59,
-  0x4e, 0xae, 0x88, 0x1f, 0xec, 0xe5, 0xf4, 0xcc, 0x30, 0xc8, 0x02, 0x58,
-  0xbe, 0x91, 0x50, 0x04, 0x95, 0x14, 0x50, 0x4f, 0x02, 0xa4, 0xe8, 0x39,
-  0x98, 0x1c, 0xd1, 0x62, 0x06, 0x08, 0x21, 0x41, 0x05, 0xfc, 0x3d, 0xad,
-  0x5f, 0xd9, 0xcd, 0x85, 0x97, 0xe5, 0x02, 0x92, 0xa3, 0x1c, 0xaf, 0x3a,
-  0x80, 0xdf, 0x71, 0x7d, 0x26, 0xf5, 0xdd, 0x66, 0xa6, 0x95, 0x6f, 0x0c,
-  0x80, 0x66, 0xfc, 0x2f, 0x47, 0x3c, 0x68, 0xa8, 0xe6, 0xa2, 0x21, 0x34,
-  0x82, 0x3a, 0x75, 0x08, 0x4a, 0x76, 0xc8, 0x19, 0x0f, 0xc8, 0x15, 0x0b,
-  0x3b, 0x7f, 0x4a, 0xf4, 0xd9, 0x6e, 0x9b, 0xe2, 0x66, 0x5f, 0x9a, 0xe5,
-  0x3e, 0x8b, 0x76, 0x98, 0xd3, 0x54, 0x4a, 0x24, 0x61, 0x3e, 0x71, 0xba,
-  0x11, 0x05, 0x98, 0x07, 0x21, 0x7a, 0x94, 0xc5, 0xda, 0x08, 0x83, 0x16,
-  0xb3, 0x81, 0x4e, 0x12, 0x9b, 0x09, 0xa2, 0xfc, 0x8b, 0x40, 0x00, 0x0e,
-  0x7d, 0x61, 0x9e, 0xfa, 0x5b, 0x34, 0xfb, 0xcb, 0x8f, 0x8d, 0x2d, 0x50,
-  0x6c, 0x1c, 0x5b, 0xcb, 0xf9, 0x4e, 0x2d, 0x0d, 0x7c, 0xfa, 0x0f, 0x82,
-  0xee, 0xbc, 0x23, 0x31, 0x63, 0xa9, 0x0c, 0xe7, 0x33, 0xd2, 0x3c, 0x6d,
-  0xd7, 0x59, 0x93, 0xe8, 0x32, 0x8e, 0x73, 0xc7, 0x33, 0xc3, 0xce, 0xe6,
-  0xec, 0x3b, 0x0b, 0x73, 0x55, 0x1e, 0xf2, 0x1b, 0x68, 0x83, 0xea, 0x35,
-  0xa8, 0xfd, 0x33, 0xf0, 0xdb, 0x81, 0x8e, 0x09, 0xa1, 0xac, 0x1d, 0x9f,
-  0x44, 0x8b, 0x51, 0xc8, 0x39, 0xb6, 0xe7, 0xee, 0xbd, 0x45, 0xcc, 0xf0,
-  0x5a, 0xa3, 0xa0, 0xf6, 0x55, 0x5f, 0x91, 0xa7, 0xa9, 0xc5, 0x3b, 0xd8,
-  0xb1, 0xae, 0xcd, 0x5a, 0x89, 0x38, 0x9f, 0x4a, 0x0c, 0x84, 0x24, 0x95,
-  0xb9, 0x15, 0x99, 0xc9, 0x5e, 0x66, 0x52, 0x75, 0x7d, 0xfc, 0x5e, 0x2b,
-  0x38, 0xdb, 0x1c, 0x6a, 0x84, 0x63, 0xe8, 0x5b, 0x9d, 0x23, 0x2d, 0xf4,
-  0x06, 0x28, 0x06, 0xb2, 0x21, 0x08, 0x13, 0x80, 0x76, 0x5a, 0x6f, 0x0a,
-  0x5b, 0xc8, 0x9e, 0x97, 0x82, 0xbd, 0xe7, 0x33, 0x81, 0xb5, 0x54, 0x37,
-  0xb2, 0x51, 0xf0, 0xc7, 0x81, 0x92, 0x93, 0xbf, 0x18, 0xd2, 0xb3, 0xaf,
-  0x56, 0x5b, 0xe3, 0x97, 0xb2, 0x1f, 0x3f, 0xd6, 0xa9, 0xc6, 0x91, 0xb1,
-  0xd2, 0x37, 0xc3, 0xc6, 0x36, 0xe3, 0xdd, 0xca, 0x70, 0xac, 0xe9, 0x9f,
-  0xa4, 0x63, 0xd9, 0x6a, 0xd2, 0xfb, 0xd3, 0x34, 0xdf, 0x94, 0x61, 0x1c,
-  0xaa, 0x55, 0x4a, 0xb4, 0xea, 0x84, 0x11, 0x5d, 0xfc, 0x9a, 0x6d, 0x18,
-  0xca, 0x07, 0xf0, 0x04, 0xad, 0x9e, 0x08, 0x84, 0x11, 0xd8, 0x8e, 0xc1,
-  0x90, 0xb1, 0x80, 0x0f, 0x86, 0x96, 0x58, 0x21, 0x60, 0x0e, 0xee, 0x88,
-  0xe6, 0x41, 0x1d, 0x3a, 0x7b, 0x91, 0xb9, 0x9f, 0xb5, 0x16, 0x23, 0xf2,
-  0x8a, 0x37, 0xa4, 0x68, 0x1b, 0xb1, 0xa6, 0x28, 0x4b, 0x4e, 0x60, 0x2c,
-  0x89, 0x66, 0x53, 0x4c, 0x54, 0x0e, 0x1f, 0xdf, 0x99, 0x69, 0x86, 0x65,
-  0xb9, 0xb4, 0x2a, 0xca, 0x51, 0x4a, 0x28, 0x00, 0x4f, 0x6a, 0x51, 0x06,
-  0xf7, 0x47, 0xca, 0x74, 0x9e, 0xaf, 0xd4, 0xb6, 0x20, 0x24, 0xdd, 0xf4,
-  0x4d, 0x10, 0x07, 0x10, 0x2d, 0x52, 0x8c, 0x0c, 0xf5, 0x00, 0x02, 0x7c,
-  0x76, 0x89, 0x04, 0xee, 0x2d, 0x10, 0x58, 0x90, 0x51, 0x52, 0xe4, 0xb1,
-  0x11, 0xac, 0x16, 0x8d, 0x52, 0x84, 0xf1, 0x00, 0x00, 0x39, 0x0d, 0xe9,
-  0x6d, 0x10, 0xe0, 0x21, 0x1a, 0x94, 0xd5, 0xce, 0x8b, 0x61, 0x81, 0x30,
-  0x88, 0x68, 0x51, 0x19, 0x04, 0x02, 0x85, 0xa3, 0x2e, 0x0c, 0x11, 0xae,
-  0x2f, 0x80, 0x2c, 0x83, 0x1a, 0x2d, 0x95, 0xa9, 0x52, 0x38, 0x00, 0x74,
-  0x97, 0xdf, 0xfd, 0xfb, 0xa4, 0xf7, 0x46, 0xea, 0x71, 0x78, 0xb7, 0xd4,
-  0xba, 0x13, 0x51, 0x6d, 0x1b, 0xbc, 0x55, 0xeb, 0xff, 0x42, 0x3a, 0x29,
-  0xdb, 0xdb, 0xa0, 0x28, 0x30, 0xf8, 0x25, 0x66, 0x5f, 0xc0, 0x47, 0xdb,
-  0xf7, 0x22, 0x29, 0xf3, 0x76, 0x79, 0xba, 0x42, 0xd3, 0xd4, 0x5f, 0xd6,
-  0x67, 0xf0, 0xaf, 0x9e, 0x9e, 0xf9, 0x99, 0xf0, 0xf8, 0x28, 0xc8, 0x00,
-  0xf6, 0x07, 0xd2, 0x7e, 0x6b, 0xad, 0x73, 0x03, 0xea, 0x95, 0xb8, 0x29,
-  0xf9, 0x34, 0x91, 0xcf, 0xb7, 0xe5, 0xe9, 0x7c, 0x5a, 0x2f, 0x0b, 0xb7,
-  0xc7, 0xd9, 0x4a, 0xbb, 0x79, 0x51, 0xdb, 0x0e, 0x67, 0x95, 0x7a, 0x46,
-  0x7d, 0x4b, 0x98, 0x1a, 0xf3, 0xd9, 0x38, 0x1b, 0xb4, 0xf5, 0x58, 0xfb,
-  0x0c, 0x07, 0x06, 0xfb, 0x8e, 0x9d, 0xa0, 0x86, 0xb8, 0xd7, 0x9f, 0xb3,
-  0x9a, 0x87, 0x6e, 0xfd, 0x4f, 0x52, 0xe1, 0xac, 0xb6, 0x9a, 0x28, 0x1f,
-  0xdd, 0xa8, 0x83, 0x51, 0x87, 0xed, 0x12, 0xf0, 0x3d, 0x86, 0x4a, 0x6b,
-  0x8c, 0x71, 0x35, 0xdd, 0x7e, 0xe3, 0xbf, 0xc1, 0x94, 0x16, 0xa2, 0xe0,
-  0x7f, 0x37, 0xb2, 0xb3, 0x60, 0xb4, 0xcd, 0x03, 0xe4, 0xa4, 0x27, 0xa0,
-  0x14, 0x5a, 0x77, 0x32, 0x11, 0x17, 0x33, 0x2d, 0x60, 0xae, 0x8e, 0x0e,
-  0x46, 0x1c, 0xf9, 0xa6, 0x85, 0x60, 0x72, 0xfd, 0xac, 0x10, 0x4b, 0xae,
-  0xd6, 0x03, 0x46, 0x9b, 0xb3, 0xb7, 0xef, 0xba, 0x65, 0xfd, 0xda, 0xee,
-  0x19, 0x03, 0x57, 0x59, 0x2e, 0xc1, 0x6c, 0xae, 0x0e, 0x39, 0x81, 0x1c,
-  0xc4, 0x85, 0x82, 0x77, 0xbc, 0x84, 0x68, 0x98, 0xda, 0xb6, 0xb3, 0xeb,
-  0x2a, 0x40, 0xe3, 0xb3, 0xc8, 0xdc, 0x0c, 0xc0, 0x2c, 0x28, 0x3e, 0x3b,
-  0x7f, 0xa7, 0xdc, 0x7f, 0xf7, 0xec, 0xd9, 0x69, 0xab, 0xb2, 0x62, 0x8e,
-  0xc8, 0x01, 0xc8, 0x80, 0x62, 0x16, 0x19, 0x0c, 0x46, 0x02, 0x13, 0x99,
-  0x70, 0x20, 0x17, 0xec, 0xd0, 0xd0, 0x1c, 0x60, 0xb6, 0x52, 0xa7, 0x40,
-  0x00, 0x13, 0xde, 0xab, 0x7f, 0x99, 0x85, 0x40, 0x6a, 0xb5, 0x41, 0x23,
-  0xb6, 0x09, 0xf7, 0x97, 0x99, 0x04, 0xb3, 0x99, 0x72, 0xba, 0x85, 0xa0,
-  0xa9, 0x99, 0x82, 0x05, 0x92, 0x84, 0x09, 0x4a, 0x2a, 0xb1, 0xaf, 0xcd,
-  0xbd, 0xf7, 0x66, 0x9c, 0x26, 0xe3, 0xf3, 0x07, 0x6f, 0x3f, 0x2f, 0xe3,
-  0xfc, 0xfe, 0x39, 0xf1, 0xc3, 0xaf, 0x67, 0x92, 0xf9, 0xf0, 0xd7, 0x3c,
-  0x8c, 0x8e, 0x2d, 0x6d, 0x53, 0x56, 0x74, 0x03, 0xb3, 0xcd, 0x65, 0xe5,
-  0x6b, 0x8a, 0x4e, 0x9e, 0x8e, 0x78, 0xc0, 0x24, 0xec, 0x9a, 0xa6, 0x01,
-  0x75, 0x41, 0x9a, 0x3b, 0x3e, 0x3c, 0x24, 0xd1, 0x69, 0x1e, 0xcc, 0xcc,
-  0x2f, 0x94, 0x25, 0xc1, 0x6f, 0x95, 0x9b, 0x6c, 0x33, 0x00, 0x4a, 0xc0,
-  0xcb, 0x2f, 0x8a, 0x3e, 0x58, 0x09, 0x15, 0x79, 0x40, 0x1c, 0x21, 0x1a,
-  0x94, 0xcd, 0x9e, 0x9e, 0xc3, 0x41, 0x12, 0x04, 0xe6, 0x48, 0xd3, 0x2c,
-  0x53, 0x63, 0x8b, 0xe2, 0xf4, 0x96, 0x5a, 0x00, 0x58, 0x5f, 0x78, 0xac,
-  0x07, 0x8a, 0xf6, 0xb3, 0xab, 0x0c, 0xf0, 0x1e, 0xf6, 0xbc, 0xaf, 0xeb,
-  0x5c, 0xbd, 0x65, 0xf0, 0xb6, 0x7b, 0xca, 0xbe, 0x8e, 0xbb, 0x13, 0x7d,
-  0xaf, 0x0e, 0xcf, 0x32, 0xaf, 0xe6, 0xf2, 0x0c, 0xe9, 0x33, 0xb0, 0x76,
-  0x08, 0x5a, 0xff, 0x97, 0x66, 0xde, 0xc9, 0x45, 0xc6, 0xf7, 0xac, 0x87,
-  0x91, 0xdf, 0xff, 0x77, 0x9c, 0x7e, 0x93, 0xae, 0xe4, 0xe4, 0xb9, 0x05,
-  0x57, 0x7b, 0xf3, 0xb3, 0x5a, 0x21, 0xef, 0x4d, 0xb1, 0x14, 0x97, 0x1b,
-  0x99, 0x35, 0x9d, 0xb5, 0xa6, 0x6c, 0xe1, 0x6d, 0x19, 0x55, 0x0a, 0x43,
-  0x0c, 0x8d, 0x20, 0x4b, 0xd7, 0x2c, 0x64, 0x02, 0x54, 0x77, 0x4b, 0x6f,
-  0x55, 0x51, 0x77, 0xc0, 0xfe, 0xbd, 0xde, 0xfd, 0x2a, 0xf6, 0xce, 0x03,
-  0x78, 0x1d, 0x74, 0x54, 0xce, 0x1a, 0x79, 0x77, 0xa0, 0xa6, 0x71, 0x5d,
-  0xd1, 0xe2, 0xa9, 0x7d, 0x31, 0x48, 0x00, 0x25, 0x22, 0x50, 0x46, 0x63,
-  0x4d, 0x2d, 0x59, 0xce, 0x70, 0xa5, 0xd4, 0xb1, 0x83, 0x76, 0x74, 0x16,
-  0x29, 0x5f, 0x70, 0x2f, 0xe7, 0xdb, 0xba, 0xe6, 0xc3, 0xe1, 0x46, 0xc0,
-  0xb9, 0xda, 0x3f, 0x7d, 0x8b, 0xf4, 0x67, 0x8b, 0xd6, 0xf3, 0x62, 0x8f,
-  0x13, 0xe7, 0xf2, 0x01, 0xf2, 0x9f, 0xc7, 0x94, 0x41, 0x46, 0x77, 0xcb,
-  0xb5, 0x63, 0xf3, 0x87, 0xe1, 0xfd, 0x72, 0x5b, 0x75, 0x42, 0x5a, 0x94,
-  0x85, 0x01, 0xb8, 0x86, 0x08, 0x10, 0x10, 0x8d, 0x41, 0xbe, 0xaa, 0x6d,
-  0x4b, 0x70, 0x5b, 0x5a, 0xb5, 0xdc, 0x06, 0x98, 0x18, 0x1a, 0x49, 0xbf,
-  0xeb, 0xa6, 0x8c, 0xb6, 0x6b, 0xf0, 0x57, 0x2c, 0x2f, 0xeb, 0xbb, 0x2d,
-  0x51, 0x41, 0x65, 0x4d, 0x6e, 0x6a, 0x4b, 0xe3, 0xc2, 0x8c, 0xb4, 0x81,
-  0x5c, 0x02, 0x45, 0x37, 0xe9, 0xfe, 0x4e, 0x85, 0xb1, 0x80, 0x20, 0x5c,
-  0x00, 0x88, 0x22, 0xa2, 0x49, 0x50, 0x02, 0x82, 0xc2, 0xe6, 0x1a, 0x47,
-  0x7d, 0x22, 0x58, 0x5a, 0x1e, 0x5b, 0xa1, 0x59, 0x27, 0x9e, 0xf0, 0x04,
-  0x46, 0x88, 0x05, 0xfa, 0x67, 0x98, 0x15, 0x43, 0x51, 0x1f, 0x58, 0x56,
-  0x67, 0xcb, 0xf8, 0xf8, 0x40, 0x07, 0x7d, 0xca, 0x56, 0x39, 0x83, 0x07,
-  0x21, 0x1a, 0x94, 0xad, 0xc6, 0x89, 0x07, 0x47, 0x00, 0x16, 0x1b, 0xa4,
-  0xe9, 0xad, 0x4c, 0xea, 0x49, 0x60, 0xa4, 0x05, 0xb2, 0x77, 0x12, 0x47,
-  0xec, 0xba, 0x40, 0xcb, 0xf3, 0x72, 0x6d, 0x3d, 0x6c, 0x9d, 0x6d, 0x2d,
-  0xb6, 0x4c, 0x57, 0xfb, 0xe7, 0x89, 0xb7, 0x4a, 0xf8, 0x3e, 0x50, 0x60,
-  0x71, 0x66, 0xcb, 0xf4, 0x42, 0x31, 0xd0, 0x31, 0x17, 0xec, 0x8d, 0x92,
-  0x29, 0x58, 0xae, 0x7c, 0x90, 0xbc, 0xeb, 0x3f, 0xe9, 0xfd, 0x5b, 0x84,
-  0xc0, 0x28, 0x71, 0x70, 0xa7, 0xab, 0xbe, 0xd9, 0xd6, 0xfd, 0x63, 0xe0,
-  0xb4, 0xc6, 0x5c, 0xd5, 0x58, 0x96, 0x2f, 0xc5, 0x17, 0xf9, 0x8e, 0x16,
-  0x32, 0xbf, 0xd0, 0xbd, 0x8f, 0x95, 0x53, 0x72, 0xa5, 0xef, 0xfa, 0x8d,
-  0x5a, 0x33, 0xa1, 0xed, 0x3a, 0x76, 0x79, 0xbd, 0x0d, 0x7e, 0x55, 0x18,
-  0x73, 0x3b, 0x2a, 0xec, 0x60, 0x85, 0x12, 0x37, 0xe6, 0x0c, 0x79, 0x6f,
-  0x26, 0xcc, 0x2d, 0xc6, 0xa0, 0x32, 0x0a, 0x4f, 0x76, 0x38, 0xb4, 0xdf,
-  0x61, 0x6f, 0xa5, 0x11, 0x31, 0xd4, 0x45, 0xc7, 0x81, 0x82, 0xdb, 0x40,
-  0x84, 0xdf, 0x40, 0x86, 0xdf, 0x00, 0x13, 0x28, 0x80, 0xa6, 0xf9, 0x06,
-  0x2c, 0x48, 0x22, 0xd8, 0x88, 0x09, 0x68, 0x24, 0xee, 0xc8, 0x58, 0x64,
-  0xb4, 0x95, 0x5f, 0xc9, 0x02, 0xc7, 0x3c, 0x1c, 0x76, 0x3c, 0xaa, 0x0a,
-  0x65, 0x75, 0x2b, 0xca, 0x08, 0x4f, 0x1b, 0xd2, 0xcf, 0xea, 0xcc, 0x0a,
-  0x5e, 0x6b, 0x3f, 0x51, 0x5f, 0xef, 0x37, 0xcd, 0x22, 0xe4, 0x20, 0xea,
-  0xaf, 0xcd, 0x1c, 0x81, 0xbb, 0x50, 0x9c, 0xb7, 0x9b, 0x62, 0x90, 0x8d,
-  0x60, 0x58, 0x05, 0x86, 0x61, 0x9a, 0x1b, 0x30, 0x56, 0xa6, 0x4e, 0xd0,
-  0x10, 0x3f, 0x6e, 0x0a, 0x69, 0x72, 0xb3, 0x85, 0xec, 0x2d, 0x35, 0x99,
-  0x31, 0xff, 0x25, 0x88, 0xb6, 0x38, 0x45, 0x55, 0x58, 0xd5, 0x45, 0x6e,
-  0x71, 0x00, 0xa0, 0xb5, 0x9e, 0x4a, 0x7e, 0xa3, 0xbb, 0xbc, 0xa5, 0x9d,
-  0x90, 0x03, 0x72, 0x80, 0x50, 0xa2, 0x74, 0x08, 0x08, 0x46, 0x01, 0x52,
-  0xa8, 0x85, 0xbd, 0x9c, 0x5a, 0xe0, 0x00, 0x22, 0x9e, 0xb1, 0xcc, 0xbc,
-  0x57, 0xd3, 0x59, 0x63, 0xc7, 0xbc, 0x63, 0x4c, 0xe6, 0x5a, 0x73, 0x3d,
-  0xd6, 0x20, 0x49, 0xf5, 0x4b, 0x04, 0x70, 0xb0, 0xa8, 0x63, 0x46, 0xb4,
-  0xc8, 0x10, 0x92, 0x21, 0x38, 0x18, 0xd5, 0xce, 0x3f, 0x37, 0xbd, 0xc5,
-  0x09, 0x02, 0x00, 0x29, 0x04, 0x96, 0xe0, 0x38, 0x89, 0x6f, 0xff, 0x7c,
-  0xb3, 0x4b, 0xf2, 0x0a, 0xf5, 0x37, 0x73, 0x07, 0xfa, 0x4c, 0xcc, 0x37,
-  0x91, 0x66, 0x0b, 0x5d, 0xb1, 0x55, 0xb3, 0xa5, 0x6d, 0xe7, 0xce, 0x84,
-  0x6d, 0x8c, 0xc4, 0xf1, 0xac, 0x64, 0x72, 0x85, 0x31, 0xe8, 0xbc, 0x7e,
-  0x90, 0xbb, 0x04, 0x67, 0xa2, 0x0b, 0xbc, 0x74, 0xa2, 0x00, 0xfb, 0x79,
-  0x3f, 0x5e, 0xbd, 0xb9, 0x3e, 0xe2, 0x9b, 0xee, 0x8e, 0xe6, 0xd2, 0x9c,
-  0x88, 0xae, 0xbf, 0x50, 0xa5, 0xb9, 0xfa, 0xf9, 0x69, 0xe9, 0xb0, 0x38,
-  0x21, 0x1a, 0x94, 0xd5, 0x9e, 0xa6, 0xc3, 0x44, 0x09, 0x55, 0x22, 0xa5,
-  0xb5, 0x59, 0x79, 0xc9, 0x35, 0xa6, 0xb8, 0x94, 0x9a, 0xb9, 0x2a, 0xaf,
-  0x08, 0x51, 0xac, 0x9d, 0x42, 0x90, 0x0a, 0x05, 0x53, 0xc3, 0xae, 0x6e,
-  0xbd, 0x97, 0x7a, 0xe1, 0xfb, 0x98, 0x7f, 0x0f, 0x24, 0xc6, 0x18, 0x95,
-  0x27, 0xa5, 0x93, 0xd7, 0xfb, 0x16, 0xce, 0xd7, 0x7b, 0x7e, 0xab, 0x69,
-  0x19, 0x5b, 0xed, 0x5e, 0xc1, 0x6e, 0xbd, 0x5c, 0xbc, 0xdb, 0x98, 0x56,
-  0x29, 0xdf, 0xfb, 0xa7, 0xb2, 0x78, 0x34, 0xdb, 0xe5, 0x86, 0x17, 0x05,
-  0xeb, 0xe8, 0xf2, 0xe9, 0xdc, 0x85, 0x50, 0x1a, 0xed, 0x1a, 0xb9, 0x16,
-  0xa2, 0x76, 0x4f, 0x51, 0xf6, 0xbd, 0x8d, 0x5e, 0x10, 0x1f, 0xdb, 0xd5,
-  0xd5, 0xc7, 0xb2, 0xb2, 0x5e, 0x2e, 0x16, 0x05, 0x05, 0x54, 0x88, 0x5a,
-  0xac, 0x34, 0x5d, 0x08, 0x62, 0x07, 0x6b, 0xda, 0xd0, 0xe0, 0x3a, 0x05,
-  0x56, 0xa7, 0xf9, 0xbe, 0xb7, 0x52, 0xe3, 0x99, 0x64, 0xad, 0x0f, 0x95,
-  0xc5, 0xf5, 0x56, 0x5a, 0xa7, 0x42, 0xc8, 0xde, 0x32, 0x53, 0x6c, 0x26,
-  0x9f, 0x9e, 0x5c, 0x26, 0x09, 0x68, 0x06, 0x87, 0x9a, 0x50, 0x82, 0x2a,
-  0xa5, 0x92, 0x28, 0x0b, 0x85, 0x16, 0xe7, 0x4d, 0x72, 0x1c, 0xf7, 0x50,
-  0x87, 0x2e, 0x65, 0x45, 0xd3, 0xe4, 0xd9, 0xa9, 0x83, 0xcd, 0x0b, 0xcd,
-  0x74, 0x06, 0x02, 0x55, 0x5d, 0x73, 0xd0, 0xb6, 0x92, 0xe6, 0xb3, 0x1a,
-  0xaf, 0x0d, 0x99, 0xe5, 0xdc, 0x57, 0xa7, 0xec, 0xe4, 0x06, 0xcb, 0xd9,
-  0x74, 0xca, 0x7a, 0xee, 0x80, 0x0c, 0x1a, 0xec, 0x88, 0xfc, 0x7b, 0x3d,
-  0xe6, 0x06, 0xe2, 0x5c, 0x50, 0xca, 0xa4, 0xcb, 0x11, 0x6e, 0x3e, 0x28,
-  0x61, 0xb4, 0xe1, 0x92, 0xe4, 0xee, 0x9b, 0x88, 0x01, 0x68, 0x3f, 0x2b,
-  0x45, 0x40, 0x3d, 0x1a, 0xa1, 0x6b, 0x80, 0x96, 0xa3, 0xb2, 0x00, 0x6a,
-  0x21, 0x23, 0x10, 0x52, 0x02, 0x11, 0xab, 0x2e, 0x6f, 0x4c, 0xb1, 0x41,
-  0xe6, 0xd2, 0x43, 0xcc, 0xa0, 0x58, 0xd6, 0x58, 0x91, 0x17, 0x24, 0x00,
-  0x3a, 0x05, 0x53, 0xc3, 0xb3, 0x6e, 0x07, 0x7d, 0xf1, 0x9a, 0xd6, 0x30,
-  0xe3, 0xbc, 0x5a, 0xd8, 0x78, 0x9a, 0x83, 0x1c, 0xf9, 0x3b, 0x0e, 0xcb,
-  0xd8, 0xa0, 0x44, 0x52, 0x27, 0x03, 0x54, 0xbd, 0x57, 0x37, 0x42, 0xf5,
-  0xfa, 0xf5, 0xca, 0x40, 0x7e, 0x1c, 0xf8, 0x0a, 0x7d, 0xcf, 0x89, 0xcb,
-  0xb6, 0xf9, 0x9e, 0xae, 0xbb, 0x70, 0xa7, 0x8f, 0x74, 0xbd, 0x71, 0x85,
-  0x93, 0x5b, 0x83, 0x25, 0x6b, 0x4a, 0x3b, 0xb2, 0x3a, 0x9a, 0x10, 0xb2,
-  0x3a, 0xb4, 0x38, 0x21, 0x09, 0x80, 0xf9, 0xce, 0x61, 0x74, 0xcb, 0x76,
-  0x0a, 0x41, 0x49, 0xa3, 0xbb, 0x35, 0xff, 0x8e, 0xe9, 0xfb, 0xeb, 0x81,
-  0x97, 0x1a, 0x87, 0x4f, 0xa9, 0xd3, 0x07, 0x4f, 0x76, 0x1f, 0x47, 0x4f,
-  0x74, 0x88, 0x22, 0xd5, 0x01, 0x94, 0x00, 0x05, 0x7f, 0x88, 0x38, 0x21,
-  0x1a, 0x94, 0xc5, 0xce, 0x8c, 0xc3, 0x24, 0x89, 0x40, 0x1a, 0xca, 0x14,
-  0x13, 0xa0, 0xbb, 0x97, 0xaa, 0xa0, 0xb8, 0xa3, 0x59, 0x62, 0x92, 0x1c,
-  0x73, 0xc7, 0x28, 0x11, 0x2c, 0xa6, 0x78, 0xb7, 0xc5, 0x4d, 0xbe, 0xbe,
-  0xd1, 0xd8, 0xbe, 0x25, 0x83, 0x98, 0xf7, 0x25, 0xe9, 0x82, 0x67, 0x1c,
-  0x98, 0x0b, 0xb0, 0x9b, 0xb7, 0x2b, 0x82, 0x34, 0x8b, 0xe7, 0x0a, 0xab,
-  0x86, 0x6d, 0xd8, 0x7d, 0x3d, 0xff, 0x69, 0xe6, 0xf5, 0xf0, 0x0d, 0x25,
-  0x65, 0xb4, 0xe2, 0x36, 0xd6, 0xa3, 0xb0, 0x61, 0xd2, 0x37, 0x30, 0x52,
-  0x8e, 0x16, 0xbe, 0xfb, 0x6c, 0xec, 0x7e, 0x4a, 0xca, 0x59, 0xcc, 0xce,
-  0x22, 0xf9, 0x8d, 0xee, 0x1b, 0x48, 0x3e, 0x2f, 0xba, 0x96, 0x2f, 0x0c,
-  0x58, 0xab, 0x2a, 0x35, 0xda, 0x7c, 0x2b, 0x14, 0xcc, 0xd9, 0x2b, 0x7d,
-  0x11, 0x6e, 0xcd, 0x73, 0x85, 0xe5, 0xef, 0x75, 0xa8, 0x61, 0x8b, 0x9d,
-  0x62, 0x12, 0xab, 0x12, 0xbb, 0xf9, 0xd7, 0xd4, 0xd1, 0xd6, 0x2b, 0xf8,
-  0xab, 0xab, 0x26, 0x3d, 0xb2, 0xa5, 0xb5, 0x42, 0x0a, 0x54, 0xa6, 0x6a,
-  0x3e, 0x32, 0x06, 0x87, 0x4a, 0x8e, 0xe7, 0xa5, 0x06, 0x77, 0x51, 0x1c,
-  0x62, 0x55, 0x71, 0xa0, 0xd9, 0xd4, 0x5d, 0x6a, 0xa1, 0x29, 0x52, 0x77,
-  0xdb, 0x86, 0x7c, 0x69, 0x64, 0x31, 0xcf, 0x4b, 0x14, 0x66, 0x36, 0x2b,
-  0x59, 0x76, 0x61, 0x28, 0x40, 0x03, 0xce, 0xc0, 0x8e, 0x8c, 0x72, 0xa3,
-  0x38, 0x13, 0x06, 0x10, 0xa2, 0x08, 0x79, 0x87, 0x39, 0xf0, 0xf5, 0x58,
-  0x79, 0x0d, 0x78, 0xfd, 0xca, 0x66, 0xd3, 0x2b, 0x5c, 0xe4, 0x17, 0x1f,
-  0xba, 0x7b, 0xf8, 0x9c, 0x1f, 0x21, 0x12, 0x37, 0xf1, 0xbf, 0x96, 0x4d,
-  0x72, 0xd7, 0xf1, 0x1d, 0xca, 0xcd, 0x10, 0x4c, 0xd2, 0x50, 0xc0, 0x34,
-  0x10, 0x44, 0x4a, 0xba, 0x66, 0xe4, 0xb0, 0x0f, 0xf1, 0x34, 0x8d, 0x65,
-  0x8a, 0x48, 0x71, 0xcf, 0x1c, 0xa0, 0x44, 0xb2, 0x97, 0xd6, 0x38, 0xf6,
-  0xb3, 0x8c, 0xf2, 0x5e, 0xa6, 0xdb, 0x7e, 0xbd, 0xbc, 0x33, 0x3c, 0xb2,
-  0x47, 0x1e, 0xf6, 0x3c, 0x40, 0x0c, 0x3c, 0x02, 0xd4, 0xdf, 0x3f, 0xfe,
-  0xfb, 0xb8, 0xff, 0xa7, 0x87, 0xad, 0xe0, 0x00, 0x36, 0x80, 0x96, 0x80,
-  0x00, 0x69, 0x0e, 0x21, 0x2a, 0x94, 0xb5, 0xa2, 0x8f, 0x64, 0x81, 0xb1,
-  0x88, 0x88, 0x22, 0x40, 0x0b, 0x65, 0xef, 0x52, 0x85, 0xcd, 0x38, 0x67,
-  0x13, 0x40, 0x00, 0x0d, 0x27, 0xea, 0x1d, 0x57, 0xc9, 0x04, 0x50, 0x7e,
-  0xeb, 0xa7, 0x49, 0x09, 0x13, 0xf1, 0x9c, 0x9c, 0x51, 0xf6, 0x6f, 0x58,
-  0xe6, 0x6f, 0x74, 0xe2, 0xaa, 0xf4, 0x1f, 0x5b, 0x91, 0xdb, 0x2a, 0x21,
-  0x58, 0xd9, 0xeb, 0x56, 0xdc, 0xde, 0x48, 0x3a, 0x07, 0x51, 0xda, 0x1e,
-  0xfb, 0x58, 0xbe, 0xd2, 0x24, 0x54, 0xec, 0xad, 0x9b, 0xc8, 0x2c, 0x2a,
-  0x33, 0x43, 0xfa, 0xab, 0x87, 0x0a, 0xaa, 0xa2, 0x56, 0x1b, 0x82, 0xfa,
-  0x31, 0xf1, 0x84, 0xee, 0x3c, 0xd4, 0x97, 0xce, 0x66, 0xdb, 0xde, 0x2d,
-  0x9b, 0x4d, 0x9f, 0x6d, 0x78, 0x58, 0x4f, 0xf3, 0x23, 0x48, 0xd0, 0x00,
-  0x96, 0xae, 0xe5, 0xc0, 0xbf, 0x9f, 0xb0, 0xde, 0x7c, 0x57, 0x7f, 0x75,
-  0x5a, 0xa5, 0x91, 0xd2, 0xf3, 0x52, 0xac, 0xd0, 0xe9, 0xc7, 0x17, 0x7b,
-  0x26, 0x07, 0x12, 0xae, 0xb9, 0x01, 0x00, 0x88, 0x8d, 0x8d, 0x98, 0xc2,
-  0x0b, 0x07, 0x62, 0x78, 0x18, 0x43, 0x9e, 0xdd, 0xcf, 0xcd, 0xe0, 0x41,
-  0xcf, 0x19, 0x42, 0x48, 0x34, 0xea, 0xa9, 0xff, 0x19, 0x59, 0xf7, 0x87,
-  0xd2, 0x11, 0x78, 0x47, 0xba, 0x4d, 0xed, 0x92, 0xa5, 0x96, 0xce, 0xfd,
-  0x30, 0x8a, 0xf6, 0x04, 0xae, 0x6f, 0x86, 0x45, 0xe4, 0xb2, 0xe7, 0xfc,
-  0x7b, 0xc3, 0x61, 0x01, 0x66, 0xb0, 0xed, 0xdf, 0x2e, 0x0e, 0xfc, 0x00,
-  0xa4, 0x63, 0x5c, 0x2b, 0xc7, 0x61, 0x30, 0x02, 0xf5, 0x68, 0x73, 0x7a,
-  0x1b, 0x6b, 0x6d, 0x74, 0xf7, 0xfd, 0xb9, 0x75, 0x97, 0xc6, 0x6f, 0xc3,
-  0xa0, 0x00, 0x25, 0x2c, 0xd0, 0x64, 0x18, 0xec, 0x00, 0x32, 0x02, 0xde,
-  0xd0, 0x74, 0x58, 0x08, 0xa0, 0x61, 0xfd, 0xc4, 0x82, 0xf5, 0xda, 0xbd,
-  0x05, 0x05, 0xf8, 0x67, 0xf5, 0xb3, 0xf4, 0xb9, 0x35, 0x1e, 0xdf, 0xc5,
-  0xc5, 0x67, 0xbd, 0x41, 0x27, 0x49, 0x6c, 0x43, 0xad, 0x7a, 0xdc, 0xfe,
-  0xa1, 0x9a, 0xaa, 0xbf, 0x9a, 0x54, 0xf2, 0x7e, 0x31, 0xd9, 0xae, 0xf6,
-  0xe3, 0x08, 0xb0, 0x1e, 0x4f, 0x16, 0xdf, 0xbd, 0xde, 0xe8, 0x01, 0x93,
-  0x00, 0x05, 0x46, 0xee, 0x21, 0x28, 0x00, 0x4a, 0x9d, 0x0c, 0x08, 0x40,
-  0x11, 0x5c, 0x4a, 0x55, 0xdb, 0x6b, 0x78, 0xb2, 0x11, 0x22, 0x7f, 0xd0,
-  0x0c, 0x19, 0x13, 0xd1, 0xa9, 0x73, 0x8c, 0x30, 0xd2, 0xb5, 0x9f, 0x08,
-  0x94, 0xfb, 0xed, 0x86, 0xd8, 0x15, 0x29, 0x13, 0xc9, 0x75, 0x42, 0xc3,
-  0x2c, 0x0a, 0x7a, 0xf0, 0x07, 0x21, 0x4c, 0xcd, 0x46, 0xdd, 0x62, 0x3a,
-  0xea, 0x31, 0x96, 0xaa, 0x23, 0x17, 0x78, 0x71, 0x6e, 0xb8, 0x25, 0x1f,
-  0xc2, 0xdd, 0x3a, 0x21, 0xfd, 0x27, 0xb5, 0x79, 0xd4, 0x41, 0xfc, 0x5d,
-  0x9e, 0x69, 0x7a, 0xb0, 0xa2, 0x43, 0xc8, 0x6b, 0x77, 0xd8, 0xe6, 0x24,
-  0x29, 0x56, 0x91, 0x91, 0x61, 0xea, 0x38, 0x4d, 0x5f, 0x9f, 0x69, 0xf5,
-  0xa5, 0x9b, 0x86, 0x98, 0xa5, 0x56, 0x8f, 0xf3, 0x0b, 0xea, 0x2f, 0xf2,
-  0xec, 0x2b, 0x1c, 0x46, 0x3d, 0xf5, 0xf8, 0x9a, 0xcb, 0x63, 0xcb, 0x7f,
-  0x09, 0x7e, 0x3d, 0x95, 0x02, 0x0c, 0xde, 0xeb, 0x94, 0x2e, 0x94, 0x68,
-  0xbd, 0x3a, 0xf5, 0x22, 0x09, 0x59, 0x40, 0x31, 0x0d, 0xa2, 0x03, 0x07,
-  0x0e, 0xc5, 0xbe, 0x3d, 0xb3, 0xcb, 0x32, 0xdc, 0x8a, 0xd5, 0x9a, 0xdc,
-  0x5f, 0x76, 0x78, 0x4a, 0x29, 0x4c, 0x96, 0x83, 0x82, 0xeb, 0x40, 0xe0,
-  0xb1, 0x69, 0xe4, 0x98, 0xdd, 0x3b, 0x12, 0x80, 0x8d, 0x69, 0x02, 0x79,
-  0xac, 0x48, 0x9d, 0x35, 0xff, 0x7e, 0xfe, 0xf7, 0xeb, 0xaf, 0xd2, 0x71,
-  0x3c, 0xae, 0x13, 0xa2, 0xa9, 0xca, 0xb6, 0xec, 0xfe, 0xbd, 0x02, 0xff,
-  0xb7, 0x2d, 0x7d, 0x40, 0xb0, 0x4c, 0x1a, 0x4c, 0xe2, 0xea, 0x65, 0x64,
-  0x3d, 0x2f, 0xde, 0xfb, 0xa3, 0xbc, 0x69, 0x56, 0xbb, 0xd4, 0x32, 0xbe,
-  0x7c, 0x7d, 0xb6, 0x39, 0x23, 0x17, 0xc9, 0xf2, 0x6b, 0xb4, 0xaa, 0xbc,
-  0x29, 0x36, 0x3a, 0x7a, 0x24, 0x58, 0x5d, 0x00, 0x10, 0x5c, 0x42, 0x68,
-  0x26, 0x8a, 0xb2, 0x3a, 0x52, 0x25, 0x91, 0x9c, 0x6b, 0x4f, 0x0f, 0x25,
-  0x2f, 0x2a, 0x5a, 0xc9, 0x4d, 0x18, 0xa3, 0x3b, 0x2c, 0x47, 0xda, 0x66,
-  0x38, 0x0d, 0xe1, 0xd6, 0xda, 0x0d, 0xb3, 0x50, 0x78, 0x45, 0xf1, 0xc5,
-  0xf0, 0x3f, 0xb2, 0x9f, 0x71, 0xa6, 0x9f, 0xd9, 0xc3, 0xad, 0x71, 0x4b,
-  0x3f, 0x61, 0xec, 0x79, 0x01, 0x15, 0xf2, 0xdd, 0xfb, 0xe9, 0x52, 0x70,
-  0x38, 0xcd, 0xfe, 0x3b, 0x01, 0xf6, 0xb0, 0xe3, 0x28, 0x8d, 0x28, 0x62,
-  0x08, 0x32, 0xde, 0x4a, 0xc8, 0x9c, 0x05, 0x33, 0x91, 0x5d, 0x24, 0x54,
-  0x46, 0x1e, 0x46, 0xe5, 0x6f, 0xef, 0xd0, 0xe2, 0x69, 0x9d, 0xa7, 0x22,
-  0x6a, 0xa1, 0xbe, 0xcb, 0xe7, 0xcc, 0x3f, 0xea, 0xfc, 0x97, 0x08, 0x7e,
-  0x27, 0xd8, 0x8a, 0xa7, 0x4f, 0x1d, 0x2e, 0x92, 0x46, 0xd3, 0x90, 0xca,
-  0x90, 0x3d, 0xc7, 0x43, 0x90, 0x3c, 0x79, 0x1e, 0x5f, 0xa3, 0x6e, 0x8b,
-  0x12, 0xa2, 0x8c, 0xdb, 0x6a, 0x5c, 0xd3, 0x4c, 0xba, 0x0d, 0x58, 0x39,
-  0xc1, 0x82, 0x86, 0xd4, 0xde, 0xfd, 0xbd, 0x0c, 0xf8, 0xcc, 0x6d, 0x38,
-  0x85, 0x11, 0x59, 0x9c, 0xd2, 0x8d, 0x36, 0x25, 0xe5, 0x7d, 0xc9, 0x54,
-  0x37, 0x6b, 0x94, 0xb8, 0x5a, 0xce, 0x84, 0x6a, 0x5e, 0xb8, 0xe9, 0xa2,
-  0x33, 0x14, 0xda, 0x80, 0x44, 0xa4, 0x80, 0xba, 0x6b, 0xdd, 0x0d, 0xd7,
-  0x55, 0x8a, 0x31, 0x42, 0x0c, 0xfd, 0x0c, 0x04, 0x79, 0xcd, 0x5b, 0xa3,
-  0x48, 0x63, 0x64, 0x38, 0x21, 0x7a, 0x8c, 0x00, 0x7f, 0xff, 0xff, 0xff,
-  0x2d, 0x6d, 0xa4, 0xb0, 0xc8, 0x82, 0xd0, 0x82, 0xa3, 0x55, 0x92, 0xb7,
-  0x7a, 0x9c, 0x1a, 0xad, 0x00, 0x40, 0x50, 0x13, 0x12, 0x32, 0x1a, 0xb1,
-  0xfa, 0x48, 0x05, 0xdf, 0x5b, 0xfa, 0xf1, 0x21, 0x43, 0xf7, 0x5a, 0x89,
-  0x15, 0x10, 0xf7, 0xd4, 0xac, 0x0c, 0x37, 0xdc, 0x1f, 0x12, 0x56, 0xa6,
-  0x8c, 0xfb, 0xfd, 0xb7, 0x70, 0xb7, 0xdb, 0x39, 0x89, 0xd9, 0x98, 0x5f,
-  0xba, 0xc3, 0x94, 0xb9, 0x8f, 0x88, 0xc1, 0xf5, 0x6e, 0x47, 0x57, 0xae,
-  0xd7, 0xe7, 0x33, 0xbf, 0x17, 0xd7, 0x58, 0x7e, 0x8a, 0x47, 0xca, 0xed,
-  0x1d, 0xe7, 0x8d, 0x61, 0x39, 0xe9, 0x45, 0x18, 0x69, 0xef, 0x71, 0xb6,
-  0xf8, 0xb0, 0x19, 0x7f, 0x2c, 0xea, 0xb7, 0x87, 0xee, 0x03, 0x17, 0xe4,
-  0x49, 0xda, 0x37, 0x2a, 0xc3, 0xe5, 0x77, 0x07, 0x5d, 0x6e, 0xd1, 0xfc,
-  0x62, 0xea, 0x43, 0xd9, 0x0c, 0x00, 0x2a, 0xad, 0x49, 0x45, 0xeb, 0x91,
-  0xa6, 0x55, 0x21, 0x40, 0x90, 0x4b, 0x7a, 0x4a, 0x64, 0x7c, 0x92, 0x97,
-  0xc9, 0x94, 0xe0, 0xae, 0x78, 0x40, 0x5d, 0xf0, 0x9d, 0x45, 0x7e, 0x69,
-  0x40, 0x15, 0x64, 0x76, 0x5b, 0x7a, 0xb2, 0x49, 0x65, 0xae, 0x56, 0xc1,
-  0x11, 0x99, 0x66, 0xdd, 0xd1, 0x91, 0x38, 0x02, 0xf6, 0x15, 0x84, 0xe2,
-  0x46, 0x16, 0x8a, 0x80, 0xf5, 0x11, 0xcc, 0x4c, 0x56, 0xf0, 0xa2, 0x63,
-  0x8d, 0x41, 0x37, 0x6c, 0x82, 0x10, 0xc0, 0x28, 0x51, 0x8a, 0x30, 0x26,
-  0x91, 0x6e, 0xda, 0xae, 0xaf, 0xc8, 0x5d, 0x8b, 0x00, 0x09, 0xbe, 0x17,
-  0xef, 0x77, 0x44, 0xa5, 0x0c, 0x8c, 0x98, 0x84, 0x9f, 0x2c, 0x94, 0x68,
-  0x3c, 0x71, 0xdd, 0x4f, 0xfd, 0xa9, 0x50, 0x75, 0x31, 0x7d, 0x07, 0xd7,
-  0xb5, 0x8e, 0xfc, 0xe6, 0xaf, 0xa4, 0xec, 0xce, 0xe2, 0x9b, 0x77, 0xae,
-  0xb2, 0xe6, 0x2c, 0xf3, 0xad, 0x64, 0x8b, 0x56, 0x25, 0x60, 0x27, 0x26,
-  0xa5, 0xfb, 0x68, 0x66, 0xd6, 0xa9, 0xa1, 0x19, 0xa3, 0x51, 0x95, 0x95,
-  0xcf, 0xb2, 0x95, 0x6c, 0x81, 0x1a, 0x2d, 0x31, 0x4e, 0x2a, 0x6d, 0x40,
-  0xd0, 0xaf, 0x0b, 0xd8, 0x1a, 0x24, 0xb6, 0x97, 0x58, 0xb0, 0x3a, 0xc0,
-  0x10, 0xaa, 0x75, 0x22, 0x13, 0x88, 0x00, 0x0c, 0x48, 0xe5, 0x36, 0x81,
-  0x28, 0x61, 0x42, 0x01, 0xc1, 0x30, 0xaf, 0x72, 0xf6, 0xc9, 0xa6, 0x80,
-  0x82, 0xf6, 0xa2, 0x89, 0x4c, 0x96, 0xbb, 0x67, 0x40, 0x34, 0xad, 0x82,
-  0x01, 0x87, 0x24, 0x03, 0x07, 0x21, 0x1a, 0x94, 0xb5, 0xce, 0x8a, 0xc2,
-  0x23, 0x20, 0x48, 0x68, 0x32, 0x19, 0x85, 0xf4, 0x51, 0x0a, 0x45, 0xaf,
-  0x84, 0xb8, 0x40, 0x01, 0x83, 0x60, 0xdb, 0x81, 0xd0, 0xd3, 0x1c, 0xae,
-  0x22, 0x30, 0x51, 0x50, 0xc0, 0x99, 0x17, 0x26, 0x97, 0xe0, 0x25, 0x61,
-  0x7e, 0xf4, 0x98, 0x8b, 0x52, 0xa3, 0x92, 0xbe, 0x5a, 0x5b, 0x37, 0xda,
-  0xb3, 0x66, 0x68, 0xbc, 0xf3, 0x2d, 0x37, 0x7b, 0xff, 0xda, 0x7b, 0xe7,
-  0xeb, 0x26, 0x38, 0x7e, 0x69, 0x2e, 0x1f, 0xf8, 0x5d, 0x31, 0x5f, 0xf7,
-  0xe7, 0x49, 0x3a, 0x74, 0x5e, 0x9b, 0xc7, 0x52, 0x98, 0x2e, 0x1f, 0x18,
-  0xa1, 0x0f, 0x9c, 0x2c, 0x71, 0x7c, 0xd7, 0x75, 0xd9, 0x36, 0x98, 0xba,
-  0xd7, 0x14, 0xe6, 0xfa, 0x47, 0x1c, 0x66, 0xaf, 0x95, 0x90, 0xb6, 0x6f,
-  0xf4, 0xbd, 0x7d, 0x0f, 0xaa, 0xe8, 0x3a, 0xaf, 0x38, 0xda, 0xce, 0xe8,
-  0xc2, 0xb2, 0xf3, 0xda, 0x76, 0xa5, 0x43, 0xd3, 0x0a, 0x21, 0x88, 0x34,
-  0x44, 0x02, 0x6b, 0x90, 0x15, 0x29, 0x49, 0x76, 0xcc, 0xfa, 0x60, 0x68,
-  0x89, 0xfd, 0x7d, 0xe3, 0xad, 0xa0, 0xad, 0x85, 0xa2, 0x2b, 0xb2, 0x52,
-  0x34, 0x42, 0x78, 0xb9, 0x36, 0x4d, 0x02, 0x90, 0xce, 0xc3, 0x95, 0xbd,
-  0x79, 0x75, 0x9e, 0x1e, 0xdd, 0x94, 0x72, 0xd3, 0x61, 0xca, 0x81, 0xe4,
-  0xd9, 0x76, 0xd6, 0xd9, 0xd1, 0xe4, 0x68, 0xd1, 0xfa, 0x34, 0x49, 0x6b,
-  0xfd, 0xae, 0x5a, 0x94, 0xd6, 0x36, 0x94, 0xf4, 0x20, 0x06, 0x40, 0x3e,
-  0x6e, 0x75, 0x85, 0x8c, 0xad, 0xaa, 0xc4, 0x26, 0x33, 0xd5, 0x9b, 0x3f,
-  0x53, 0x02, 0xe9, 0xb2, 0xc2, 0xac, 0x05, 0xa5, 0x8c, 0x39, 0x4b, 0x8a,
-  0x93, 0xd2, 0xc2, 0xdf, 0x24, 0x92, 0x2c, 0x12, 0x5e, 0x35, 0x76, 0xfb,
-  0xa1, 0xe5, 0x6c, 0x2e, 0x96, 0xba, 0x0e, 0xbb, 0x1d, 0xde, 0x00, 0x13,
-  0x56, 0x88, 0x42, 0x18, 0x5e, 0x02, 0x11, 0xba, 0xa3, 0x38, 0x65, 0xca,
-  0xab, 0x60, 0xe1, 0x71, 0xd3, 0x84, 0x15, 0x2a, 0xae, 0x80, 0xc3, 0x8c,
-  0x82, 0x74, 0x87, 0xe4, 0xb2, 0x13, 0xe7, 0xe1, 0xeb, 0x7e, 0x92, 0xca,
-  0x7c, 0x06, 0x11, 0xe9, 0x95, 0x57, 0x54, 0xfc, 0xaf, 0x63, 0xbf, 0x13,
-  0xa1, 0x86, 0xee, 0x6e, 0x14, 0xe0, 0x8a, 0xb2, 0x0c, 0x9d, 0xd3, 0xed,
-  0x0c, 0xf8, 0x19, 0x6e, 0x31, 0x9f, 0x20, 0x02, 0x3d, 0xd0, 0xb7, 0x35,
-  0xfc, 0xf2, 0xef, 0x1c, 0xa8, 0x12, 0x69, 0x1a, 0x2f, 0xcf, 0x1f, 0xa8,
-  0xb8, 0x54, 0xb0, 0x69, 0x49, 0x31, 0x53, 0x8f, 0xc4, 0xe8, 0x01, 0xb1,
-  0x96, 0xd0, 0xd2, 0xc7, 0xd0, 0xda, 0xb1, 0x52, 0xb4, 0x00, 0x20, 0x36,
-  0xd5, 0x39, 0x45, 0x28, 0x88, 0xd6, 0xe0, 0x88, 0x0a, 0xfb, 0xa3, 0xa8,
-  0x25, 0x11, 0x64, 0x0a, 0x5c, 0x14, 0x70, 0x0e, 0x21, 0x1a, 0x94, 0xbd,
-  0xd6, 0x07, 0x41, 0x21, 0x8b, 0x83, 0x7b, 0xea, 0xef, 0x7a, 0x54, 0x65,
-  0x74, 0x27, 0x96, 0x97, 0x40, 0x00, 0x0c, 0xe3, 0x95, 0x89, 0xfa, 0x9f,
-  0xd6, 0xf5, 0xff, 0x77, 0x8e, 0xb5, 0x0e, 0x1d, 0x1e, 0xbd, 0xe4, 0x4a,
-  0x72, 0xf2, 0xde, 0x7d, 0x69, 0x1d, 0x71, 0x96, 0xa7, 0xa7, 0xa7, 0xbc,
-  0x45, 0xd9, 0xd6, 0x13, 0xc5, 0x53, 0x98, 0x92, 0x64, 0x98, 0xcf, 0x7f,
-  0xc6, 0xef, 0x1d, 0x11, 0x96, 0xa0, 0xbf, 0x0f, 0xa3, 0x5b, 0xd1, 0x1d,
-  0xa9, 0xa1, 0xf9, 0x5e, 0xda, 0xfb, 0x8f, 0x30, 0xb9, 0x73, 0x94, 0xdd,
-  0x9a, 0x31, 0x1d, 0x6d, 0x3a, 0x13, 0x30, 0x91, 0x5a, 0x7f, 0x9d, 0xf5,
-  0x64, 0x76, 0x24, 0x2a, 0x03, 0xb0, 0xb1, 0x6b, 0x25, 0x3c, 0x5f, 0x40,
-  0x98, 0x26, 0x87, 0x17, 0xb4, 0x31, 0xf8, 0x94, 0xfc, 0xc9, 0xca, 0x59,
-  0x33, 0x52, 0x61, 0x86, 0xe2, 0xa9, 0xb6, 0xd2, 0x14, 0x52, 0x97, 0xaa,
-  0x16, 0x4e, 0x2d, 0x5e, 0xf9, 0x5a, 0x83, 0x64, 0x01, 0xb0, 0x82, 0x43,
-  0x46, 0x82, 0x63, 0x78, 0xf6, 0xe7, 0x81, 0x0e, 0x4b, 0x31, 0xd6, 0x74,
-  0xe8, 0xf9, 0xf2, 0x6e, 0x69, 0x9f, 0x2a, 0xd8, 0x0a, 0x43, 0x34, 0x39,
-  0xc3, 0x4b, 0xba, 0x98, 0x48, 0x42, 0x49, 0x43, 0x3b, 0x98, 0x47, 0x6e,
-  0x91, 0x32, 0x92, 0x45, 0x24, 0xe4, 0xbd, 0xa6, 0xb9, 0x65, 0x57, 0x51,
-  0x3b, 0x2a, 0x4e, 0xc7, 0x0a, 0x79, 0x34, 0x15, 0xc7, 0xb1, 0xb7, 0x9b,
-  0x9a, 0x14, 0x47, 0x70, 0x4c, 0x5a, 0x18, 0xe6, 0x80, 0x18, 0xc0, 0x92,
-  0xa1, 0x45, 0x5e, 0xec, 0x87, 0x49, 0xf8, 0x16, 0x2d, 0xc8, 0x04, 0x62,
-  0xe6, 0x76, 0xd3, 0x5c, 0x36, 0x4d, 0x26, 0xe4, 0xa3, 0x6a, 0xde, 0x69,
-  0xfc, 0x97, 0xd4, 0x79, 0xe1, 0xca, 0xaa, 0xc3, 0xd6, 0xb3, 0x30, 0xce,
-  0xb8, 0xd5, 0x07, 0xbe, 0x82, 0x7a, 0x60, 0x20, 0x35, 0xc8, 0x6e, 0x1d,
-  0x8e, 0xd7, 0xdf, 0x5d, 0xb5, 0x12, 0x95, 0x70, 0x65, 0xe5, 0x29, 0x30,
-  0x87, 0xe2, 0xef, 0x50, 0x02, 0xeb, 0xc6, 0xf0, 0xde, 0x0b, 0x3f, 0xdc,
-  0x69, 0x08, 0x6e, 0xc0, 0x00, 0x84, 0xb2, 0x56, 0x75, 0xdf, 0xa6, 0xb5,
-  0x80, 0xf1, 0x3d, 0xdc, 0x7c, 0x79, 0x2b, 0x6a, 0x80, 0x07, 0xb8, 0x2a,
-  0x09, 0xcb, 0x63, 0x28, 0xda, 0x00, 0x14, 0xc2, 0x00, 0x38, 0x21, 0x1a,
-  0x94, 0xa5, 0xd2, 0x0a, 0x81, 0x18, 0x02, 0xaa, 0x26, 0x95, 0x88, 0x38,
-  0x5d, 0xcd, 0x00, 0x00, 0x07, 0xff, 0xc4, 0xcd, 0x3b, 0x27, 0x8e, 0xcd,
-  0x49, 0x30, 0x0f, 0x1e, 0x16, 0xd3, 0x6f, 0x12, 0xe1, 0xfe, 0x25, 0xb9,
-  0xbe, 0xe7, 0x40, 0x11, 0x2b, 0xed, 0xa5, 0xdb, 0x73, 0xd3, 0x99, 0xbe,
-  0x0d, 0xa3, 0x7d, 0x71, 0x4a, 0xd1, 0x13, 0x5e, 0x5c, 0xa5, 0x66, 0xfa,
-  0xf6, 0x96, 0x9e, 0xe0, 0x6f, 0x7d, 0xa5, 0x8e, 0xf9, 0x2f, 0xb5, 0x18,
-  0x12, 0x41, 0xa4, 0x3e, 0xea, 0xc5, 0xa5, 0xe1, 0x74, 0x7f, 0xd5, 0xfc,
-  0x07, 0xd0, 0x24, 0x57, 0x5c, 0x67, 0x57, 0xcd, 0x98, 0xb6, 0xc3, 0xd2,
-  0x67, 0x65, 0x40, 0x59, 0x5c, 0x19, 0x8a, 0x79, 0x77, 0x5c, 0xfb, 0x1e,
-  0x37, 0x37, 0xeb, 0x5f, 0xb2, 0x96, 0x34, 0x9f, 0x65, 0xff, 0xd0, 0xef,
-  0x8a, 0x21, 0x4a, 0x5c, 0x75, 0x49, 0x59, 0x76, 0xeb, 0x0d, 0x37, 0xe3,
-  0xdd, 0xef, 0x15, 0x6c, 0x80, 0xe8, 0xd3, 0x61, 0xc5, 0xae, 0x0f, 0xdf,
-  0xd0, 0x41, 0x8d, 0x39, 0xbe, 0x9c, 0x2d, 0x7d, 0x9b, 0x2f, 0x1d, 0x0a,
-  0xd3, 0xde, 0x9d, 0xb9, 0xcb, 0x7e, 0x99, 0x5e, 0x3c, 0xd1, 0x1b, 0x6d,
-  0x6a, 0x9c, 0xd6, 0x07, 0xe7, 0x63, 0x45, 0xf0, 0x54, 0x76, 0x2b, 0xbc,
-  0x65, 0x4e, 0x96, 0x2d, 0x43, 0x34, 0x57, 0x9e, 0x75, 0x99, 0x24, 0xbd,
-  0xef, 0x38, 0xef, 0x42, 0x1f, 0x6e, 0xac, 0x2d, 0xda, 0x6f, 0x9e, 0xf0,
-  0xbd, 0xd7, 0x8c, 0x31, 0xe1, 0x7c, 0x26, 0x01, 0x33, 0x6b, 0x61, 0xaa,
-  0x80, 0x62, 0xd0, 0x12, 0x54, 0xb8, 0xb5, 0x4a, 0x6c, 0x1c, 0x3c, 0x9f,
-  0x00, 0xd3, 0x28, 0x08, 0x36, 0x45, 0xc4, 0xe2, 0x10, 0xf8, 0xa3, 0xe8,
-  0xfc, 0x43, 0x23, 0xd3, 0x7e, 0xd9, 0x79, 0x31, 0xc7, 0x8d, 0xad, 0xb6,
-  0x72, 0xaa, 0xc9, 0x69, 0xf2, 0xaa, 0xc7, 0x84, 0x93, 0xdb, 0xb3, 0x89,
-  0x56, 0xc4, 0x37, 0xf1, 0xf1, 0xe4, 0xd0, 0x0b, 0xcd, 0x6e, 0x28, 0x0a,
-  0x67, 0x5b, 0x89, 0x90, 0xee, 0xbc, 0x5e, 0x5a, 0xdc, 0x93, 0x17, 0x28,
-  0x58, 0x0b, 0x1c, 0xd9, 0x94, 0xdc, 0x89, 0x38, 0x22, 0x02, 0x14, 0x1c,
-  0xdb, 0xcb, 0xc9, 0x09, 0x41, 0x10, 0xbf, 0x5d, 0x95, 0xa7, 0xe7, 0x65,
-  0x92, 0xd6, 0xa0, 0x54, 0xaa, 0x96, 0x2d, 0x60, 0x10, 0xb0, 0x00, 0x1a,
-  0xe0, 0x70, 0x21, 0x1a, 0x94, 0xd5, 0xce, 0x87, 0x03, 0x40, 0x91, 0x94,
-  0x82, 0x61, 0x5d, 0x16, 0x9a, 0x15, 0x42, 0x5d, 0xdd, 0xad, 0x00, 0x4c,
-  0x0b, 0x12, 0x78, 0xb6, 0x26, 0xe7, 0xca, 0xe3, 0xd0, 0x26, 0x3c, 0xbf,
-  0xa9, 0x34, 0x37, 0xe4, 0x33, 0xec, 0x89, 0x4d, 0x3b, 0x62, 0x34, 0xf7,
-  0x25, 0xe6, 0xde, 0xc2, 0xd2, 0x1d, 0x93, 0xf3, 0x51, 0xcf, 0x95, 0x6c,
-  0x9e, 0x9b, 0xfd, 0x5f, 0x83, 0x6b, 0x6e, 0x28, 0x6e, 0x38, 0xf8, 0x95,
-  0x7f, 0x72, 0xa9, 0xe6, 0xbe, 0xdd, 0xf1, 0x9c, 0x77, 0xd8, 0x19, 0x00,
-  0x3c, 0xbd, 0x4b, 0x65, 0x50, 0x7e, 0xd3, 0x3c, 0x6b, 0x9e, 0x67, 0xe2,
-  0xde, 0x74, 0xe6, 0x9d, 0x6d, 0xe7, 0x3e, 0x21, 0x54, 0x5e, 0x9f, 0xa3,
-  0x64, 0x6c, 0xaf, 0xe1, 0xdb, 0x0b, 0xb5, 0xd9, 0x39, 0xc4, 0x9f, 0x29,
-  0xdb, 0xf2, 0xdb, 0xe9, 0x38, 0xc8, 0xec, 0x4e, 0xd2, 0x65, 0xd2, 0xd3,
-  0x51, 0xe0, 0x9d, 0x24, 0xc8, 0xc6, 0x44, 0x02, 0x29, 0x8e, 0x38, 0xef,
-  0xb5, 0x86, 0x1c, 0xf0, 0x9e, 0x7a, 0x64, 0x00, 0x91, 0x23, 0xf6, 0x90,
-  0xaa, 0x7c, 0x96, 0xf6, 0x31, 0x45, 0xeb, 0x1d, 0x4b, 0xfb, 0xe9, 0x66,
-  0x3c, 0xcf, 0x42, 0xa6, 0x97, 0x39, 0x9b, 0x58, 0x79, 0x64, 0x1d, 0x03,
-  0x29, 0x5b, 0x68, 0xed, 0x13, 0x24, 0x1a, 0xa5, 0x7c, 0x3b, 0xcc, 0xa3,
-  0xa3, 0x08, 0x53, 0x73, 0x6b, 0x28, 0x46, 0xb7, 0x92, 0x49, 0x30, 0x61,
-  0x68, 0x27, 0x34, 0x95, 0x49, 0x19, 0xca, 0x36, 0x88, 0xa5, 0x10, 0x21,
-  0x38, 0xe0, 0x58, 0xc7, 0xf6, 0x9b, 0xe4, 0x75, 0x66, 0xa5, 0x1d, 0xaf,
-  0x8f, 0xaa, 0x08, 0xec, 0xbe, 0x0f, 0x0d, 0x3c, 0xbf, 0xef, 0x6e, 0x80,
-  0x99, 0xa7, 0x30, 0x90, 0xc2, 0x20, 0x10, 0xc1, 0x68, 0xde, 0xa2, 0xb8,
-  0xb3, 0xc0, 0x69, 0xd3, 0x5d, 0x0b, 0x02, 0x80, 0x73, 0xbb, 0xee, 0xe3,
-  0xed, 0x75, 0xe2, 0x6d, 0x54, 0x72, 0x7f, 0x57, 0xce, 0x2f, 0x36, 0xf3,
-  0x3d, 0x52, 0xa5, 0x4a, 0xf1, 0x63, 0x33, 0x97, 0xd3, 0x3a, 0x7b, 0x7f,
-  0xd8, 0xbe, 0xf3, 0x79, 0xe2, 0x79, 0x77, 0x1a, 0xd7, 0x68, 0x6d, 0xf6,
-  0x62, 0x9b, 0x85, 0x4b, 0x33, 0x2c, 0x02, 0x50, 0x2f, 0x84, 0xd0, 0x56,
-  0xbc, 0x26, 0xa5, 0xf0, 0x92, 0x6d, 0x74, 0x78, 0xd9, 0x65, 0x19, 0x26,
-  0xb8, 0xa4, 0x7b, 0xad, 0x19, 0x10, 0x98, 0x04, 0xc0, 0x6a, 0x15, 0x43,
-  0xf7, 0x00, 0xa2, 0x2e, 0x70, 0x9c, 0xa8, 0x0b, 0xd2, 0x0b, 0x95, 0x21,
-  0x14, 0x4b, 0xc1, 0x48, 0x5f, 0x92, 0x7a, 0x42, 0x72, 0xa2, 0xb0, 0x7a,
-  0x26, 0x02, 0x91, 0x80, 0x19, 0x7e, 0x66, 0x40, 0x38, 0x21, 0x1a, 0x94,
-  0xb5, 0xce, 0x86, 0xc3, 0x42, 0x0b, 0x48, 0x17, 0x22, 0xa8, 0x09, 0x7c,
-  0x2e, 0x68, 0x00, 0x00, 0x63, 0x8c, 0x98, 0x2a, 0xd4, 0xce, 0x6f, 0xe8,
-  0xe0, 0xe8, 0xc1, 0x83, 0x40, 0x99, 0xbe, 0xb3, 0x7f, 0xe2, 0xba, 0xc4,
-  0x11, 0x7f, 0xce, 0xd8, 0x74, 0x77, 0xb4, 0xec, 0x8c, 0xcd, 0x98, 0xe2,
-  0x5e, 0x5e, 0x1f, 0x51, 0xf0, 0x75, 0x6e, 0xa8, 0xd2, 0xae, 0x48, 0x26,
-  0xbb, 0x57, 0x9d, 0x01, 0x31, 0xfd, 0x67, 0x2e, 0xfd, 0x27, 0xcc, 0x9c,
-  0x5d, 0xfb, 0x33, 0x27, 0x16, 0xe9, 0x9c, 0x82, 0x1f, 0xb0, 0x70, 0x4c,
-  0xa5, 0x93, 0x45, 0xe1, 0x56, 0x96, 0xb8, 0x3b, 0xf3, 0x64, 0x7f, 0x9b,
-  0xd2, 0xfd, 0x67, 0x30, 0xec, 0x0f, 0x8a, 0xb9, 0x4e, 0x7a, 0x8e, 0x7b,
-  0xc1, 0x73, 0xf9, 0xee, 0x4f, 0x5d, 0x82, 0x87, 0xab, 0xa9, 0xd7, 0x31,
-  0xf2, 0x52, 0x94, 0x29, 0x97, 0x1b, 0xa8, 0x02, 0x39, 0x96, 0xab, 0xa7,
-  0x50, 0xf0, 0x7f, 0x47, 0x87, 0x9f, 0xa8, 0x7f, 0x34, 0xb2, 0xc2, 0x0f,
-  0x17, 0xf8, 0x4a, 0x15, 0xb4, 0x63, 0x42, 0xce, 0xee, 0xa6, 0x21, 0xcb,
-  0x21, 0x4f, 0x2d, 0xac, 0xa6, 0xc8, 0x86, 0x56, 0x67, 0xe1, 0x70, 0xf1,
-  0xb2, 0x8f, 0xf5, 0xfe, 0x6c, 0x70, 0xf0, 0xed, 0xcc, 0xee, 0x25, 0x9a,
-  0x58, 0x7b, 0xc2, 0xd7, 0x81, 0x37, 0x31, 0x1f, 0xe3, 0xba, 0x51, 0x71,
-  0xb5, 0x14, 0x96, 0xdf, 0x56, 0x61, 0x27, 0xb0, 0x0a, 0xb2, 0x8d, 0xb7,
-  0xc9, 0x74, 0x5b, 0xa5, 0x49, 0x2e, 0xa6, 0xbf, 0x7c, 0x23, 0xe8, 0xa7,
-  0xf6, 0xa5, 0xe3, 0xb6, 0x01, 0x2f, 0x68, 0x83, 0x81, 0x04, 0x48, 0x21,
-  0x08, 0x08, 0x60, 0xb2, 0x0c, 0x92, 0xa0, 0x7b, 0x5b, 0x8e, 0x28, 0xab,
-  0x18, 0x20, 0x2a, 0xb0, 0x70, 0xd7, 0x13, 0x7e, 0x39, 0xbe, 0x3b, 0x06,
-  0x6f, 0xea, 0xe3, 0xf7, 0x40, 0xf8, 0xcd, 0x93, 0x14, 0x1c, 0x33, 0xf7,
-  0x0a, 0xd1, 0xa4, 0xd6, 0xce, 0x14, 0x6c, 0xb3, 0xb6, 0xd5, 0xec, 0x06,
-  0x25, 0x9c, 0x22, 0x26, 0x28, 0x81, 0x69, 0xfd, 0x46, 0x8c, 0x05, 0xaf,
-  0x5e, 0x81, 0xfc, 0xaa, 0xeb, 0x28, 0x81, 0x18, 0x38, 0x3c, 0x34, 0xc3,
-  0xf9, 0xec, 0x02, 0xd8, 0x80, 0x80, 0x58, 0x05, 0x4a, 0xa5, 0x02, 0xd0,
-  0x4c, 0x94, 0x12, 0x56, 0xb6, 0x82, 0x20, 0x01, 0x1b, 0x40, 0x42, 0x41,
-  0x41, 0x23, 0xf8, 0xbc, 0x90, 0x94, 0x6e, 0x3f, 0x7f, 0xac, 0x6d, 0xa3,
-  0xc0, 0x5a, 0x79, 0x15, 0x0e, 0x21, 0x1a, 0x94, 0xdd, 0xce, 0x89, 0x63,
-  0xa1, 0xa1, 0x98, 0x24, 0x62, 0x4b, 0x56, 0x70, 0xb6, 0x46, 0xec, 0xd5,
-  0xf4, 0xe9, 0xab, 0x9b, 0xa0, 0x93, 0x00, 0x1c, 0x1e, 0xb3, 0xe9, 0xb6,
-  0xe8, 0xb7, 0x47, 0xe7, 0xfc, 0x4b, 0x0e, 0xfa, 0x85, 0x06, 0x1a, 0x80,
-  0x3a, 0xff, 0xf1, 0x7f, 0x6b, 0x58, 0x88, 0xcc, 0xe0, 0x71, 0x41, 0xe1,
-  0x59, 0x7a, 0x88, 0x27, 0xa7, 0xf6, 0x06, 0x06, 0x0a, 0x7a, 0xc2, 0xf7,
-  0x6c, 0x26, 0x50, 0x07, 0xa3, 0xfd, 0x93, 0xa7, 0xff, 0x3d, 0xf8, 0x5f,
-  0x1f, 0xee, 0x8c, 0xd1, 0xcc, 0xf9, 0xff, 0x24, 0x69, 0xb9, 0x19, 0x64,
-  0x89, 0x44, 0x49, 0x01, 0xc0, 0x51, 0xf9, 0x7f, 0x5c, 0xf2, 0x3c, 0xc2,
-  0x8a, 0x1f, 0xe6, 0x59, 0x73, 0xab, 0xb9, 0xb2, 0x1b, 0xab, 0xae, 0x7b,
-  0xc7, 0xe5, 0x61, 0xb3, 0xbd, 0x40, 0xd2, 0x94, 0x98, 0x9e, 0x85, 0x65,
-  0x47, 0xe8, 0xa4, 0x37, 0x7f, 0x60, 0xe5, 0xfa, 0x0d, 0x5a, 0x76, 0xbc,
-  0xd6, 0x08, 0xb9, 0x4b, 0xae, 0x36, 0x2d, 0xfa, 0xea, 0x80, 0xe8, 0x8e,
-  0xe3, 0xf4, 0x0d, 0x6f, 0xfe, 0xdd, 0x6d, 0xfe, 0x5f, 0xff, 0xb2, 0xa8,
-  0xbf, 0x4d, 0x68, 0x87, 0x8e, 0x03, 0xa6, 0xfb, 0x53, 0xc5, 0xfa, 0xa3,
-  0xa0, 0xaa, 0x50, 0x7e, 0xdb, 0xe0, 0x7a, 0x5b, 0xf6, 0x78, 0x66, 0xc7,
-  0xa4, 0x35, 0x3e, 0x85, 0xcd, 0x59, 0x87, 0xe3, 0xb3, 0x5c, 0x16, 0x1d,
-  0xa5, 0xeb, 0x1b, 0xaf, 0x6b, 0xb9, 0x96, 0x46, 0xd9, 0xb1, 0xe8, 0x1a,
-  0x9f, 0xd2, 0xed, 0xdb, 0x6d, 0x7a, 0xa3, 0x64, 0xc5, 0xdc, 0x23, 0xa4,
-  0x6b, 0xd3, 0x52, 0x2b, 0x50, 0xe4, 0xc8, 0xc1, 0x92, 0x59, 0x0e, 0x5c,
-  0x21, 0xcd, 0xab, 0x2d, 0x52, 0x3d, 0x2c, 0xaf, 0xec, 0xb5, 0x88, 0xdd,
-  0xce, 0x32, 0xf7, 0x83, 0x1f, 0xd2, 0x8a, 0xd8, 0xf2, 0x23, 0xbc, 0xd8,
-  0xcf, 0x8c, 0xbc, 0xa9, 0x81, 0xd1, 0x34, 0x97, 0xd7, 0xe2, 0xd5, 0x02,
-  0x7a, 0xdf, 0x59, 0x3f, 0xa1, 0x81, 0x0b, 0xfd, 0x66, 0x9a, 0xce, 0x79,
-  0x29, 0xf2, 0x6b, 0xae, 0xcd, 0x18, 0x35, 0xb7, 0xdc, 0xff, 0xaa, 0x8e,
-  0x61, 0x20, 0x17, 0x1c, 0xbe, 0x40, 0x06, 0x82, 0x48, 0xbc, 0xe1, 0xb3,
-  0xd3, 0x42, 0x03, 0xab, 0x7d, 0x26, 0xbd, 0x7e, 0xe3, 0x69, 0xe7, 0xdb,
-  0x56, 0x74, 0x00, 0x04, 0xc5, 0xb1, 0x88, 0x63, 0x41, 0x88, 0x80, 0x62,
-  0x10, 0x40, 0x98, 0x23, 0x73, 0x52, 0x98, 0x81, 0x6f, 0x2b, 0xf6, 0x58,
-  0x05, 0x8d, 0x83, 0x4a, 0xf6, 0xed, 0x85, 0x19, 0xd3, 0x4e, 0xa8, 0xdb,
-  0xd2, 0xfe, 0x97, 0xce, 0x8c, 0x11, 0xa3, 0xe1, 0x0e, 0x11, 0x20, 0x28,
-  0xb9, 0xe3, 0xf9, 0x1d, 0x22, 0x2d, 0xc0, 0x0c, 0xa1, 0x76, 0xfc, 0x42,
-  0x58, 0x72, 0x86, 0xd2, 0x22, 0xf0, 0xc4, 0x7f, 0x6f, 0xda, 0x02, 0x5c,
-  0xfe, 0x66, 0x33, 0x70, 0x89, 0xd7, 0x88, 0x19, 0xcb, 0x2c, 0x95, 0x44,
-  0xf2, 0x01, 0x4c, 0xbe, 0xe5, 0xc5, 0x45, 0x36, 0xac, 0x28, 0x5f, 0xef,
-  0x83, 0x2f, 0xcf, 0xaf, 0xef, 0x7f, 0x15, 0xe5, 0x70, 0xe0, 0x21, 0x1a,
-  0x94, 0xb5, 0xaa, 0x95, 0x05, 0x41, 0x8b, 0x80, 0x52, 0xf0, 0xc5, 0x54,
-  0x79, 0x4e, 0x32, 0x5d, 0x9a, 0x18, 0x68, 0xa1, 0xac, 0x9d, 0xa0, 0x12,
-  0x0a, 0x05, 0x69, 0xe0, 0xae, 0xa7, 0x54, 0x26, 0x97, 0xcd, 0xfa, 0xed,
-  0x8d, 0x69, 0x9c, 0x91, 0xc9, 0x1a, 0x6d, 0xcf, 0x68, 0xa2, 0x8f, 0x95,
-  0x0b, 0xfb, 0xee, 0x4b, 0xb6, 0xfc, 0x8b, 0x5b, 0xab, 0x5e, 0x9c, 0x5d,
-  0xa5, 0x3c, 0x3a, 0xab, 0x4e, 0x5e, 0xb7, 0x9e, 0xd3, 0xb0, 0x3c, 0x1a,
-  0xfc, 0x50, 0xee, 0x58, 0xef, 0x09, 0x24, 0x0e, 0xa7, 0x3f, 0x2a, 0xe3,
-  0xbd, 0x69, 0x19, 0xa5, 0xc1, 0x36, 0x65, 0xad, 0x2e, 0xcd, 0xf0, 0xba,
-  0x93, 0xed, 0xcf, 0x39, 0xc3, 0xb9, 0x72, 0xe5, 0xff, 0x75, 0x81, 0xd0,
-  0x36, 0x6b, 0xfc, 0x8f, 0x16, 0x46, 0xa5, 0xa8, 0xd6, 0xfa, 0x7c, 0xc5,
-  0x30, 0x5a, 0xcc, 0x93, 0x4c, 0xa6, 0x98, 0x81, 0xb4, 0x90, 0xc3, 0xc3,
-  0x4e, 0x59, 0x6d, 0xbe, 0xb3, 0xa6, 0xf3, 0x14, 0x28, 0x4c, 0x40, 0xce,
-  0xd7, 0xff, 0xb7, 0x3f, 0x70, 0xc5, 0xb6, 0x65, 0x5e, 0xbd, 0x9a, 0x40,
-  0xc5, 0xa1, 0xeb, 0x82, 0x4b, 0x63, 0x75, 0x01, 0xe0, 0x9a, 0xd4, 0xa8,
-  0xac, 0x45, 0xad, 0xd9, 0x96, 0xa8, 0xec, 0xd5, 0x4b, 0xa7, 0x3f, 0x50,
-  0xa0, 0xa4, 0xcb, 0xd2, 0x60, 0x90, 0x7f, 0xfb, 0x4f, 0x90, 0xd9, 0x70,
-  0x6b, 0xd6, 0x59, 0x53, 0xdc, 0x6b, 0x0c, 0x4a, 0x46, 0x8c, 0x22, 0x37,
-  0x88, 0x4a, 0xd9, 0xe8, 0xe8, 0x81, 0x10, 0x14, 0x48, 0x02, 0x13, 0x80,
-  0x11, 0xe1, 0x80, 0xf6, 0x35, 0xf8, 0xb3, 0x17, 0x41, 0xac, 0x9d, 0xa0,
-  0x12, 0x0a, 0x05, 0x68, 0xfc, 0xbc, 0x4a, 0x8f, 0xbc, 0xfe, 0xf5, 0x1b,
-  0xe2, 0x7a, 0x3f, 0xcf, 0x16, 0xfb, 0x23, 0xb3, 0x49, 0x88, 0x73, 0x28,
-  0x3a, 0x31, 0xef, 0x09, 0x80, 0xbd, 0xe3, 0xc2, 0xbb, 0xc4, 0xd6, 0xee,
-  0x3b, 0xeb, 0x4e, 0x7b, 0x6e, 0x18, 0x33, 0x20, 0x19, 0x04, 0xa5, 0xde,
-  0x69, 0x90, 0xd0, 0x38, 0x3c, 0x4d, 0x55, 0x4e, 0x3a, 0x64, 0xcf, 0xae,
-  0xd7, 0xda, 0x20, 0x51, 0xdb, 0xf6, 0x38, 0x4f, 0xe7, 0x0b, 0xf2, 0x09,
-  0x5c, 0x0f, 0x08, 0x00, 0x8d, 0xd4, 0x9d, 0xae, 0x56, 0xe9, 0x2b, 0x20,
-  0x06, 0x46, 0x00, 0x31, 0xc0, 0x34, 0x32, 0xa5, 0x61, 0x7a, 0x45, 0x93,
-  0x10, 0x03, 0x07, 0x21, 0x1a, 0x94, 0xc5, 0xba, 0x96, 0x83, 0x13, 0x90,
-  0x84, 0xa2, 0x23, 0x77, 0x6b, 0x36, 0xc9, 0x1e, 0x65, 0xaa, 0xec, 0x81,
-  0x9c, 0x06, 0x04, 0x9a, 0x02, 0x43, 0x3f, 0xf0, 0x7d, 0x07, 0xff, 0x2b,
-  0xa5, 0x3e, 0xb7, 0x6c, 0x7d, 0xbf, 0xb5, 0x2d, 0x24, 0x5d, 0xc1, 0xbe,
-  0x2b, 0x50, 0x67, 0xd4, 0xfb, 0x26, 0x48, 0xf3, 0xb5, 0xbe, 0xeb, 0x99,
-  0x81, 0xa5, 0xe7, 0xf1, 0xcc, 0xe1, 0xfa, 0x97, 0x4d, 0x56, 0xc3, 0xc9,
-  0x7e, 0xed, 0xff, 0x94, 0x61, 0x1e, 0x71, 0xfe, 0x8f, 0xb2, 0xb9, 0x34,
-  0xde, 0x22, 0x3e, 0x7a, 0xf7, 0x53, 0xeb, 0x3b, 0xcf, 0xc2, 0xfb, 0xf7,
-  0x5a, 0xf2, 0xaa, 0x85, 0xdd, 0x79, 0x35, 0x5b, 0xaa, 0x7f, 0xba, 0x7a,
-  0xcd, 0x56, 0x37, 0x7d, 0x77, 0x65, 0xc6, 0xe3, 0x6b, 0x3b, 0x6e, 0x26,
-  0x33, 0x24, 0xa1, 0x4b, 0xb9, 0xda, 0xcd, 0xa6, 0x14, 0x62, 0x97, 0x1e,
-  0xe6, 0x5b, 0x90, 0x58, 0xb5, 0x6b, 0x12, 0x66, 0xd8, 0xac, 0x54, 0x70,
-  0x66, 0x5a, 0xd9, 0x49, 0x6a, 0xd5, 0x49, 0x90, 0x86, 0x64, 0xa8, 0xad,
-  0x17, 0xc0, 0x82, 0x93, 0x7c, 0xad, 0x89, 0xa8, 0x41, 0x68, 0x6b, 0xa3,
-  0xb7, 0xc1, 0x4d, 0x26, 0x6b, 0x2f, 0xbd, 0x54, 0xdb, 0x77, 0x48, 0x9c,
-  0xe6, 0x42, 0x5f, 0xa9, 0xb2, 0x9a, 0xe2, 0x25, 0x58, 0x46, 0xab, 0x94,
-  0x45, 0x7d, 0x51, 0xb4, 0x14, 0x48, 0x89, 0xd6, 0x65, 0x34, 0xe1, 0x9d,
-  0xa5, 0x25, 0xca, 0x28, 0x99, 0x55, 0x9e, 0xa5, 0x8b, 0x98, 0xa4, 0x9d,
-  0x8b, 0xd1, 0x8a, 0x33, 0x9a, 0x11, 0x41, 0x72, 0xd1, 0x04, 0xd5, 0xbc,
-  0xd2, 0x44, 0x17, 0x04, 0x6a, 0xaa, 0xed, 0x4a, 0x54, 0x5b, 0x81, 0xae,
-  0x0e, 0x00, 0x01, 0x6c, 0xad, 0x58, 0x0e, 0x78, 0x00, 0xa3, 0x9c, 0x2a,
-  0x22, 0xbf, 0x4e, 0xb8, 0xda, 0x94, 0x2d, 0xba, 0x4e, 0x22, 0xd3, 0x87,
-  0xfc, 0x6e, 0x53, 0x91, 0xe1, 0x54, 0x7c, 0x4e, 0xe6, 0xcc, 0xdc, 0x46,
-  0x38, 0xfe, 0x04, 0xb0, 0x16, 0xcb, 0xfe, 0x45, 0x1d, 0xca, 0xec, 0x9b,
-  0xea, 0xff, 0x38, 0xe6, 0xc9, 0x81, 0xa9, 0x51, 0x22, 0xa1, 0xfd, 0x9f,
-  0xab, 0xe3, 0xe6, 0xd4, 0x73, 0x77, 0x9b, 0xbe, 0x7f, 0x0d, 0xed, 0xbf,
-  0x8f, 0x4d, 0xa8, 0x00, 0xf7, 0x87, 0xe3, 0xe3, 0xcf, 0x40, 0x58, 0x00,
-  0x31, 0x39, 0xcb, 0x26, 0x67, 0x7d, 0xcb, 0x26, 0x68, 0x7d, 0x11, 0x1d,
-  0xd4, 0x55, 0x52, 0xbb, 0x1e, 0x17, 0xd6, 0x09, 0xda, 0x1f, 0x8c, 0x90,
-  0xb7, 0xd7, 0xa7, 0x6a, 0xd9, 0x71, 0x91, 0x5e, 0x9e, 0x2b, 0xf1, 0x9c,
-  0xa1, 0x4c, 0x79, 0x40, 0x3c, 0x5a, 0x8b, 0x54, 0xb1, 0x40, 0x00, 0xed,
-  0x9b, 0x2a, 0x81, 0x9e, 0x9a, 0xd3, 0x4a, 0x0c, 0xf0, 0x0e, 0x21, 0x1a,
-  0x8e, 0xca, 0x1f, 0xff, 0x6f, 0xff, 0x35, 0x65, 0xa8, 0x41, 0x90, 0xa4,
-  0x71, 0x18, 0x0a, 0x55, 0x33, 0x42, 0xa5, 0xde, 0xac, 0xd7, 0x15, 0xaa,
-  0xb0, 0x00, 0xd6, 0x48, 0x86, 0x7a, 0x05, 0x7b, 0x20, 0xbb, 0x8d, 0xca,
-  0xe5, 0x8d, 0x6b, 0x12, 0x90, 0x08, 0x09, 0x94, 0xb8, 0xf4, 0x3e, 0x71,
-  0x6e, 0x23, 0xd5, 0x39, 0x8b, 0x79, 0x76, 0xcc, 0x64, 0x1e, 0x6b, 0xaa,
-  0xf4, 0xaa, 0xe6, 0x20, 0xa6, 0x8f, 0x95, 0x65, 0x0e, 0xcd, 0x1c, 0x68,
-  0x09, 0xab, 0x27, 0x2a, 0x9b, 0x5e, 0x69, 0xb8, 0xe6, 0x5c, 0xb7, 0x2e,
-  0x80, 0x87, 0x4f, 0xe3, 0x5c, 0x71, 0xda, 0xa4, 0xa8, 0xcb, 0xc3, 0xd6,
-  0x71, 0x5b, 0xc8, 0x65, 0x2a, 0x80, 0x2d, 0xbb, 0xdd, 0x5d, 0x44, 0x52,
-  0x19, 0x9c, 0xe3, 0x76, 0x4d, 0x5d, 0x71, 0x04, 0x20, 0x5a, 0xd3, 0x2d,
-  0x08, 0x44, 0x8a, 0x48, 0xb5, 0x26, 0x37, 0x11, 0x6c, 0x2a, 0x1c, 0x16,
-  0x35, 0x51, 0x73, 0x82, 0x90, 0x44, 0x63, 0x84, 0x40, 0x6d, 0x88, 0x52,
-  0x20, 0xb6, 0x13, 0x82, 0x4c, 0x0b, 0x82, 0x15, 0x28, 0x25, 0x3d, 0x3a,
-  0xa5, 0xab, 0xcd, 0x68, 0x6e, 0xa6, 0xef, 0x55, 0xb3, 0x36, 0x78, 0x30,
-  0x2a, 0xbc, 0x75, 0xf6, 0x91, 0x27, 0x3d, 0x6c, 0xd5, 0xcb, 0x2c, 0x55,
-  0x6a, 0xdd, 0xd6, 0x4d, 0xdf, 0x14, 0xf2, 0x37, 0x95, 0x4f, 0x6f, 0x1c,
-  0xfc, 0x8e, 0x48, 0x84, 0xfb, 0x38, 0xd7, 0x9f, 0xce, 0x1c, 0x40, 0x5f,
-  0xeb, 0xab, 0xe4, 0xe4, 0xff, 0xce, 0xee, 0xe6, 0xe6, 0xd3, 0xfc, 0x9c,
-  0xe7, 0x5a, 0xe8, 0x6c, 0x87, 0x21, 0xb3, 0x59, 0xc1, 0x8a, 0xd5, 0x92,
-  0x16, 0x48, 0x59, 0xcc, 0xf5, 0xd5, 0x97, 0x75, 0x0f, 0xff, 0x74, 0x39,
-  0x70, 0x11, 0xfa, 0x84, 0xc5, 0xae, 0x8a, 0xc5, 0x22, 0x00, 0x85, 0xe3,
-  0x74, 0x56, 0xc2, 0xd9, 0x49, 0xad, 0x34, 0x71, 0xad, 0x52, 0xe2, 0x22,
-  0x0b, 0x65, 0x2f, 0x7f, 0x82, 0x07, 0x46, 0x71, 0x55, 0x38, 0x0e, 0xcd,
-  0xeb, 0x08, 0x3f, 0x3a, 0x46, 0x38, 0x18, 0x26, 0x44, 0x7d, 0x73, 0xfa,
-  0x1c, 0x3d, 0x23, 0x05, 0x81, 0xc8, 0x54, 0x9e, 0xf3, 0x8e, 0x0e, 0x0e,
-  0xa3, 0x20, 0xa0, 0xff, 0x43, 0x86, 0xf8, 0x8d, 0xcf, 0x60, 0x83, 0xb3,
-  0x62, 0x55, 0x89, 0xf5, 0xe3, 0x1e, 0x2f, 0xe0, 0x12, 0x95, 0x29, 0x7c,
-  0x51, 0x72, 0x0c, 0xe8, 0x44, 0x53, 0x9f, 0x7b, 0x54, 0x81, 0x7e, 0x7d,
-  0x08, 0xa7, 0xbf, 0x75, 0x37, 0x34, 0xbb, 0x63, 0xaf, 0x6f, 0x51, 0x6c,
-  0x3c, 0x5d, 0xc1, 0xfe, 0x2a, 0x25, 0x2a, 0x7f, 0x6c, 0x3d, 0x5d, 0xa3,
-  0x1b, 0x10, 0x4c, 0xa5, 0xa8, 0xbd, 0xcf, 0x4e, 0x7f, 0xc3, 0x3d, 0xaf,
-  0x2f, 0xfa, 0x05, 0x11, 0x00, 0xcd, 0x4c, 0xc0, 0x90, 0xa1, 0x79, 0xa7,
-  0x11, 0x41, 0x69, 0xa7, 0x6e, 0xba, 0x13, 0x00, 0x0b, 0x6c, 0x50, 0x4a,
-  0xd5, 0x1b, 0x60, 0x2c, 0x1c, 0x21, 0x1a, 0x8d, 0x08, 0x5f, 0x34, 0x07,
-  0xfd, 0x37, 0x54, 0x61, 0x0c, 0x40, 0x6c, 0xae, 0x0a, 0x85, 0x57, 0x34,
-  0x1a, 0x5c, 0xb1, 0xe6, 0xda, 0x5e, 0x11, 0x5a, 0x5b, 0x2a, 0x42, 0xe0,
-  0x70, 0x7f, 0x7b, 0x4e, 0xb3, 0x87, 0xbd, 0x7e, 0xee, 0xc1, 0x96, 0x62,
-  0x7f, 0x55, 0x58, 0x5d, 0x6d, 0xec, 0x0f, 0x3e, 0xd9, 0x1f, 0x0e, 0x6b,
-  0x0f, 0x18, 0xe5, 0xad, 0xae, 0x36, 0x3a, 0xb8, 0xbc, 0xf5, 0xcb, 0x5b,
-  0xbc, 0x1b, 0x89, 0x29, 0xa3, 0xf8, 0xc5, 0x18, 0x6b, 0xd4, 0x9d, 0x93,
-  0x5e, 0xda, 0xa2, 0xae, 0xe6, 0x5d, 0xf3, 0x76, 0x46, 0xb6, 0x1b, 0x27,
-  0x2b, 0x75, 0x7a, 0x04, 0xa3, 0x23, 0x14, 0xa4, 0xa3, 0x78, 0xb7, 0x23,
-  0xf9, 0xd3, 0xb3, 0x3c, 0xab, 0x60, 0x87, 0x9f, 0xfd, 0x5a, 0x26, 0xae,
-  0xb3, 0x6f, 0xb9, 0x10, 0xb8, 0x09, 0xa8, 0x54, 0x13, 0xc5, 0x5a, 0x62,
-  0xb4, 0x57, 0x02, 0x6c, 0x32, 0x0b, 0x52, 0x32, 0x8c, 0x34, 0x29, 0x68,
-  0x30, 0x5f, 0x7f, 0x52, 0xd2, 0x9d, 0xe3, 0x64, 0x9b, 0x65, 0x85, 0x74,
-  0x16, 0x42, 0xe1, 0x70, 0x09, 0x27, 0x38, 0x6d, 0x9d, 0xb6, 0x42, 0x86,
-  0x86, 0xeb, 0x31, 0xc6, 0x72, 0xd4, 0x68, 0x0f, 0x25, 0x86, 0x9a, 0xb9,
-  0x68, 0xb0, 0x16, 0x44, 0x46, 0xe6, 0x63, 0x73, 0x4f, 0x1c, 0x15, 0xd9,
-  0x8b, 0xb6, 0xff, 0x07, 0xce, 0xad, 0x0e, 0x36, 0xc4, 0xc3, 0x4f, 0xa5,
-  0x5b, 0xbe, 0x8e, 0x36, 0xe8, 0x40, 0xe2, 0x1e, 0xf1, 0x39, 0xf2, 0x14,
-  0x20, 0xf8, 0xcf, 0x86, 0x81, 0x53, 0xcc, 0x80, 0x0a, 0x3a, 0xca, 0x71,
-  0x06, 0x09, 0xa1, 0x09, 0x15, 0x2a, 0x9f, 0x40, 0x92, 0x01, 0xaa, 0x26,
-  0xab, 0x9d, 0x25, 0xe8, 0xbd, 0x85, 0xdc, 0x4f, 0xf3, 0x8e, 0x41, 0xef,
-  0x23, 0x02, 0x96, 0xba, 0xcc, 0xa4, 0x9f, 0x9f, 0x3a, 0xfb, 0x58, 0xdc,
-  0x3d, 0x58, 0xff, 0xcc, 0x3e, 0x5c, 0x60, 0x4e, 0xc2, 0xff, 0x84, 0xe5,
-  0x5e, 0xa4, 0x8d, 0x3f, 0xbb, 0x66, 0x6b, 0xc0, 0x20, 0xda, 0x5e, 0x4c,
-  0xd3, 0xcf, 0xb2, 0x74, 0x13, 0x04, 0xa7, 0x31, 0xc9, 0x7c, 0x30, 0xd3,
-  0x0a, 0x14, 0xa5, 0xb1, 0x92, 0x3c, 0x25, 0x2e, 0x98, 0x6d, 0x14, 0x5e,
-  0x3c, 0x3e, 0x40, 0x16, 0x02, 0x60, 0x1c, 0x21, 0x1a, 0x8f, 0x80, 0x1e,
-  0x0c, 0x00, 0x5f, 0x3f, 0x67, 0xa7, 0xb0, 0xc6, 0x24, 0x9c, 0x31, 0x2a,
-  0xa8, 0x52, 0x4e, 0x92, 0xd1, 0xab, 0x34, 0x12, 0x85, 0x96, 0xc9, 0x92,
-  0xb0, 0x3c, 0xe3, 0x9e, 0x35, 0xfe, 0x94, 0xf9, 0xee, 0x6e, 0x81, 0x3a,
-  0xbb, 0xbb, 0x99, 0x31, 0x3c, 0x3b, 0x8c, 0x1a, 0xf2, 0xd1, 0xb3, 0x89,
-  0x55, 0xca, 0x8d, 0x82, 0x7d, 0xc2, 0x32, 0xe3, 0xab, 0x8c, 0xd7, 0x4a,
-  0xb7, 0x64, 0x60, 0xae, 0x89, 0x7e, 0xad, 0xb8, 0xe4, 0x15, 0xee, 0xd7,
-  0x9e, 0x3f, 0x39, 0xc0, 0x41, 0x63, 0xb5, 0xb9, 0x29, 0x72, 0xb4, 0xdb,
-  0x0a, 0xfb, 0xd4, 0xe8, 0xe4, 0xc8, 0x55, 0xb7, 0xdc, 0x16, 0x67, 0x09,
-  0x1d, 0x3b, 0x7c, 0x43, 0x5b, 0x30, 0x62, 0x47, 0x19, 0x49, 0x11, 0x87,
-  0x24, 0x8d, 0x68, 0x83, 0xb4, 0x48, 0x92, 0x05, 0xd5, 0x71, 0xee, 0xb1,
-  0xab, 0xc7, 0xcf, 0xb6, 0x71, 0xa2, 0xbb, 0xe2, 0x45, 0xa6, 0x12, 0x6c,
-  0x09, 0xe4, 0x8a, 0x89, 0xa5, 0x8b, 0x75, 0x49, 0x28, 0x8c, 0x40, 0x60,
-  0x9a, 0x2b, 0x68, 0x16, 0x50, 0xd1, 0x4f, 0x45, 0xe1, 0x01, 0x18, 0x9b,
-  0xe1, 0x8e, 0x50, 0xf5, 0x28, 0xe5, 0x46, 0x1a, 0x17, 0xbb, 0xf1, 0xb2,
-  0xf3, 0x8d, 0xa8, 0x4c, 0x4a, 0xc5, 0xa1, 0x40, 0x51, 0x72, 0x1b, 0xeb,
-  0x6a, 0xf4, 0x83, 0xc7, 0x04, 0x81, 0x3f, 0x6d, 0xa4, 0xb0, 0x90, 0xc2,
-  0xc5, 0x20, 0x02, 0x58, 0xe4, 0xbd, 0x7d, 0x6f, 0x65, 0x92, 0xe1, 0xe7,
-  0x7c, 0x2e, 0x82, 0x74, 0x4d, 0xf5, 0xcb, 0x9e, 0x62, 0xd3, 0x89, 0xe7,
-  0x06, 0x54, 0x2c, 0x8c, 0x08, 0x5c, 0x89, 0x99, 0x30, 0xa7, 0xff, 0x33,
-  0xf3, 0xe6, 0x81, 0x74, 0x97, 0x99, 0xab, 0x70, 0x63, 0xd0, 0x73, 0x1f,
-  0xec, 0x90, 0x12, 0x9c, 0xdf, 0x8a, 0xf4, 0x9a, 0x33, 0xce, 0xbe, 0x99,
-  0x25, 0xcc, 0xea, 0x77, 0xb6, 0x14, 0x13, 0xec, 0x72, 0xc4, 0x60, 0x89,
-  0x30, 0x42, 0xe7, 0x82, 0x56, 0x30, 0x19, 0x83, 0x15, 0xf6, 0xde, 0x55,
-  0x20, 0xf5, 0x7b, 0xc0, 0xa2, 0xee, 0x1e, 0x29, 0xdb, 0xe7, 0x87, 0x90,
-  0x7c, 0xd8, 0xab, 0xec, 0x99, 0x74, 0x04, 0xbe, 0x2c, 0x08, 0x95, 0x10,
-  0x00, 0x98, 0xe9, 0x18, 0x1c, 0x63, 0x2e, 0x06, 0x17, 0x43, 0xbe, 0xad,
-  0x1e, 0x7b, 0xaf, 0x50, 0xa0, 0xc0, 0xae, 0x96, 0xbd, 0x48, 0x32, 0xb3,
-  0xc1, 0x5c, 0xb9, 0x22, 0x38, 0xa0, 0xac, 0x11, 0x53, 0xa1, 0xe2, 0x02,
-  0xc5, 0x6b, 0x36, 0xaa, 0xac, 0x60, 0xd2, 0x45, 0x19, 0x57, 0x3e, 0x97,
-  0x32, 0x47, 0xda, 0x3e, 0x74, 0x11, 0x7c, 0x28, 0xbe, 0xc5, 0x38, 0xe9,
-  0xd4, 0x9f, 0xf9, 0xb5, 0x51, 0x5c, 0x1b, 0x6a, 0x9b, 0x05, 0x65, 0x97,
-  0x56, 0x1d, 0xf1, 0xf6, 0x53, 0x58, 0x77, 0x2c, 0xa2, 0x50, 0xa9, 0xf0,
-  0xa3, 0x87, 0x3f, 0x5e, 0xdc, 0x49, 0x81, 0xc0, 0x21, 0x2a, 0x8e, 0x80,
-  0x04, 0x00, 0x00, 0x1f, 0x3b, 0x66, 0xa7, 0x30, 0x90, 0x63, 0x15, 0xc1,
-  0x32, 0x18, 0x0e, 0x11, 0xad, 0x34, 0xb2, 0x50, 0xb8, 0x6c, 0x0e, 0x47,
-  0x9b, 0xfe, 0x37, 0xc5, 0x6d, 0x00, 0x13, 0x18, 0x31, 0xc4, 0xb8, 0x2c,
-  0x23, 0x5d, 0x6d, 0x08, 0x6f, 0x65, 0xc2, 0xd1, 0xf0, 0x79, 0x08, 0xfc,
-  0xed, 0xbc, 0x7c, 0x75, 0x82, 0x89, 0x4c, 0x6c, 0x8d, 0x70, 0x09, 0xe7,
-  0xe4, 0x91, 0x58, 0x97, 0x9b, 0x63, 0x69, 0xbb, 0x52, 0xda, 0x0e, 0xc8,
-  0xfa, 0x5c, 0xee, 0x19, 0x2c, 0x0a, 0x75, 0xc1, 0xc3, 0x00, 0x44, 0x64,
-  0x6c, 0xa9, 0x78, 0x4a, 0x1b, 0xe4, 0xc4, 0xaf, 0x23, 0x0c, 0xb2, 0xba,
-  0x84, 0x46, 0xe3, 0x08, 0x97, 0x86, 0x55, 0x10, 0x76, 0xc4, 0xc6, 0xb1,
-  0x44, 0x31, 0xbe, 0x8b, 0x44, 0xab, 0xd7, 0x8a, 0x95, 0xb6, 0x45, 0x45,
-  0x29, 0x08, 0x56, 0xee, 0x08, 0x05, 0x6f, 0x90, 0x88, 0x04, 0xe2, 0x03,
-  0x3c, 0x3d, 0xdc, 0x48, 0x48, 0x12, 0x0a, 0xb3, 0x6b, 0x44, 0x92, 0xd4,
-  0x9c, 0xce, 0x98, 0x27, 0xe5, 0xa3, 0x90, 0x31, 0xdb, 0x78, 0xd2, 0x20,
-  0x6b, 0xf8, 0xb6, 0x5f, 0x5f, 0x6a, 0x66, 0xd0, 0xb5, 0x00, 0x13, 0x97,
-  0x08, 0x33, 0x0d, 0x0a, 0x2c, 0x2a, 0x98, 0x65, 0x2d, 0x13, 0x5b, 0x4c,
-  0xd7, 0x06, 0x6a, 0xac, 0x1a, 0x27, 0x98, 0xd0, 0xa3, 0xd8, 0x5c, 0xb6,
-  0x0e, 0x1c, 0xf4, 0xb7, 0x17, 0xfb, 0xa5, 0x12, 0x8e, 0x07, 0x3b, 0x3e,
-  0x52, 0x06, 0x3d, 0x2e, 0x04, 0x1e, 0x9a, 0xbc, 0x23, 0x1c, 0x27, 0xae,
-  0x76, 0x2d, 0x64, 0x0c, 0xf9, 0x75, 0x0e, 0x99, 0xa7, 0xae, 0x91, 0x6d,
-  0xbc, 0x56, 0xfa, 0x50, 0xe3, 0x59, 0x8b, 0x43, 0x46, 0xb9, 0x7b, 0x59,
-  0xf5, 0x35, 0x2f, 0xa8, 0x3b, 0xad, 0x37, 0x74, 0x3a, 0x25, 0x34, 0xf7,
-  0x5a, 0x14, 0xf6, 0xb2, 0xed, 0xb1, 0x2c, 0x1a, 0xee, 0x29, 0xc8, 0xfc,
-  0x9a, 0x4d, 0x65, 0xd5, 0x29, 0x57, 0x00, 0x67, 0x87, 0x32, 0x6f, 0xf6,
-  0x4a, 0xda, 0x82, 0x5c, 0x6c, 0xe9, 0x13, 0xe8, 0x2f, 0x1d, 0x25, 0xca,
-  0x9a, 0xc9, 0x0d, 0x59, 0x85, 0xe6, 0xa6, 0x94, 0xb2, 0xa6, 0x99, 0x8a,
-  0x2c, 0xab, 0x68, 0xc3, 0xb5, 0x3c, 0x6b, 0xab, 0xcc, 0xe9, 0xf4, 0x29,
-  0x28, 0xe0, 0xca, 0xab, 0x5f, 0x3a, 0x6f, 0xa5, 0xce, 0xd5, 0xf9, 0xca,
-  0x3c, 0xbc, 0x99, 0xb1, 0x7d, 0x35, 0xd6, 0x1b, 0x08, 0x3c, 0xd2, 0x82,
-  0xc3, 0x20, 0xe6, 0x0b, 0x0a, 0xda, 0xf0, 0xda, 0xfa, 0xc6, 0xa4, 0x2c,
-  0x03, 0x68, 0x0a, 0x0b, 0x0b, 0x01, 0x49, 0xd2, 0x4b, 0xc1, 0xa2, 0x71,
-  0x5a, 0xd8, 0x00, 0x70, 0x21, 0x4c, 0x36, 0x80, 0x0c, 0xa0, 0xdf, 0xbf,
-  0xff, 0xca, 0xd5, 0x44, 0x36, 0xa3, 0x33, 0xb7, 0x21, 0x22, 0x16, 0x06,
-  0xec, 0x24, 0x22, 0xc0, 0x87, 0x9d, 0x7c, 0x18, 0x7f, 0x75, 0x4b, 0xe8,
-  0xfa, 0xc7, 0x5f, 0xdd, 0x2a, 0x69, 0xa1, 0xe0, 0x2e, 0xc0, 0x7c, 0xef,
-  0x70, 0x5c, 0xfc, 0xaa, 0xb7, 0xa2, 0x70, 0x32, 0xfe, 0xfd, 0x90, 0x3c,
-  0xb9, 0x10, 0x82, 0x54, 0xb1, 0xcc, 0x76, 0x4d, 0xb9, 0xc1, 0xe6, 0x28,
-  0x67, 0xc6, 0x86, 0x95, 0x3b, 0xff, 0xdc, 0x91, 0x43, 0x9e, 0x4d, 0x3e,
-  0xb3, 0xe2, 0x17, 0x2e, 0x3b, 0x2b, 0xc9, 0x2e, 0x0c, 0xe8, 0x6b, 0xcf,
-  0xef, 0x2a, 0x4d, 0x9c, 0x41, 0x56, 0xda, 0xab, 0xe0, 0x5f, 0xdd, 0x99,
-  0x47, 0xaa, 0x70, 0xef, 0x62, 0x9d, 0x88, 0x9f, 0x27, 0x91, 0xd2, 0xdd,
-  0x98, 0xb4, 0x7a, 0x38, 0xf1, 0x59, 0x4e, 0x3b, 0xdf, 0xea, 0xcc, 0x7b,
-  0x0d, 0x1c, 0x90, 0x02, 0xfb, 0xfb, 0x37, 0x43, 0xfd, 0x55, 0x28, 0x02,
-  0xbf, 0x0e, 0x45, 0x0b, 0x27, 0xd4, 0x79, 0xc0, 0xa6, 0x4f, 0xed, 0x6c,
-  0xb7, 0x43, 0x5e, 0x60, 0x68, 0x1f, 0x3a, 0x5a, 0xc1, 0xf9, 0xd8, 0xb5,
-  0x9c, 0x7d, 0x35, 0xb5, 0xbb, 0x7b, 0x17, 0xec, 0x4e, 0xdf, 0x86, 0xf1,
-  0x5f, 0x45, 0x43, 0x5a, 0xbb, 0x4b, 0xa1, 0x13, 0xd1, 0x60, 0x60, 0xde,
-  0x7a, 0xcd, 0xc0, 0xda, 0xbc, 0xa4, 0x36, 0xc4, 0x11, 0x86, 0xea, 0x78,
-  0x4c, 0x67, 0x8f, 0x2e, 0x35, 0x11, 0xc4, 0x88, 0x82, 0x64, 0xa4, 0x22,
-  0xd9, 0xc9, 0x9a, 0x67, 0x73, 0xa5, 0x26, 0xf5, 0x3b, 0x26, 0xb5, 0x24,
-  0x1f, 0x6f, 0x64, 0xa3, 0xb8, 0x50, 0x98, 0x25, 0xef, 0x0e, 0xba, 0x05,
-  0x10, 0xcb, 0x54, 0x90, 0x8c, 0x07, 0x66, 0x9b, 0x11, 0x21, 0x84, 0x96,
-  0x3c, 0xbe, 0x3f, 0xa0, 0x4e, 0x17, 0xae, 0xbf, 0xb5, 0x15, 0x7a, 0x5f,
-  0x6a, 0xab, 0x34, 0xd4, 0x73, 0x00, 0x71, 0xcc, 0xf0, 0x65, 0x44, 0xfb,
-  0xb3, 0x87, 0x69, 0xee, 0x8f, 0x48, 0x8b, 0x32, 0x42, 0x29, 0xd1, 0x35,
-  0x75, 0xaa, 0xa7, 0xbb, 0x42, 0xc2, 0xe7, 0x4c, 0x47, 0x72, 0x83, 0xea,
-  0x36, 0xcd, 0x08, 0x98, 0xaa, 0x70, 0x77, 0xfa, 0xce, 0xce, 0xa9, 0xc7,
-  0x70, 0x56, 0x57, 0xfc, 0xbb, 0x88, 0x92, 0x57, 0x42, 0xba, 0xaa, 0x32,
-  0x5c, 0x7e, 0x9d, 0xb8, 0x28, 0xa1, 0x3a, 0x3e, 0x22, 0xb2, 0x15, 0xad,
-  0x05, 0x22, 0xbe, 0x5e, 0xfd, 0x5d, 0xea, 0xaf, 0x25, 0x6d, 0x0d, 0x9d,
-  0x31, 0xda, 0x77, 0xcc, 0x0e, 0xc0, 0x79, 0xc0, 0x0d, 0x53, 0x46, 0x15,
-  0x70, 0x78, 0x5d, 0x3b, 0x10, 0xd6, 0x54, 0x48, 0xb9, 0x21, 0x75, 0x1e,
-  0x79, 0xaf, 0x8a, 0x19, 0x3c, 0x0c, 0x33, 0x2c, 0xe5, 0x68, 0x1a, 0xe1,
-  0xd5, 0x43, 0x56, 0xbb, 0x4b, 0x5c, 0x19, 0x8a, 0xf0, 0x21, 0x7a, 0x8d,
-  0x88, 0x05, 0x3f, 0x41, 0xbf, 0x39, 0x68, 0x86, 0xa1, 0x46, 0x07, 0x3c,
-  0x19, 0x92, 0x2a, 0x14, 0xcb, 0xbb, 0xcd, 0x12, 0xba, 0x97, 0xd3, 0x91,
-  0xa8, 0x43, 0x59, 0x52, 0xb8, 0x3f, 0xf0, 0x3a, 0x03, 0x58, 0xe8, 0x5f,
-  0xd4, 0x2f, 0xbc, 0x3c, 0x47, 0x0f, 0x92, 0xf3, 0x2f, 0x37, 0xe1, 0xfa,
-  0xeb, 0x44, 0xd2, 0xd2, 0x2e, 0x99, 0x57, 0x67, 0x49, 0xbb, 0xc7, 0xd5,
-  0x2b, 0xf4, 0x8d, 0xb0, 0x92, 0xd6, 0x1c, 0x09, 0x8a, 0x45, 0x3a, 0xf6,
-  0x6e, 0x60, 0xd6, 0xef, 0xce, 0xde, 0x67, 0x88, 0x3c, 0x21, 0xdb, 0x66,
-  0x00, 0x88, 0x5e, 0x89, 0x22, 0xeb, 0x64, 0x9c, 0x45, 0x99, 0x48, 0x1c,
-  0xa4, 0x02, 0x38, 0xef, 0x42, 0x00, 0x77, 0x1a, 0xd4, 0x97, 0xaf, 0x7b,
-  0x00, 0xf3, 0xe9, 0x3d, 0xde, 0x2b, 0x69, 0x02, 0xbf, 0x6a, 0xf0, 0x49,
-  0xb3, 0x5f, 0xbf, 0xc3, 0xf8, 0x0d, 0x20, 0x81, 0x09, 0x3f, 0x5b, 0x4a,
-  0x21, 0xa7, 0x55, 0x66, 0xbf, 0xaa, 0x73, 0x07, 0x3d, 0x20, 0xbd, 0xa9,
-  0xb5, 0xa6, 0xfb, 0x40, 0x3b, 0x8a, 0x70, 0xbc, 0x68, 0xbb, 0x45, 0x4b,
-  0xd8, 0xda, 0xb4, 0x93, 0xb8, 0xa1, 0x39, 0x9c, 0xe9, 0x44, 0x1b, 0xc0,
-  0x39, 0x24, 0x4d, 0x2d, 0x73, 0x0d, 0x5f, 0xc0, 0x13, 0xb6, 0xea, 0x3a,
-  0x04, 0x4a, 0x87, 0x14, 0x2c, 0xed, 0xcf, 0x1b, 0x92, 0x37, 0xc2, 0xa2,
-  0xe4, 0x16, 0xf6, 0x2e, 0x2f, 0x6b, 0x97, 0x28, 0x07, 0x70, 0x41, 0xe3,
-  0xdb, 0x9e, 0x09, 0x79, 0xb8, 0xad, 0x13, 0xe6, 0xae, 0x33, 0xf4, 0x54,
-  0xf7, 0x03, 0x89, 0x25, 0x42, 0x0f, 0xfa, 0x45, 0x3c, 0x5a, 0xf2, 0x6a,
-  0x9e, 0xf2, 0xd4, 0x0a, 0xf6, 0xd3, 0x11, 0x9e, 0x42, 0x0f, 0x62, 0xd2,
-  0xfe, 0x69, 0xfa, 0x7e, 0x57, 0xe4, 0x2f, 0x19, 0xc6, 0x3f, 0x5d, 0xab,
-  0xdf, 0x59, 0xf1, 0x99, 0x7b, 0xdd, 0x7b, 0x06, 0x5e, 0xbd, 0xf2, 0xbf,
-  0x1e, 0xa5, 0xef, 0x66, 0x5a, 0x95, 0xd4, 0x42, 0x01, 0x3b, 0x6c, 0xd5,
-  0x0d, 0x52, 0x4b, 0xa9, 0xb3, 0x78, 0x8f, 0x51, 0xa2, 0x19, 0xda, 0x78,
-  0xbd, 0x40, 0x41, 0xc4, 0xb7, 0xf1, 0xfe, 0xf4, 0x36, 0x53, 0xd7, 0x08,
-  0x88, 0xd1, 0xac, 0xf7, 0xc0, 0xde, 0x8d, 0xb2, 0x87, 0xc5, 0x80, 0x9c,
-  0x30, 0x5b, 0xe0, 0x25, 0xa1, 0x16, 0xa1, 0x31, 0x5a, 0x79, 0x2a, 0xbf,
-  0x7a, 0xa1, 0x42, 0x4c, 0x60, 0x14, 0x9a, 0xaf, 0x92, 0x2e, 0xa5, 0x4c,
-  0x33, 0xad, 0x8c, 0xe3, 0xa7, 0xc0, 0x77, 0x0f, 0x79, 0x11, 0x52, 0x60,
-  0xa4, 0x8a, 0x0b, 0x4d, 0x68, 0x0b, 0x3f, 0xce, 0xbf, 0x77, 0x5f, 0x13,
-  0x5f, 0x5c, 0x82, 0x44, 0xb9, 0x5a, 0x4e, 0x12, 0xb2, 0x29, 0x5a, 0xa0,
-  0x52, 0x83, 0x61, 0x60, 0xe0, 0x21, 0x1a, 0x8f, 0xf0, 0x00, 0x1f, 0x67,
-  0x8b, 0x39, 0x69, 0x86, 0xa9, 0x4d, 0xe5, 0x56, 0x84, 0x2b, 0x06, 0xf5,
-  0xa1, 0x11, 0x6b, 0x9e, 0xd5, 0x98, 0x27, 0x9a, 0x03, 0xb3, 0x0e, 0x88,
-  0x40, 0x79, 0x6e, 0xe2, 0xb9, 0x97, 0x79, 0x2d, 0x8e, 0x65, 0x6f, 0xd5,
-  0xf4, 0x03, 0xcc, 0x1b, 0x5a, 0xc6, 0xb6, 0x46, 0xa3, 0x5d, 0x4a, 0xcf,
-  0x63, 0x58, 0x8d, 0x77, 0x49, 0x35, 0xe8, 0x20, 0x97, 0x20, 0x88, 0xfb,
-  0xa6, 0x93, 0xdb, 0x20, 0x5e, 0xaa, 0x25, 0xcc, 0x69, 0x7d, 0x5a, 0x01,
-  0xd2, 0x1a, 0x0d, 0xc1, 0x14, 0xbf, 0x49, 0xf2, 0xb4, 0x43, 0x42, 0xbe,
-  0x31, 0x14, 0xc4, 0xa9, 0xab, 0x4c, 0xa0, 0x9f, 0x46, 0x8a, 0x9e, 0x33,
-  0x8d, 0x7b, 0xe9, 0xa7, 0x43, 0x8f, 0x27, 0x16, 0xfb, 0x61, 0xd7, 0x53,
-  0x8c, 0xeb, 0x1d, 0x0f, 0x28, 0xd6, 0xcf, 0x3e, 0x9c, 0xfc, 0xac, 0x9c,
-  0xf3, 0xad, 0xe2, 0x9b, 0x9b, 0xd9, 0xd7, 0xd6, 0x6b, 0x90, 0x57, 0x41,
-  0x4d, 0x32, 0x00, 0xbf, 0x3e, 0x42, 0x81, 0x56, 0x34, 0xb9, 0x79, 0x2b,
-  0xb8, 0xd0, 0xd7, 0xc1, 0xb2, 0x2e, 0xbb, 0xb6, 0x25, 0x83, 0xbd, 0xd8,
-  0x46, 0x17, 0x8d, 0xcc, 0xa0, 0xb0, 0x2a, 0x01, 0x41, 0x11, 0xa5, 0x11,
-  0x14, 0x4d, 0x72, 0x90, 0xc8, 0x04, 0xf4, 0x1e, 0xcf, 0x48, 0x12, 0xa0,
-  0x44, 0x48, 0x11, 0x5b, 0x4a, 0xe7, 0x83, 0xd2, 0x4b, 0x19, 0x76, 0x4e,
-  0x03, 0x57, 0x74, 0xd5, 0x98, 0x79, 0xd8, 0x19, 0x08, 0x2b, 0xfc, 0xba,
-  0x56, 0x3f, 0xab, 0x77, 0xa7, 0x7c, 0x19, 0xfe, 0x19, 0x28, 0x11, 0xbc,
-  0x47, 0x2d, 0xfc, 0xae, 0x81, 0x92, 0x9d, 0x33, 0xc9, 0xf7, 0xc6, 0x51,
-  0x9b, 0x34, 0xe7, 0x7c, 0x6a, 0xe4, 0xd9, 0xdf, 0x67, 0xc0, 0x01, 0xc5,
-  0xc4, 0x96, 0x0a, 0x91, 0xbe, 0xc7, 0xe4, 0xf4, 0x8e, 0xc7, 0x27, 0xc3,
-  0x59, 0x2b, 0xb0, 0x71, 0xfa, 0xbe, 0x65, 0x64, 0xbb, 0xcb, 0x35, 0x2d,
-  0x33, 0x1c, 0xd2, 0x26, 0xf4, 0xfa, 0xd5, 0x87, 0xa2, 0x51, 0x1a, 0xa9,
-  0xc7, 0xcc, 0x54, 0x83, 0xb1, 0x6c, 0xc4, 0x04, 0xab, 0x9f, 0x09, 0xfe,
-  0x86, 0x51, 0xbf, 0xe5, 0xd7, 0xdd, 0x1e, 0x14, 0xff, 0xaa, 0xd2, 0xd4,
-  0x58, 0x09, 0x84, 0x93, 0xf1, 0x6d, 0x73, 0x5f, 0xaa, 0x37, 0x12, 0xca,
-  0x16, 0x94, 0x00, 0x06, 0x7f, 0xdd, 0x0c, 0x6a, 0x0c, 0x12, 0xdb, 0xa6,
-  0xcc, 0xb5, 0xef, 0x34, 0x53, 0x92, 0x65, 0x79, 0xd7, 0x2d, 0x60, 0x0c,
-  0xf7, 0xcb, 0x0c, 0x20, 0x13, 0xe3, 0xaf, 0x6a, 0x9e, 0x4b, 0xcf, 0xdf,
-  0x96, 0xf2, 0xd4, 0x82, 0xbf, 0xe6, 0xfb, 0x88, 0x14, 0x46, 0xd5, 0x94,
-  0x7c, 0x9d, 0xd2, 0x11, 0x01, 0xc0, 0x21, 0x1a, 0x94, 0xed, 0xba, 0x98,
-  0xc3, 0x32, 0xa0, 0x84, 0xcb, 0xa4, 0x20, 0x2b, 0x2d, 0x1c, 0x19, 0xc1,
-  0x62, 0xe8, 0x26, 0xac, 0x09, 0xad, 0x4a, 0x0d, 0xd7, 0xad, 0xfe, 0xbf,
-  0x35, 0x8f, 0xb5, 0x47, 0x52, 0x79, 0x96, 0xad, 0xe3, 0xa6, 0xe2, 0x8f,
-  0x39, 0xfb, 0x6e, 0xc5, 0x72, 0xf6, 0x14, 0xd6, 0x92, 0x76, 0x9d, 0x85,
-  0xe6, 0xd9, 0xc4, 0xe7, 0x08, 0xdb, 0x75, 0xb7, 0x03, 0x4a, 0x35, 0x83,
-  0x4e, 0x5f, 0x18, 0xf8, 0x15, 0x0c, 0xbf, 0x95, 0x4e, 0x36, 0x7e, 0xaa,
-  0x61, 0x7b, 0xf3, 0xe7, 0xc6, 0xc7, 0x56, 0x36, 0x4c, 0xb9, 0xef, 0x77,
-  0xba, 0x99, 0x5c, 0x9e, 0xac, 0xf3, 0xbc, 0xa2, 0xfb, 0xd8, 0x59, 0x6c,
-  0x5a, 0x05, 0x52, 0xbf, 0x33, 0x48, 0xd5, 0xf3, 0x97, 0x86, 0xfd, 0xd7,
-  0x15, 0x5b, 0xe5, 0xe6, 0x04, 0x04, 0xf1, 0x8d, 0xa3, 0x10, 0x01, 0xba,
-  0xa7, 0xa3, 0x1b, 0x4b, 0x6e, 0xae, 0xaf, 0x2f, 0x05, 0x06, 0xdf, 0x2b,
-  0xd2, 0x1b, 0x89, 0xc9, 0x8e, 0xca, 0x19, 0x2a, 0xaa, 0xd2, 0x58, 0x77,
-  0x4b, 0x23, 0x5b, 0x1c, 0x61, 0x04, 0x7a, 0xe1, 0xbd, 0x47, 0x78, 0xcd,
-  0x9c, 0xf6, 0x55, 0xf5, 0xa6, 0xb6, 0x60, 0x92, 0x2e, 0x26, 0x08, 0x15,
-  0x83, 0x30, 0x16, 0x50, 0x21, 0x5c, 0x0f, 0xfd, 0x06, 0xd5, 0x08, 0x80,
-  0x34, 0x44, 0xd5, 0x56, 0x79, 0xaa, 0x82, 0x16, 0x57, 0x6b, 0x3e, 0x3c,
-  0xfa, 0xec, 0x60, 0xbf, 0x36, 0x39, 0x78, 0xc5, 0xdc, 0xdd, 0x23, 0xe9,
-  0x62, 0x75, 0x97, 0x74, 0x0d, 0xeb, 0xe3, 0x68, 0x98, 0x2d, 0xc2, 0x97,
-  0x5e, 0x69, 0x72, 0xd1, 0xb2, 0x33, 0xa7, 0x7c, 0x7a, 0x51, 0x95, 0x41,
-  0x39, 0x4d, 0xb1, 0x41, 0xc1, 0x04, 0x10, 0x38, 0x84, 0x04, 0x21, 0x25,
-  0xd5, 0x45, 0x94, 0xa0, 0x48, 0xff, 0x40, 0x17, 0xc8, 0x6a, 0x73, 0xfc,
-  0x5c, 0x75, 0x4b, 0x39, 0x79, 0x56, 0xab, 0xd4, 0x3d, 0x8b, 0xad, 0x58,
-  0xb1, 0xd9, 0x83, 0xf3, 0xcc, 0xe1, 0xb9, 0xb6, 0x45, 0xc5, 0xdd, 0x31,
-  0x63, 0xc7, 0x4c, 0x1b, 0x67, 0x91, 0x2e, 0x7e, 0x9f, 0xa9, 0x45, 0x23,
-  0xf1, 0x1c, 0x72, 0x9e, 0x23, 0x6a, 0xc3, 0x12, 0x88, 0x73, 0x23, 0x33,
-  0xe0, 0xd6, 0x08, 0x86, 0x40, 0x3a, 0x4e, 0x8e, 0xc4, 0xba, 0x35, 0x22,
-  0x72, 0x9a, 0x2a, 0x55, 0x50, 0x56, 0x06, 0x49, 0x21, 0x37, 0x2a, 0x25,
-  0xf5, 0x61, 0xc0, 0xfd, 0x9e, 0xed, 0x17, 0x5c, 0x51, 0x1c, 0x61, 0xc0,
-  0x21, 0x1a, 0x94, 0xcd, 0xa2, 0x89, 0x69, 0xa2, 0xb0, 0xd4, 0x48, 0x61,
-  0x28, 0x62, 0x32, 0xe4, 0x4a, 0x41, 0xa2, 0x25, 0x5d, 0xa0, 0xcb, 0x2c,
-  0x0c, 0x4a, 0x80, 0x27, 0x06, 0xc0, 0x47, 0x01, 0xcc, 0x55, 0x10, 0x7d,
-  0x4a, 0xea, 0x66, 0x80, 0xe9, 0xca, 0x81, 0x54, 0x89, 0x62, 0x91, 0x2a,
-  0x4c, 0x2f, 0x08, 0xaa, 0xe5, 0x05, 0x0e, 0x46, 0x53, 0x54, 0x57, 0x19,
-  0xb9, 0x65, 0x61, 0x64, 0xad, 0x61, 0x38, 0x70, 0xed, 0xed, 0x95, 0x1b,
-  0x47, 0xf0, 0xc8, 0xfb, 0x37, 0x3b, 0xa6, 0x0a, 0x94, 0x5d, 0x97, 0x34,
-  0x94, 0xc1, 0x84, 0xcf, 0x1a, 0xdd, 0xba, 0x8e, 0xb0, 0x04, 0x03, 0x4b,
-  0xd1, 0x4b, 0x77, 0x5f, 0xb6, 0x52, 0xbe, 0xe8, 0x90, 0xe0, 0x71, 0xbc,
-  0x81, 0x84, 0x68, 0x5f, 0x94, 0xfc, 0x47, 0xe7, 0x39, 0xb3, 0x5a, 0x66,
-  0x55, 0x2b, 0x66, 0xa3, 0xa5, 0x72, 0x7a, 0x12, 0xe4, 0x26, 0x5d, 0xa4,
-  0x4b, 0x34, 0xca, 0xaa, 0x37, 0xb4, 0xc2, 0x8c, 0xd0, 0x0b, 0x9c, 0x13,
-  0x4f, 0x8f, 0x9d, 0x1b, 0xd1, 0x3b, 0x37, 0x23, 0xc7, 0xb0, 0x55, 0x8c,
-  0x9e, 0x5e, 0xed, 0x81, 0xbe, 0x0a, 0xcd, 0x50, 0x79, 0x2d, 0x5a, 0xc0,
-  0xe0, 0xd7, 0x67, 0x16, 0x91, 0x93, 0x4b, 0x56, 0xa6, 0xb9, 0x80, 0x5e,
-  0x2a, 0xa0, 0x2c, 0x2b, 0x57, 0x0b, 0x4a, 0x51, 0x91, 0x2d, 0x53, 0x9e,
-  0x6c, 0x72, 0xf2, 0xbb, 0xde, 0x80, 0x10, 0x01, 0x60, 0x3b, 0x16, 0x3b,
-  0x55, 0x6b, 0xa8, 0xb2, 0x16, 0xea, 0x81, 0x3b, 0x91, 0x4d, 0x1c, 0x0f,
-  0x94, 0xe6, 0x81, 0xd2, 0x51, 0x91, 0x9c, 0x1a, 0xd5, 0x08, 0xd3, 0x76,
-  0x3d, 0x07, 0xbf, 0xab, 0x5c, 0xb6, 0x25, 0x23, 0x90, 0xf4, 0x23, 0x64,
-  0x8d, 0x10, 0x92, 0x17, 0x90, 0x23, 0x0b, 0x6b, 0x88, 0x6a, 0xbd, 0xf7,
-  0x0a, 0x46, 0x60, 0x99, 0xa7, 0xb1, 0x28, 0x48, 0x10, 0xa1, 0x2b, 0x7a,
-  0x4a, 0x96, 0xda, 0x55, 0x13, 0xae, 0x45, 0x16, 0x92, 0xea, 0xd3, 0x09,
-  0x72, 0xaf, 0x50, 0x68, 0xef, 0xf2, 0x58, 0x1d, 0xea, 0x60, 0xb0, 0x89,
-  0x8b, 0x5d, 0x63, 0x09, 0xe2, 0xd3, 0x7f, 0xb5, 0xf1, 0xf8, 0x79, 0x33,
-  0x7a, 0x23, 0x06, 0x2b, 0x2c, 0xbd, 0x47, 0x6d, 0x92, 0x35, 0x31, 0x74,
-  0x8a, 0x69, 0x74, 0x94, 0xc5, 0x69, 0x10, 0x44, 0xd1, 0x72, 0x35, 0x62,
-  0x96, 0x6d, 0x9b, 0x79, 0xff, 0xdb, 0xfd, 0xff, 0xdf, 0xfe, 0x6f, 0xcf,
-  0xf1, 0xfa, 0x3c, 0x33, 0xb8, 0x31, 0xc0, 0x21, 0x1a, 0x8f, 0x78, 0x4e,
-  0x0f, 0xcf, 0xff, 0x3b, 0x4f, 0xb0, 0xd1, 0x18, 0xc4, 0x21, 0x72, 0x19,
-  0x9e, 0x72, 0x65, 0xe5, 0x55, 0x92, 0xa2, 0xe5, 0x49, 0x5a, 0x6b, 0xad,
-  0xe0, 0x69, 0xd5, 0x85, 0x93, 0xfe, 0x0f, 0xf2, 0x73, 0x7f, 0xa2, 0xe9,
-  0x5f, 0xd0, 0xfb, 0xdf, 0xff, 0xfa, 0x29, 0xae, 0x47, 0x5b, 0x83, 0xfe,
-  0xbc, 0x32, 0xf7, 0xfa, 0x77, 0x96, 0xc7, 0xc9, 0xba, 0xb0, 0xa6, 0x96,
-  0x54, 0x6d, 0xae, 0x6a, 0xf5, 0x06, 0x6e, 0x05, 0xc5, 0x74, 0x5b, 0x88,
-  0xd5, 0xd8, 0xee, 0xf0, 0x75, 0xe0, 0xcc, 0x2e, 0x31, 0x35, 0x4d, 0x26,
-  0x5b, 0x50, 0xa0, 0xd6, 0xde, 0xed, 0x2c, 0xe5, 0x55, 0xee, 0xa2, 0x94,
-  0x69, 0x77, 0xb9, 0x82, 0xab, 0x64, 0x2c, 0xe6, 0xd2, 0xf2, 0x50, 0x07,
-  0x53, 0x14, 0x3c, 0xce, 0x0c, 0xad, 0x83, 0x09, 0xac, 0x93, 0xf7, 0xd7,
-  0xf5, 0x2f, 0x0f, 0x8f, 0x9b, 0xbe, 0xc3, 0xda, 0x91, 0x60, 0x14, 0x12,
-  0x98, 0xfe, 0x60, 0xbb, 0xc9, 0x28, 0x53, 0x5b, 0x57, 0xca, 0x30, 0xc7,
-  0x1d, 0xb9, 0x74, 0x23, 0x39, 0x78, 0x42, 0x00, 0xa7, 0xd3, 0x41, 0x6b,
-  0xfe, 0xf2, 0x8e, 0x77, 0x38, 0x50, 0x58, 0x10, 0xe5, 0xad, 0xfb, 0xaa,
-  0xef, 0x91, 0x34, 0xb3, 0x56, 0x91, 0x98, 0xaa, 0xf6, 0x90, 0x11, 0x00,
-  0x14, 0x44, 0x31, 0x72, 0xdf, 0xb7, 0x94, 0x18, 0x6f, 0x00, 0x99, 0xa4,
-  0x59, 0xd8, 0x76, 0x44, 0x08, 0x10, 0x82, 0x01, 0x40, 0x90, 0x80, 0x24,
-  0x10, 0x10, 0x98, 0x46, 0x46, 0x37, 0x7c, 0xd7, 0xb7, 0x68, 0x8d, 0x57,
-  0xc5, 0x3c, 0x9a, 0x68, 0x16, 0xce, 0x99, 0x70, 0xc7, 0x6e, 0x19, 0x2d,
-  0xe3, 0xbc, 0xd7, 0xac, 0x9e, 0x2e, 0xa2, 0xaf, 0x6c, 0xc1, 0x61, 0x3f,
-  0x2f, 0xf0, 0xf6, 0x38, 0x3e, 0xc7, 0x8b, 0xd3, 0x73, 0x7b, 0x2f, 0xae,
-  0xc9, 0x80, 0xf5, 0xc7, 0xc2, 0x92, 0x06, 0x43, 0xa4, 0x7c, 0x59, 0x08,
-  0x6d, 0xbe, 0x7b, 0xb2, 0xc8, 0x00, 0x1f, 0x1c, 0xb6, 0xbc, 0xf4, 0x6c,
-  0x49, 0xf6, 0x73, 0x0b, 0x37, 0xcc, 0x19, 0x29, 0x63, 0xb1, 0xdf, 0x7f,
-  0x94, 0xc4, 0x38, 0xc7, 0xff, 0x72, 0xc0, 0x35, 0xd7, 0x64, 0x3b, 0x10,
-  0xf4, 0xc4, 0x73, 0xcc, 0x70, 0x3c, 0xc1, 0xc8, 0x29, 0xcc, 0xc3, 0x49,
-  0xb9, 0xb7, 0xe6, 0x11, 0xa3, 0xe9, 0xa5, 0xea, 0x8e, 0x5b, 0xd9, 0xbc,
-  0x76, 0x7c, 0xe3, 0x70, 0x62, 0x33, 0xef, 0x03, 0x15, 0x0d, 0xf7, 0x57,
-  0x70, 0x2b, 0xd5, 0x75, 0x49, 0x30, 0x08, 0x78, 0xdd, 0x92, 0x20, 0x5d,
-  0xfa, 0xc6, 0xe3, 0x6a, 0xe2, 0x60, 0x00, 0x62, 0x00, 0xd7, 0xb5, 0xa8,
-  0xa4, 0x03, 0x07, 0x21, 0x1a, 0x8f, 0xd0, 0x07, 0x0a, 0x00, 0x0f, 0x43,
-  0x4b, 0x83, 0xd0, 0x98, 0xc3, 0x07, 0x0a, 0xe7, 0x4b, 0x8a, 0x72, 0xb8,
-  0x46, 0x85, 0x5d, 0xad, 0xe7, 0x18, 0x3a, 0x74, 0x0e, 0x7b, 0xcd, 0x73,
-  0x6f, 0x2c, 0xf5, 0xd2, 0x5d, 0x77, 0xe8, 0xbf, 0x7b, 0xc9, 0x85, 0xdb,
-  0x6d, 0x23, 0x75, 0x47, 0x55, 0x5b, 0x80, 0x90, 0xcb, 0x5d, 0x85, 0x48,
-  0xdb, 0xc9, 0x8d, 0x55, 0xc7, 0x5d, 0xa3, 0xb7, 0xa5, 0xe0, 0xc0, 0xfc,
-  0xeb, 0x06, 0xd1, 0xed, 0x3f, 0xed, 0x36, 0xdb, 0xaf, 0xba, 0xdf, 0x43,
-  0x87, 0xb9, 0xc5, 0x7d, 0xf4, 0x07, 0x1d, 0xc7, 0x79, 0x4a, 0x9d, 0x25,
-  0x18, 0x8d, 0x1d, 0xd7, 0xa7, 0x3d, 0x92, 0x37, 0x75, 0x20, 0x9a, 0x85,
-  0xd0, 0xe9, 0x94, 0x06, 0xe4, 0xac, 0xe6, 0xae, 0x96, 0xfd, 0x55, 0x77,
-  0xf3, 0x71, 0x0b, 0x1c, 0xe4, 0x3a, 0x1e, 0xdf, 0xee, 0xc6, 0xd0, 0x20,
-  0x1c, 0x1e, 0x0c, 0x7a, 0x7f, 0x6c, 0xfb, 0xb3, 0xf2, 0x9c, 0xd4, 0xbd,
-  0x85, 0x9f, 0xcb, 0x2f, 0xdb, 0x81, 0x10, 0x8d, 0xd2, 0x00, 0x4b, 0x47,
-  0x3d, 0x2f, 0xde, 0x3d, 0x3a, 0xdf, 0x88, 0x17, 0x85, 0x69, 0xe2, 0x32,
-  0xd7, 0xfd, 0xb6, 0x10, 0x56, 0x70, 0x42, 0xab, 0x45, 0xd2, 0xdc, 0x05,
-  0xe6, 0x00, 0x7c, 0xb4, 0x2f, 0x66, 0xdf, 0xbc, 0x5f, 0x2f, 0xd7, 0xfc,
-  0x44, 0x02, 0x6a, 0xe1, 0x46, 0x81, 0xb1, 0x50, 0xc2, 0x61, 0xb7, 0x76,
-  0xc9, 0x8b, 0xc4, 0x8b, 0x5a, 0x6b, 0x26, 0x90, 0x01, 0xaf, 0xa8, 0x19,
-  0x13, 0x4a, 0xc0, 0xb1, 0x01, 0x1d, 0x67, 0xe6, 0x36, 0xd6, 0x5f, 0xd8,
-  0x7b, 0xce, 0x92, 0x45, 0xb7, 0x32, 0xdc, 0x67, 0x9e, 0xac, 0x98, 0xcd,
-  0x97, 0xa2, 0xd2, 0x7f, 0x3f, 0xb6, 0x22, 0x8a, 0xb7, 0xe6, 0xe6, 0xab,
-  0x8e, 0x63, 0x4a, 0xaf, 0xf6, 0xbf, 0x13, 0xbc, 0xe5, 0x42, 0x6d, 0xf8,
-  0xdb, 0xa1, 0xba, 0x85, 0x41, 0xae, 0xb4, 0x40, 0x23, 0x00, 0x8a, 0x61,
-  0x47, 0x18, 0x71, 0x40, 0x9e, 0x27, 0x3a, 0xfd, 0x4f, 0x5d, 0xbc, 0xef,
-  0x1c, 0x7b, 0x09, 0xc6, 0xae, 0x6f, 0x98, 0xaa, 0xa3, 0x01, 0xf0, 0x9a,
-  0x86, 0x81, 0x95, 0xb3, 0xca, 0xd6, 0xb1, 0x22, 0x88, 0x90, 0x25, 0xf6,
-  0x5b, 0x0a, 0x4b, 0x71, 0x40, 0x21, 0xb5, 0x6c, 0xd2, 0x91, 0x27, 0x8c,
-  0x44, 0xce, 0x5a, 0x64, 0x81, 0x6d, 0x06, 0x30, 0x0a, 0x81, 0x94, 0x1f,
-  0x78, 0x6c, 0x7e, 0x81, 0x3f, 0xf4, 0xb7, 0x53, 0x53, 0xd3, 0x2c, 0x97,
-  0x94, 0xb6, 0x15, 0xe1, 0x86, 0xbc, 0x3a, 0x5f, 0xc3, 0x6f, 0x8c, 0xad,
-  0x67, 0xe3, 0x8b, 0x69, 0x9e, 0x7b, 0x1d, 0xda, 0x2c, 0x0a, 0x2c, 0xab,
-  0xca, 0xaf, 0xfd, 0xf2, 0xe3, 0xf7, 0xdd, 0x34, 0xc3, 0x76, 0xd7, 0xd5,
-  0xdf, 0xe3, 0x9d, 0xbb, 0xe2, 0xc9, 0x02, 0x3d, 0xda, 0x57, 0xe8, 0xd9,
-  0x43, 0x64, 0xf7, 0x2c, 0xec, 0x4e, 0x7d, 0x8c, 0xc2, 0xee, 0x58, 0x60,
-  0x30, 0x9e, 0x07, 0xea, 0x2d, 0xa7, 0x09, 0x7a, 0xf1, 0xb5, 0x48, 0x70,
-  0x5d, 0xab, 0x6a, 0x6d, 0x61, 0xb7, 0xfe, 0xc0, 0x07, 0xce, 0x01, 0xf2,
-  0xfd, 0x62, 0xf9, 0xbf, 0xa0, 0x03, 0x07, 0x21, 0x1a, 0x8b, 0xc8, 0x07,
-  0x0c, 0x00, 0x01, 0x3d, 0x66, 0xa6, 0xa9, 0x18, 0x43, 0x15, 0x56, 0xf3,
-  0xe8, 0x06, 0xe8, 0x16, 0x43, 0x81, 0xfa, 0xf6, 0xa2, 0xf4, 0x68, 0x16,
-  0xb6, 0x76, 0x15, 0xf3, 0x34, 0xc4, 0xe1, 0x5e, 0x78, 0xc3, 0x10, 0x99,
-  0x3b, 0x0a, 0x17, 0x28, 0xd5, 0x20, 0xe3, 0xb3, 0xa5, 0xc6, 0x4d, 0x1a,
-  0x4d, 0x4f, 0x1b, 0x02, 0x98, 0xb0, 0x32, 0xc3, 0x65, 0x61, 0xcc, 0x3a,
-  0xcf, 0x90, 0x6f, 0x2c, 0x00, 0x34, 0xa5, 0xeb, 0xb4, 0xa3, 0xf0, 0x37,
-  0x49, 0x19, 0x20, 0x2e, 0xe7, 0x78, 0x06, 0x16, 0x10, 0x27, 0x35, 0x4a,
-  0xf3, 0x85, 0xf2, 0x77, 0x74, 0xb3, 0x6f, 0xaf, 0x2c, 0xca, 0xc8, 0x97,
-  0x18, 0x4a, 0x61, 0x5a, 0x9f, 0x51, 0x25, 0x94, 0xa9, 0xc5, 0x72, 0xbe,
-  0x1a, 0x83, 0x1e, 0x7c, 0xd9, 0x91, 0xb5, 0xa2, 0x47, 0x21, 0x86, 0x49,
-  0xd8, 0x27, 0x95, 0x10, 0x38, 0x8e, 0x18, 0x98, 0x82, 0xf6, 0x8c, 0x78,
-  0xd4, 0xb2, 0xc8, 0xee, 0xa8, 0x84, 0xbb, 0xf6, 0xd6, 0x9d, 0x65, 0x49,
-  0xf7, 0x9e, 0x11, 0x27, 0x83, 0x94, 0x14, 0x92, 0xb4, 0x80, 0x00, 0x00,
-  0x93, 0x51, 0x56, 0x8c, 0x0d, 0xad, 0xbb, 0x02, 0x6e, 0xe0, 0xc3, 0x82,
-  0x00, 0x50, 0xa4, 0x31, 0x50, 0x9e, 0x1c, 0xd8, 0x4d, 0xea, 0xb2, 0xd0,
-  0xbd, 0x64, 0xb0, 0x22, 0x69, 0xd0, 0x1f, 0x6e, 0x49, 0x2b, 0x54, 0xc7,
-  0x98, 0xdd, 0x6b, 0x4f, 0xaa, 0x62, 0x52, 0x89, 0xf3, 0xb1, 0xa0, 0x39,
-  0xc8, 0x45, 0x45, 0xbd, 0x72, 0xc5, 0x10, 0xbd, 0xad, 0xba, 0x63, 0xba,
-  0xb0, 0x51, 0x67, 0x99, 0xb1, 0x34, 0x77, 0xe4, 0xb6, 0x78, 0x1c, 0x35,
-  0x08, 0xfb, 0xfa, 0x01, 0x72, 0x61, 0x95, 0x38, 0x3c, 0x5b, 0xc0, 0x34,
-  0x36, 0xda, 0xfe, 0x4b, 0x88, 0xde, 0x99, 0x4a, 0x6d, 0x30, 0x0a, 0x8d,
-  0x65, 0x3f, 0x7e, 0xaa, 0x4f, 0x43, 0x0c, 0x18, 0xb0, 0x4c, 0x32, 0x84,
-  0x72, 0x73, 0xf6, 0x03, 0xdc, 0xf9, 0x08, 0xb2, 0x03, 0x22, 0xcc, 0xa1,
-  0x07, 0x63, 0xb3, 0x45, 0x85, 0x35, 0xf1, 0x3f, 0xb1, 0xa1, 0x50, 0x6b,
-  0x75, 0x0f, 0x0a, 0x1a, 0x88, 0x9f, 0x5b, 0x4d, 0xda, 0xe2, 0x22, 0x75,
-  0xce, 0x09, 0xdf, 0xaf, 0x40, 0xb7, 0xef, 0x47, 0x6a, 0x48, 0x43, 0x7e,
-  0xa7, 0x0a, 0xd1, 0x51, 0xe3, 0xa5, 0xbc, 0x8b, 0x1b, 0x9f, 0x82, 0x85,
-  0xd0, 0x41, 0x05, 0xb0, 0x83, 0xa2, 0x30, 0x73, 0x02, 0x44, 0x00, 0x8e,
-  0x2b, 0xf1, 0xbc, 0xca, 0x82, 0xd8, 0x76, 0xad, 0x11, 0x4f, 0x45, 0xf1,
-  0x7f, 0x8f, 0x43, 0xdc, 0x00, 0xe0, 0x21, 0x1a, 0x8b, 0xc0, 0x0f, 0x8f,
-  0x41, 0xcf, 0x43, 0x52, 0x64, 0x10, 0xc5, 0xaa, 0xe3, 0x16, 0x25, 0x15,
-  0x06, 0x6a, 0xc2, 0xcb, 0x3e, 0xb3, 0x05, 0xba, 0xc0, 0x07, 0xcc, 0xa8,
-  0x32, 0x3c, 0x0e, 0xfe, 0x36, 0xe3, 0xd8, 0xb9, 0x62, 0x4f, 0x41, 0xc2,
-  0x60, 0x97, 0x99, 0x91, 0xf4, 0x2e, 0x27, 0xa0, 0xcb, 0x42, 0xa8, 0xc9,
-  0xd3, 0x30, 0xd9, 0xd3, 0xcb, 0x33, 0x00, 0x53, 0x3c, 0x63, 0xc5, 0x21,
-  0x0b, 0xf8, 0x3b, 0x80, 0x56, 0x59, 0x06, 0x76, 0xaa, 0x45, 0x97, 0x08,
-  0xf6, 0x6d, 0xfc, 0x87, 0x6f, 0x6f, 0xa5, 0x1a, 0xac, 0x7a, 0x2c, 0x72,
-  0xdd, 0x54, 0x7a, 0x71, 0xd0, 0xf8, 0x78, 0xcf, 0x7d, 0xaa, 0x97, 0x9a,
-  0xc8, 0x05, 0xbc, 0x0d, 0x10, 0xe6, 0x1a, 0xef, 0x3e, 0xcf, 0xc5, 0xb3,
-  0xdc, 0x32, 0xbb, 0x63, 0xc0, 0x07, 0x41, 0x4f, 0xc0, 0xef, 0x38, 0x69,
-  0x8d, 0x58, 0x72, 0x6f, 0xc0, 0xca, 0x24, 0x03, 0xe0, 0xa0, 0x45, 0x92,
-  0xd4, 0x49, 0x28, 0xd8, 0x00, 0x16, 0x61, 0x94, 0x2c, 0xcd, 0x24, 0xff,
-  0x94, 0xfe, 0x39, 0xa5, 0x42, 0x23, 0xe7, 0x2d, 0x16, 0xb4, 0xff, 0x55,
-  0x77, 0xfe, 0xf7, 0xc1, 0xa6, 0x8e, 0x26, 0xf0, 0x85, 0xe8, 0x00, 0x05,
-  0xb9, 0xe4, 0x0e, 0x3e, 0x11, 0x80, 0x52, 0xe0, 0x4f, 0xdc, 0x10, 0x90,
-  0x31, 0x22, 0x04, 0x46, 0x42, 0x14, 0x9a, 0x73, 0x9a, 0x37, 0x74, 0x43,
-  0x77, 0xc5, 0x20, 0xe0, 0x59, 0x3a, 0x0d, 0x68, 0x15, 0x2d, 0xa7, 0xa4,
-  0x59, 0xec, 0x75, 0xaa, 0x73, 0x78, 0x17, 0xdd, 0x31, 0xd7, 0xc6, 0xe9,
-  0x09, 0xbd, 0xf4, 0x79, 0x62, 0x1f, 0x72, 0x4e, 0x6f, 0xbb, 0xa0, 0x53,
-  0xc5, 0x4c, 0x68, 0x3d, 0x6e, 0x7d, 0x57, 0x23, 0xc7, 0x32, 0xb4, 0x36,
-  0xec, 0x2f, 0x0a, 0xf1, 0xda, 0x73, 0x99, 0xbf, 0xcd, 0x19, 0x39, 0x8c,
-  0x4c, 0x20, 0xae, 0xf5, 0x40, 0x85, 0xda, 0x32, 0xb7, 0xf4, 0x68, 0x3e,
-  0x98, 0x94, 0x4c, 0x12, 0x1f, 0xa4, 0xf6, 0x04, 0x57, 0x47, 0x1b, 0xca,
-  0xfa, 0xa8, 0x8d, 0x98, 0x4c, 0x99, 0x92, 0xc9, 0x92, 0x10, 0x0b, 0x09,
-  0x0c, 0xab, 0xe9, 0x75, 0xd7, 0x94, 0x81, 0xac, 0x45, 0x87, 0xf0, 0xc4,
-  0xac, 0x75, 0xb4, 0xc5, 0x3b, 0x45, 0x8e, 0xdc, 0x94, 0xe5, 0xd2, 0x65,
-  0xc3, 0xe9, 0xee, 0x9c, 0x40, 0x25, 0xb5, 0x19, 0x12, 0x68, 0xd8, 0x9d,
-  0xa2, 0x45, 0x84, 0x22, 0x14, 0x8d, 0x47, 0x28, 0xe9, 0x7b, 0xaa, 0x6b,
-  0x5b, 0xc2, 0x34, 0x8c, 0xe0, 0x60, 0x0a, 0x8c, 0x77, 0x07, 0x21, 0x1a,
-  0x8f, 0xd6, 0x1f, 0xcf, 0xe7, 0xfb, 0x37, 0x66, 0xa5, 0x58, 0x68, 0x50,
-  0x74, 0x11, 0xb9, 0x4a, 0x73, 0x3a, 0xad, 0xdd, 0xa9, 0x0a, 0xd5, 0x4b,
-  0x55, 0xc2, 0x17, 0x83, 0x4e, 0xa0, 0x6a, 0xfc, 0xbc, 0xda, 0xe0, 0xfe,
-  0x97, 0xd5, 0x12, 0x68, 0x1c, 0x1b, 0xdf, 0x9d, 0x21, 0x18, 0x7d, 0xa5,
-  0x75, 0xc4, 0x6a, 0x2a, 0x0a, 0x78, 0x43, 0x0d, 0xd1, 0x92, 0x49, 0x00,
-  0xb1, 0x94, 0x6e, 0xa0, 0x4a, 0x6d, 0x84, 0x94, 0x9b, 0xfc, 0xd3, 0x53,
-  0xaf, 0x1c, 0xf4, 0xee, 0x32, 0xe5, 0x2c, 0x01, 0x41, 0xc5, 0x19, 0xbb,
-  0x80, 0xab, 0xdb, 0xb2, 0x5e, 0x73, 0x0b, 0x91, 0xb4, 0x88, 0xc6, 0xe1,
-  0x59, 0x75, 0x77, 0x9d, 0xb1, 0x74, 0x4e, 0xab, 0x64, 0x6a, 0xd1, 0x37,
-  0xad, 0x90, 0xc3, 0x8b, 0x28, 0x6c, 0x2a, 0x90, 0x9d, 0xbc, 0x42, 0x94,
-  0x20, 0xc0, 0xa4, 0xbc, 0x69, 0xd0, 0x9e, 0x95, 0xfd, 0x1a, 0x64, 0x29,
-  0xeb, 0xa4, 0xc6, 0x8b, 0xf1, 0x43, 0x10, 0x50, 0x11, 0x79, 0x07, 0xdf,
-  0xfb, 0xe7, 0x53, 0x7b, 0x58, 0x26, 0x73, 0x08, 0x63, 0xa7, 0x1e, 0x5e,
-  0xe5, 0x98, 0xd5, 0x97, 0xaf, 0xdb, 0xf6, 0x1c, 0xc0, 0x00, 0x05, 0x46,
-  0xdc, 0xf0, 0x4e, 0xfb, 0x7a, 0x85, 0x11, 0x09, 0x39, 0x77, 0x86, 0x07,
-  0x13, 0x8e, 0x9d, 0xad, 0xa9, 0x52, 0x35, 0x40, 0x0a, 0x00, 0x03, 0x34,
-  0x52, 0xc6, 0x4f, 0x0b, 0xa3, 0x72, 0xf2, 0xe1, 0x00, 0x4d, 0xdb, 0x98,
-  0xa0, 0x18, 0x19, 0x10, 0x46, 0x81, 0x11, 0x80, 0xc4, 0x60, 0x25, 0x53,
-  0x95, 0xa8, 0x68, 0xa6, 0x74, 0x8d, 0x21, 0x7a, 0x34, 0xd3, 0x41, 0x4d,
-  0x41, 0xe5, 0xa5, 0xa6, 0x19, 0x8d, 0xce, 0x50, 0x25, 0x3f, 0x4e, 0xbb,
-  0xc8, 0xdb, 0x2e, 0x0b, 0x4c, 0xae, 0x59, 0x35, 0x5a, 0x61, 0xa7, 0x67,
-  0x40, 0xfe, 0xed, 0xc4, 0x3d, 0xda, 0x3a, 0x0e, 0x1d, 0x25, 0xe2, 0x39,
-  0x34, 0x3b, 0x4b, 0x0d, 0xcc, 0x3e, 0x93, 0xa5, 0xfa, 0xfc, 0x3a, 0x51,
-  0x5c, 0x9b, 0x1e, 0xb5, 0xa9, 0x02, 0xd0, 0x42, 0xb2, 0xcc, 0x5e, 0x78,
-  0x41, 0x9e, 0x08, 0xc7, 0x0c, 0xcf, 0xe5, 0x43, 0xbd, 0xa9, 0xd5, 0x03,
-  0xa5, 0xb6, 0xd1, 0x94, 0x18, 0x30, 0x18, 0xcd, 0x9d, 0xfc, 0xf9, 0x3e,
-  0x16, 0xa6, 0x94, 0xd2, 0xda, 0x58, 0x20, 0x62, 0xe7, 0x3e, 0x8c, 0x60,
-  0x60, 0x28, 0x89, 0x13, 0x05, 0xeb, 0x25, 0xdb, 0x76, 0xc4, 0xba, 0x12,
-  0xf4, 0xf9, 0x3b, 0x5e, 0xba, 0x64, 0xb9, 0xc6, 0xcd, 0xb2, 0xd5, 0xe8,
-  0x97, 0x99, 0x12, 0x6f, 0x1f, 0xe8, 0x00, 0x04, 0xb7, 0x60, 0x0e, 0x21,
-  0x1a, 0x94, 0xcd, 0xc6, 0x8f, 0x03, 0x41, 0x99, 0x10, 0x62, 0x51, 0xce,
-  0xd6, 0xb8, 0x71, 0xc8, 0xb4, 0x5a, 0xd2, 0x2c, 0xbc, 0x16, 0xe0, 0x0d,
-  0xfb, 0xf0, 0xbb, 0xac, 0x88, 0x03, 0xde, 0xb5, 0xf7, 0xe1, 0xa0, 0x1b,
-  0xae, 0xf1, 0x5a, 0xed, 0xdd, 0xbe, 0x76, 0x9c, 0xe2, 0xe8, 0x64, 0xd9,
-  0x3c, 0xf5, 0xb0, 0xea, 0x7e, 0x7e, 0x4e, 0xb1, 0x7c, 0x3d, 0xd5, 0x3e,
-  0x02, 0x5e, 0x9d, 0xca, 0x5f, 0xa3, 0x5c, 0x83, 0x7d, 0xf3, 0x33, 0x03,
-  0x56, 0xf2, 0x9c, 0x0b, 0xdc, 0x71, 0x15, 0x7c, 0xdf, 0x06, 0xa3, 0xbe,
-  0x0d, 0x8e, 0x13, 0xa3, 0xb3, 0x2d, 0x35, 0xcd, 0x67, 0x6a, 0xec, 0x33,
-  0xfb, 0xef, 0x98, 0x50, 0x18, 0xfb, 0x04, 0xb1, 0x4c, 0x80, 0xc4, 0xdd,
-  0xa5, 0x7b, 0xab, 0x83, 0xb0, 0xc1, 0x40, 0x1b, 0xe9, 0x8f, 0xb0, 0x34,
-  0xea, 0x09, 0x06, 0x55, 0x4c, 0x90, 0xaf, 0xcc, 0x44, 0x88, 0x60, 0x44,
-  0xa0, 0xad, 0x98, 0x94, 0x9e, 0x99, 0x42, 0xd7, 0xe8, 0xbb, 0x8e, 0xba,
-  0x8a, 0xce, 0xe9, 0x81, 0x5a, 0xd8, 0xcd, 0xcd, 0x68, 0xad, 0x9b, 0x6c,
-  0x57, 0x5e, 0x86, 0xd1, 0x25, 0x3c, 0x36, 0xa0, 0x03, 0xd1, 0x14, 0x57,
-  0x97, 0x89, 0xd2, 0xa5, 0x72, 0xf8, 0x27, 0xd4, 0xbe, 0xe7, 0xe7, 0xeb,
-  0x57, 0x43, 0xf8, 0xbd, 0x1f, 0xb9, 0xab, 0x11, 0x30, 0x58, 0xe0, 0xa9,
-  0x6b, 0x56, 0xbd, 0x74, 0xfb, 0x19, 0xf5, 0x34, 0xf9, 0xcd, 0xbe, 0x9f,
-  0x9b, 0x03, 0x5a, 0x57, 0x9d, 0xa3, 0xa0, 0xb0, 0xb5, 0xc8, 0x78, 0xd8,
-  0x5d, 0xce, 0x43, 0x7e, 0xce, 0x38, 0x81, 0xb3, 0x73, 0xaa, 0xa8, 0xe1,
-  0x2c, 0x04, 0x85, 0x4d, 0x42, 0xf1, 0x82, 0xa1, 0x29, 0xed, 0x0a, 0x6f,
-  0x0d, 0x59, 0x8c, 0xd9, 0xd6, 0x00, 0x26, 0x69, 0x36, 0x48, 0x2a, 0x94,
-  0x06, 0x41, 0x01, 0x88, 0x80, 0xc2, 0x46, 0xe3, 0x6d, 0x63, 0x8a, 0x28,
-  0x07, 0x0e, 0xba, 0x74, 0x05, 0xbd, 0x3b, 0x3b, 0xce, 0x83, 0x6e, 0x9b,
-  0xfc, 0x55, 0x62, 0xb5, 0xe6, 0xfa, 0x8a, 0xb6, 0x1c, 0x74, 0xb4, 0x72,
-  0xc7, 0xd3, 0x56, 0xe2, 0x9d, 0x11, 0x76, 0x5d, 0xea, 0xc7, 0x2d, 0xbe,
-  0x2a, 0x06, 0xa6, 0x71, 0x54, 0xe0, 0x9f, 0x8d, 0xc4, 0x39, 0xb6, 0x94,
-  0x1d, 0xf0, 0x20, 0x70, 0xec, 0x63, 0x9d, 0x75, 0x40, 0x5c, 0x31, 0x4e,
-  0x64, 0xb2, 0xd5, 0xa2, 0x33, 0x83, 0x7a, 0x4e, 0xa1, 0x7d, 0x33, 0x15,
-  0xcb, 0xfd, 0xe3, 0xe1, 0xd5, 0xb0, 0x5e, 0xd4, 0xa3, 0xa1, 0x1e, 0x35,
-  0x01, 0xbe, 0x6a, 0x11, 0x21, 0xc3, 0x54, 0x8b, 0x0f, 0xdf, 0xba, 0xc5,
-  0x2d, 0x14, 0x13, 0x00, 0xe0, 0x21, 0x1a, 0x94, 0xdd, 0xca, 0x12, 0x83,
-  0x11, 0x11, 0x04, 0xc4, 0x6c, 0xdf, 0x18, 0xe9, 0x50, 0x15, 0x72, 0xa6,
-  0x9a, 0x3a, 0xe4, 0xa3, 0x50, 0x06, 0xaa, 0x98, 0x71, 0x7f, 0xae, 0x73,
-  0x27, 0x79, 0xf7, 0x56, 0xbd, 0xb4, 0x4c, 0x76, 0x0d, 0x6e, 0x0f, 0x94,
-  0x9e, 0x18, 0x37, 0x3b, 0xbb, 0x55, 0xb9, 0xfd, 0x36, 0xf3, 0x5d, 0x72,
-  0xe4, 0x08, 0x5b, 0x1a, 0x7b, 0x85, 0x90, 0x04, 0x08, 0xe3, 0x97, 0x5d,
-  0x1e, 0x1d, 0xa3, 0x63, 0x59, 0xe9, 0x62, 0x0e, 0xfc, 0xc2, 0x7c, 0x63,
-  0x96, 0x5f, 0x92, 0x5f, 0x0d, 0xec, 0xee, 0x36, 0xd9, 0xb8, 0xbe, 0xa8,
-  0x8b, 0xac, 0x35, 0x74, 0xa7, 0xfb, 0xff, 0xbe, 0x9f, 0x73, 0xbb, 0xe9,
-  0x6e, 0x6a, 0xf4, 0xcd, 0xb1, 0xe4, 0x8d, 0xd1, 0x22, 0x2b, 0xf0, 0x0e,
-  0xc3, 0x93, 0x37, 0x10, 0xb2, 0x7e, 0x35, 0x26, 0xdc, 0xd9, 0x71, 0xe6,
-  0x8c, 0xb0, 0xa6, 0x14, 0xc1, 0x1a, 0x2c, 0xb4, 0x36, 0xf4, 0xf5, 0xd4,
-  0x32, 0xf0, 0x61, 0x63, 0x4f, 0xa0, 0x7a, 0x72, 0x08, 0xc8, 0x1d, 0x7a,
-  0x65, 0xc7, 0xda, 0xcb, 0x4a, 0x3c, 0x82, 0x64, 0x18, 0x65, 0xb9, 0xc5,
-  0xad, 0x5f, 0xbe, 0xd8, 0xb4, 0x01, 0x59, 0xbb, 0x6e, 0xeb, 0xeb, 0x1f,
-  0x4c, 0x9c, 0xba, 0x11, 0x62, 0xff, 0x3a, 0x38, 0x1e, 0x5c, 0x9d, 0xd5,
-  0x8e, 0x71, 0x9b, 0x9e, 0x60, 0xf4, 0xb1, 0x5c, 0x9e, 0xee, 0x76, 0x57,
-  0x96, 0x28, 0xb8, 0x82, 0x62, 0x54, 0x00, 0xe6, 0x90, 0xff, 0x5e, 0x67,
-  0xd4, 0x8c, 0x75, 0x33, 0xab, 0xaa, 0x70, 0xc8, 0x0b, 0xdd, 0x09, 0x4f,
-  0x0d, 0x92, 0x0c, 0xe0, 0x20, 0xd6, 0x59, 0x70, 0x94, 0xec, 0x13, 0xd6,
-  0xf7, 0x23, 0x0c, 0x82, 0x02, 0x11, 0x00, 0xc4, 0x20, 0x71, 0x22, 0x75,
-  0x8c, 0x65, 0xc2, 0x50, 0x09, 0x77, 0x3e, 0x3a, 0xab, 0x05, 0xd1, 0xfa,
-  0x9b, 0x93, 0x48, 0x75, 0xf7, 0xf6, 0x98, 0xe7, 0x3b, 0x28, 0xa9, 0xdc,
-  0x2f, 0xf2, 0xd4, 0xb9, 0x61, 0xa1, 0xc3, 0x47, 0x5b, 0xc3, 0xe5, 0x46,
-  0xd7, 0x49, 0x64, 0xf9, 0xb6, 0x4b, 0x28, 0x66, 0x0e, 0xb4, 0x6e, 0xe6,
-  0xdf, 0xf8, 0x0c, 0x23, 0xb3, 0x15, 0x44, 0x59, 0xe9, 0xec, 0xbf, 0x7d,
-  0x2c, 0x7b, 0xc9, 0x9f, 0x26, 0x77, 0x1d, 0x33, 0x51, 0x54, 0x30, 0xe2,
-  0x4d, 0x42, 0x20, 0xb8, 0x52, 0x6c, 0x4b, 0x8f, 0xe8, 0xae, 0xa0, 0x12,
-  0x30, 0xa5, 0x12, 0x52, 0x01, 0x9c, 0x89, 0x23, 0x6b, 0x23, 0x11, 0x61,
-  0xc4, 0xfc, 0x73, 0xa8, 0x94, 0x00, 0x38, 0x21, 0x1a, 0x8d, 0x24, 0x0f,
-  0xff, 0x85, 0xff, 0x37, 0x6b, 0xa2, 0x58, 0x68, 0xb0, 0x46, 0x18, 0xb8,
-  0x6f, 0x2d, 0xc5, 0x24, 0xdc, 0x11, 0xc0, 0xa5, 0xb4, 0x75, 0x59, 0xb1,
-  0xd5, 0x81, 0x64, 0xd3, 0xff, 0x5d, 0xdd, 0x39, 0xa2, 0x86, 0x05, 0x23,
-  0x0c, 0x74, 0xc8, 0x4c, 0x78, 0x10, 0x21, 0x90, 0xc7, 0x46, 0x23, 0xa5,
-  0x5a, 0x18, 0x1d, 0x09, 0xd5, 0xb6, 0xa6, 0xd1, 0x36, 0x34, 0x28, 0x71,
-  0xeb, 0x1d, 0x5e, 0xd4, 0x32, 0xd6, 0xda, 0xd9, 0x3c, 0xf6, 0xc7, 0x87,
-  0xe4, 0x96, 0x4e, 0xca, 0x28, 0x0f, 0x1c, 0x73, 0x51, 0xeb, 0xf4, 0xeb,
-  0x36, 0x5b, 0x5c, 0xdf, 0x41, 0xac, 0x97, 0xcb, 0x3a, 0x6d, 0x6b, 0x63,
-  0x22, 0x15, 0x3f, 0xa3, 0x33, 0xd0, 0xdf, 0x2a, 0x95, 0x1d, 0x65, 0xb5,
-  0xef, 0x0e, 0x26, 0xad, 0xb6, 0xe8, 0x4d, 0xc7, 0xb8, 0x99, 0xa7, 0x10,
-  0x7c, 0x66, 0x18, 0xca, 0x43, 0x88, 0xf0, 0x64, 0x15, 0x85, 0x87, 0xf4,
-  0xeb, 0x88, 0x2a, 0xe2, 0xa6, 0x65, 0xc1, 0xd9, 0x93, 0xfb, 0x1a, 0x8f,
-  0x68, 0xc6, 0xe6, 0xae, 0xb1, 0x69, 0xd6, 0xd9, 0x8e, 0xd0, 0x28, 0x91,
-  0x22, 0x41, 0xa4, 0xe1, 0x57, 0x34, 0x55, 0x9e, 0x2f, 0x5c, 0xb0, 0xf9,
-  0x63, 0x8f, 0x6c, 0x61, 0x61, 0x18, 0x43, 0xd4, 0x4a, 0xa5, 0xfc, 0x22,
-  0x34, 0xfc, 0x73, 0x6c, 0xe7, 0xbb, 0xc9, 0x98, 0x39, 0x08, 0x27, 0x92,
-  0xa8, 0xcb, 0xb6, 0x0c, 0x7f, 0x38, 0xe0, 0x4a, 0x3a, 0x09, 0x89, 0x5a,
-  0xa0, 0x9a, 0xb5, 0x26, 0xaa, 0x12, 0xa1, 0x2a, 0x58, 0x21, 0xb6, 0xbe,
-  0x3b, 0x02, 0x6e, 0xdf, 0x03, 0x62, 0xa2, 0x44, 0x28, 0x11, 0x42, 0x63,
-  0x00, 0x20, 0x88, 0xe1, 0x4b, 0x5a, 0x4d, 0x0b, 0x2e, 0x5e, 0x05, 0x14,
-  0xda, 0x45, 0xfe, 0xba, 0x62, 0xc3, 0xf0, 0x72, 0xd0, 0x01, 0xf8, 0x08,
-  0xcb, 0x6e, 0xda, 0x82, 0x82, 0x66, 0xca, 0x90, 0x0f, 0xc6, 0xc7, 0x4d,
-  0xd4, 0x05, 0xd9, 0xd2, 0x14, 0xa0, 0x4b, 0x4e, 0x36, 0x27, 0x1d, 0xfe,
-  0x52, 0xd3, 0x5d, 0xa6, 0x4f, 0x18, 0xfb, 0xc3, 0x82, 0x7b, 0x8c, 0xbc,
-  0xee, 0x91, 0xd0, 0x9d, 0xd3, 0xbe, 0xbd, 0xe6, 0xb8, 0x8f, 0xd4, 0xa3,
-  0x8e, 0x7c, 0x43, 0x0f, 0x54, 0x01, 0x9f, 0xed, 0x42, 0x4f, 0x76, 0x70,
-  0xb3, 0xda, 0x72, 0x85, 0x0b, 0x21, 0x02, 0xf2, 0xfa, 0x74, 0x2f, 0x8e,
-  0x8d, 0x36, 0xf5, 0x28, 0x91, 0xa8, 0x24, 0x1e, 0xe7, 0xc4, 0xb6, 0x64,
-  0x41, 0x24, 0x7d, 0x7c, 0x73, 0x2c, 0x8d, 0x2a, 0x38, 0x1f, 0x1f, 0x59,
-  0xf0, 0x75, 0x58, 0x8d, 0x6e, 0x1c, 0x8f, 0x4e, 0x47, 0xaa, 0x78, 0xa4,
-  0x67, 0x03, 0xae, 0xc1, 0x8d, 0x4a, 0x51, 0xa4, 0xaf, 0x45, 0x24, 0xb5,
-  0x2b, 0x81, 0xf5, 0x43, 0xc0, 0x42, 0x34, 0xb2, 0x1a, 0x45, 0x74, 0x89,
-  0xa7, 0xdb, 0x80, 0x0e, 0x46, 0xe7, 0x60, 0xa5, 0x44, 0x03, 0x4c, 0xf6,
-  0x3e, 0xef, 0xcc, 0x00, 0x90, 0x5d, 0x44, 0xc5, 0x44, 0xf6, 0x83, 0xe7,
-  0x0b, 0xdf, 0x13, 0x3d, 0x80, 0x70, 0x21, 0x1a, 0x8f, 0xf0, 0x07, 0xcf,
-  0x1e, 0xff, 0x3f, 0x66, 0xa6, 0xb1, 0x86, 0x26, 0x56, 0x93, 0xaa, 0x55,
-  0x28, 0x04, 0x82, 0xba, 0xd5, 0xde, 0xd0, 0x97, 0xc0, 0x5b, 0x2a, 0x49,
-  0x24, 0xff, 0xc1, 0x3b, 0xb6, 0x0c, 0x5b, 0x1c, 0xc2, 0xa1, 0xb9, 0xa7,
-  0x34, 0xdb, 0xc7, 0x6e, 0x49, 0x79, 0xd4, 0x6c, 0x6c, 0x0a, 0xb7, 0xeb,
-  0x55, 0x62, 0x7a, 0xd1, 0x57, 0x42, 0x1a, 0x46, 0xaa, 0xf7, 0x81, 0x55,
-  0xcd, 0x14, 0xa9, 0x14, 0xcb, 0xfa, 0x65, 0x16, 0x65, 0xe6, 0x64, 0xce,
-  0x91, 0x77, 0x45, 0xec, 0x9b, 0xec, 0x17, 0x4b, 0xb8, 0x31, 0x49, 0x5a,
-  0x53, 0x8e, 0xab, 0xc9, 0x49, 0xd7, 0x1c, 0x93, 0xf8, 0x1f, 0x25, 0x12,
-  0x35, 0x7b, 0x34, 0x89, 0x5f, 0x9f, 0x27, 0x61, 0x0a, 0xdc, 0x2b, 0x00,
-  0x77, 0x02, 0x65, 0x62, 0xa5, 0xe8, 0xa3, 0xb7, 0x0d, 0xfc, 0xee, 0xa6,
-  0x79, 0x82, 0xc6, 0x3f, 0xa7, 0x3c, 0x56, 0xf0, 0xd6, 0x80, 0xc8, 0xdd,
-  0x71, 0x33, 0x23, 0x6b, 0x82, 0xd1, 0x05, 0xc9, 0x84, 0x80, 0x13, 0x85,
-  0xd4, 0xa8, 0x85, 0x85, 0x20, 0x6a, 0x25, 0x75, 0x3b, 0x1a, 0xdd, 0xe5,
-  0x4c, 0x90, 0x2a, 0x0b, 0x85, 0xad, 0xb0, 0x50, 0x2d, 0x29, 0xbc, 0x2b,
-  0x45, 0xaf, 0x7d, 0x3c, 0x00, 0x9c, 0xb8, 0x99, 0x18, 0x43, 0x31, 0x55,
-  0x95, 0x4b, 0x00, 0x44, 0xb1, 0x66, 0xbd, 0x92, 0xaa, 0xe4, 0x97, 0x50,
-  0x3d, 0x55, 0xae, 0x68, 0x6e, 0xda, 0x6b, 0xdd, 0xb5, 0xe7, 0xee, 0x7b,
-  0x03, 0x5d, 0x0a, 0x9a, 0x25, 0x92, 0xe7, 0x2e, 0xc6, 0xe4, 0xb8, 0x48,
-  0x2b, 0x8b, 0xc9, 0xe5, 0xaa, 0x12, 0x33, 0x2d, 0x21, 0x63, 0x42, 0x54,
-  0x77, 0x41, 0x7b, 0xd7, 0xa3, 0x28, 0x60, 0xe1, 0x9c, 0xa8, 0xbd, 0xc1,
-  0xdd, 0x29, 0xdc, 0x98, 0x54, 0x8e, 0x85, 0x6c, 0x47, 0x83, 0x37, 0x75,
-  0xeb, 0xef, 0xc3, 0xa1, 0xe3, 0xf0, 0xd0, 0x78, 0xee, 0x34, 0xb3, 0x63,
-  0x55, 0xb2, 0xe2, 0x00, 0x52, 0x70, 0x75, 0xd5, 0xff, 0xa1, 0x00, 0x23,
-  0x00, 0xd9, 0x7c, 0xbe, 0xca, 0xda, 0x93, 0x32, 0xa8, 0xd6, 0x2e, 0x88,
-  0x9a, 0xeb, 0x27, 0x40, 0xa0, 0x7b, 0x64, 0x2d, 0x12, 0x33, 0xb3, 0xd5,
-  0xed, 0xea, 0xc5, 0xe8, 0x7b, 0x1a, 0x4c, 0x02, 0x35, 0xa4, 0x80, 0xb5,
-  0xd0, 0x98, 0x10, 0x2a, 0xeb, 0x42, 0x8b, 0xb7, 0x2a, 0x52, 0x4d, 0x30,
-  0x0e, 0x21, 0x1a, 0x8f, 0xc0, 0x00, 0x06, 0x00, 0xff, 0x35, 0x6b, 0xa5,
-  0xb1, 0x06, 0x00, 0x46, 0x94, 0xda, 0x17, 0x97, 0xc5, 0xa3, 0x2e, 0xda,
-  0x04, 0x40, 0x1f, 0x74, 0xf4, 0x4e, 0x2e, 0xf8, 0x3a, 0x57, 0x52, 0x77,
-  0xf5, 0xa4, 0x6a, 0x9c, 0x52, 0xc9, 0xf8, 0x27, 0xe9, 0xa7, 0x96, 0x07,
-  0xfc, 0x42, 0xe7, 0x42, 0x73, 0x3f, 0x45, 0x69, 0x5f, 0xde, 0xea, 0xcd,
-  0xeb, 0xea, 0xa9, 0x7e, 0x76, 0xc1, 0x2f, 0x59, 0x5c, 0xfd, 0x9a, 0xd5,
-  0xb0, 0x5c, 0x27, 0xc1, 0x5c, 0x5b, 0x09, 0x3c, 0xb7, 0x6d, 0xda, 0x73,
-  0xfa, 0xc6, 0xc5, 0x65, 0xc4, 0x60, 0x78, 0xe6, 0x2a, 0x3e, 0x5d, 0xba,
-  0xb4, 0x54, 0x2b, 0x18, 0x18, 0xd7, 0x95, 0x22, 0xa0, 0x9a, 0x38, 0xd4,
-  0x32, 0xa4, 0x09, 0x36, 0xf3, 0x22, 0xe1, 0x03, 0xc2, 0x40, 0x84, 0x13,
-  0x47, 0x37, 0x65, 0x36, 0xaf, 0x75, 0x5d, 0xd8, 0x7b, 0x66, 0xfb, 0xcd,
-  0x8e, 0x3a, 0x3a, 0xe1, 0x8a, 0xb9, 0x30, 0x19, 0x04, 0x00, 0xd2, 0x98,
-  0x69, 0xa5, 0xcb, 0xc2, 0xf2, 0x6f, 0x8f, 0x20, 0xbb, 0x13, 0xed, 0xe5,
-  0x0a, 0x73, 0x90, 0x4c, 0x17, 0x52, 0x2d, 0x38, 0x83, 0x7e, 0xf8, 0xd3,
-  0x14, 0x71, 0x17, 0x84, 0x6d, 0x6b, 0xde, 0x02, 0x2f, 0x34, 0x37, 0x66,
-  0x93, 0x91, 0x0a, 0x27, 0x1e, 0x38, 0x62, 0x84, 0x6c, 0xa0, 0x52, 0xd7,
-  0xa4, 0xe2, 0x94, 0xce, 0x83, 0x69, 0xb6, 0x18, 0x02, 0x76, 0xe7, 0x43,
-  0x61, 0x10, 0x98, 0x48, 0x12, 0x30, 0x9d, 0x60, 0x00, 0x22, 0xe6, 0x4f,
-  0x2b, 0x43, 0x4a, 0x3a, 0xc2, 0xe0, 0x37, 0x10, 0x3f, 0xe4, 0x36, 0xb8,
-  0x44, 0x53, 0xb1, 0xf9, 0x53, 0x51, 0xd6, 0xa8, 0x25, 0x06, 0x70, 0xbd,
-  0x50, 0xec, 0xde, 0xfb, 0xaf, 0x80, 0xca, 0xc0, 0xf3, 0x9c, 0xd1, 0xc9,
-  0x33, 0x2d, 0x02, 0x52, 0x83, 0xc5, 0x7b, 0x5a, 0x67, 0x43, 0xf8, 0xff,
-  0x52, 0x36, 0xac, 0xd2, 0x64, 0xe1, 0x92, 0x38, 0x7a, 0xa2, 0xec, 0x5d,
-  0x8a, 0x3c, 0x08, 0x2e, 0x8e, 0x36, 0xec, 0x8e, 0x62, 0x51, 0x82, 0xe6,
-  0xce, 0x53, 0x88, 0xdb, 0x72, 0xd0, 0x3a, 0x7b, 0x8b, 0xde, 0xbf, 0x1f,
-  0x0a, 0x89, 0x1c, 0xc5, 0xab, 0xc8, 0xa5, 0x59, 0xbd, 0x1b, 0xb5, 0xfc,
-  0xa9, 0x99, 0x23, 0xf1, 0x17, 0x2d, 0xbe, 0xc4, 0xf5, 0x9c, 0x4d, 0xe3,
-  0x7b, 0x2c, 0x9b, 0x65, 0x8f, 0x5f, 0xe3, 0x13, 0x90, 0x08, 0xad, 0x38,
-  0xad, 0xd4, 0xde, 0x5b, 0xbd, 0xf9, 0x32, 0x7c, 0x13, 0xeb, 0xe6, 0xe7,
-  0xb6, 0x1a, 0x21, 0x69, 0xc6, 0x2f, 0x24, 0x6b, 0x08, 0x29, 0xd1, 0x4c,
-  0x34, 0xd1, 0x31, 0x88, 0x84, 0x3d, 0xa9, 0xf0, 0xe9, 0x59, 0xa1, 0x19,
-  0x97, 0x4e, 0x79, 0xcd, 0xeb, 0xcb, 0x9e, 0x27, 0x4b, 0x23, 0x32, 0x23,
-  0xa6, 0x6d, 0x06, 0x08, 0xb0, 0xe8, 0x53, 0x82, 0x70, 0xaa, 0x32, 0x39,
-  0x80, 0xef, 0x16, 0x82, 0xdc, 0xee, 0x7f, 0x70, 0x70, 0x4a, 0xd1, 0xcb,
-  0x4e, 0x94, 0x2a, 0x4b, 0x2a, 0x57, 0x05, 0x45, 0x46, 0x1c, 0x67, 0x02,
-  0x2f, 0xd3, 0x18, 0xf2, 0x92, 0x03, 0x07, 0x21, 0x1a, 0x8d, 0x00, 0x00,
-  0x0e, 0x01, 0xff, 0x37, 0x64, 0xa7, 0xb2, 0x48, 0x42, 0x44, 0x10, 0x98,
-  0x24, 0x83, 0x25, 0x58, 0xbb, 0xba, 0xb4, 0x34, 0xb8, 0x00, 0x58, 0x6c,
-  0xee, 0xdf, 0xfa, 0x8d, 0x10, 0x2f, 0x1b, 0xc4, 0x49, 0x80, 0xba, 0xef,
-  0xce, 0xdb, 0xf8, 0x7d, 0x3b, 0x1b, 0x6a, 0xca, 0xfd, 0x92, 0x6b, 0x5f,
-  0x25, 0xab, 0x62, 0xf0, 0x26, 0x14, 0x3c, 0x47, 0x95, 0x48, 0x36, 0x8a,
-  0x82, 0x75, 0x4c, 0x6f, 0x81, 0xe6, 0xbd, 0x23, 0x17, 0x33, 0x0f, 0x4c,
-  0xfb, 0x25, 0x5c, 0x26, 0x1e, 0x2b, 0xa7, 0x87, 0xfc, 0x94, 0xcd, 0x2e,
-  0x43, 0x63, 0x40, 0xd0, 0xf4, 0xaf, 0xfe, 0x0f, 0xe6, 0x4d, 0x8d, 0x66,
-  0x9e, 0xfc, 0x73, 0xae, 0xea, 0x3c, 0x64, 0xa2, 0xca, 0xa7, 0x51, 0xa6,
-  0xae, 0xab, 0x1f, 0x66, 0x61, 0x60, 0xdd, 0x9c, 0xbb, 0xdb, 0xb3, 0x0f,
-  0x8f, 0x67, 0x7d, 0x3c, 0x38, 0xa2, 0x67, 0x12, 0xef, 0xbe, 0x49, 0xf3,
-  0x80, 0x8c, 0xe1, 0x9d, 0x9d, 0x25, 0x40, 0x37, 0x9d, 0x61, 0x4a, 0x99,
-  0x27, 0xce, 0x0c, 0x80, 0x60, 0xf8, 0xfe, 0xdb, 0x1c, 0xaf, 0xa3, 0xfb,
-  0x34, 0xe8, 0x04, 0x0b, 0x29, 0xf5, 0x8a, 0x74, 0x43, 0x84, 0x62, 0x52,
-  0x39, 0xf8, 0x05, 0xa2, 0xd6, 0x61, 0xd7, 0x14, 0x87, 0xe8, 0xe5, 0x53,
-  0x46, 0x5e, 0x1d, 0x29, 0x1b, 0x5e, 0xa5, 0x44, 0x96, 0xed, 0xa7, 0x38,
-  0x21, 0x41, 0xb5, 0x41, 0x6b, 0xd8, 0x2a, 0x4d, 0x40, 0xbf, 0x9f, 0x89,
-  0x2d, 0x65, 0x6e, 0x13, 0xf6, 0xfa, 0x3b, 0x85, 0x0a, 0x2e, 0x71, 0x4c,
-  0x00, 0x64, 0xb2, 0xe5, 0x3f, 0x0b, 0xf0, 0x8e, 0x09, 0xc1, 0x60, 0x11,
-  0x29, 0xd5, 0x3a, 0x65, 0x4c, 0xe6, 0x2e, 0xa1, 0x26, 0x93, 0x0b, 0xb7,
-  0xc1, 0x3f, 0x86, 0x6f, 0xa8, 0xcc, 0x49, 0x80, 0xd1, 0x50, 0xaa, 0xec,
-  0x5e, 0x2d, 0x3d, 0x78, 0xc6, 0x4f, 0x3e, 0xcc, 0xc8, 0xa4, 0x00, 0xcf,
-  0xf9, 0x78, 0x77, 0xc0, 0x46, 0x8e, 0x47, 0xf6, 0x9e, 0xf9, 0x8d, 0x46,
-  0xb7, 0x87, 0xcb, 0x16, 0xcf, 0x35, 0xea, 0xd5, 0x7b, 0x07, 0x64, 0xae,
-  0x70, 0x18, 0xdd, 0x58, 0xbc, 0xf2, 0xf7, 0xa8, 0x49, 0x07, 0x26, 0x9c,
-  0xcb, 0x44, 0x5f, 0x15, 0x73, 0xf1, 0xb6, 0xcb, 0x05, 0xe9, 0xe6, 0xbe,
-  0x60, 0xdd, 0xce, 0xd9, 0x07, 0x65, 0xd1, 0xf4, 0xc9, 0x69, 0x2a, 0x42,
-  0x9b, 0xf0, 0xf4, 0x89, 0x14, 0x43, 0x56, 0xf6, 0xd9, 0x23, 0x46, 0xb2,
-  0xf3, 0x80, 0xa7, 0x2d, 0x81, 0x79, 0x92, 0x4c, 0xf7, 0x16, 0x32, 0xf1,
-  0xb6, 0x2d, 0x7b, 0x13, 0x9f, 0xda, 0xab, 0x5e, 0x41, 0x36, 0xb5, 0x57,
-  0x8c, 0x13, 0xff, 0xda, 0xe7, 0x7a, 0x4a, 0xd5, 0xac, 0xd0, 0x40, 0xab,
-  0x88, 0xb4, 0x11, 0x0e, 0x00, 0x01, 0x52, 0x45, 0x1f, 0xad, 0x82, 0x65,
-  0x29, 0x9b, 0xc9, 0xea, 0xe4, 0x9b, 0x9d, 0x4e, 0x74, 0x11, 0x44, 0x0e,
-  0x21, 0x1a, 0x8f, 0x80, 0x00, 0x00, 0x00, 0xff, 0x31, 0x68, 0xa2, 0xc1,
-  0xd0, 0xa3, 0x10, 0x09, 0x56, 0x6f, 0x4b, 0x25, 0x6a, 0x0d, 0x58, 0xcd,
-  0x5d, 0x53, 0x7c, 0x32, 0xc5, 0x00, 0x09, 0x34, 0x7b, 0xe6, 0xa3, 0x1f,
-  0xc4, 0x4a, 0xc1, 0xba, 0x16, 0xf1, 0xff, 0x2c, 0x75, 0x30, 0xf0, 0x5e,
-  0x68, 0xcf, 0x7a, 0xa2, 0x23, 0x69, 0x71, 0x59, 0x5b, 0xf7, 0xf4, 0x37,
-  0x06, 0x75, 0xc8, 0xa4, 0x2c, 0xcf, 0xcd, 0xe4, 0x59, 0xdc, 0xcd, 0x59,
-  0xae, 0x34, 0x7c, 0x2c, 0x36, 0xbb, 0x94, 0xc1, 0xdb, 0x70, 0xe6, 0xf3,
-  0x04, 0x9b, 0x09, 0x5e, 0x94, 0xc9, 0x2d, 0xd9, 0x23, 0x78, 0xf7, 0x9a,
-  0x49, 0xcd, 0xa2, 0x8d, 0xf5, 0xab, 0x5c, 0x27, 0x5f, 0x8c, 0xaf, 0xd3,
-  0x4f, 0x9a, 0x00, 0xca, 0xf5, 0x55, 0x79, 0x77, 0x89, 0x80, 0xa9, 0x45,
-  0xe9, 0x02, 0x05, 0xc1, 0x02, 0xb7, 0x51, 0x42, 0x10, 0x01, 0x2b, 0x7e,
-  0xa6, 0xa1, 0x42, 0xa9, 0x96, 0x8c, 0xa7, 0x32, 0x51, 0x5b, 0x40, 0x39,
-  0x00, 0x05, 0x95, 0xf3, 0x56, 0x7d, 0x6a, 0xc7, 0x81, 0xce, 0x5d, 0x80,
-  0x09, 0xd5, 0x20, 0x4d, 0xdd, 0xd0, 0x4c, 0x14, 0x20, 0xb1, 0x2b, 0x29,
-  0x97, 0x14, 0x9c, 0xf1, 0x6b, 0x0d, 0x38, 0xb4, 0x0b, 0x3a, 0x08, 0x0e,
-  0xc6, 0xfc, 0x16, 0x63, 0xf7, 0xa9, 0x9c, 0x8a, 0x7f, 0x31, 0xd1, 0xff,
-  0x65, 0xa9, 0x89, 0xac, 0x2d, 0xd5, 0x4c, 0x31, 0x1e, 0x91, 0xc7, 0xcb,
-  0xaa, 0xa4, 0x9a, 0xcc, 0x98, 0xef, 0x54, 0x93, 0x10, 0x70, 0x13, 0x70,
-  0x0d, 0x91, 0x77, 0x9b, 0xb8, 0xf1, 0xd7, 0x26, 0x7f, 0xf1, 0x97, 0xae,
-  0xdb, 0x57, 0xdf, 0x8d, 0xca, 0xa0, 0xe6, 0x6d, 0xd1, 0xb2, 0xb1, 0x5c,
-  0x5f, 0x2c, 0x3b, 0xb5, 0xbe, 0xb1, 0xd5, 0x94, 0x18, 0x31, 0x3d, 0xad,
-  0x4f, 0x36, 0x3b, 0x23, 0x17, 0xd0, 0xac, 0xe6, 0x28, 0xee, 0xe3, 0xc3,
-  0xa2, 0xb1, 0x4d, 0xb9, 0x37, 0xe4, 0x57, 0x34, 0x31, 0x56, 0x29, 0x89,
-  0xe7, 0xd8, 0x8b, 0x65, 0xb5, 0x70, 0xdb, 0x38, 0x46, 0x65, 0x59, 0xc2,
-  0x90, 0x8a, 0xbc, 0xc2, 0x8c, 0x35, 0x3e, 0x0e, 0x5a, 0x61, 0x1a, 0xdb,
-  0x30, 0xa2, 0x50, 0x77, 0x3b, 0x75, 0x2a, 0x1b, 0xaa, 0xf2, 0x31, 0xba,
-  0x83, 0x93, 0x8a, 0xba, 0x82, 0x4e, 0x6c, 0x57, 0x86, 0x96, 0xd9, 0x59,
-  0xb2, 0xc7, 0x45, 0xd1, 0xeb, 0x51, 0x39, 0xe6, 0xbd, 0xdc, 0x70, 0xd3,
-  0xc2, 0x17, 0xa5, 0x29, 0xcf, 0x19, 0xf1, 0xf1, 0x62, 0x8b, 0x25, 0x7f,
-  0xbc, 0x0b, 0x29, 0x78, 0xa1, 0xf6, 0xc5, 0xb2, 0xf7, 0x5f, 0xcb, 0x84,
-  0xb8, 0xdc, 0x1b, 0x73, 0x58, 0x1a, 0x33, 0x4c, 0xaa, 0x55, 0x2c, 0xae,
-  0x4a, 0x32, 0x25, 0x19, 0x83, 0x81, 0xa7, 0x2a, 0x40, 0xe0, 0x21, 0x1a,
-  0x8f, 0x80, 0x00, 0x00, 0x0f, 0xff, 0x3f, 0x6a, 0x84, 0xa0, 0x86, 0xa9,
-  0xb7, 0xc1, 0x15, 0x2a, 0x95, 0xa1, 0x5a, 0x38, 0x69, 0xab, 0xa3, 0x03,
-  0x80, 0x19, 0x6b, 0x72, 0x7e, 0x65, 0x7c, 0xb6, 0xe3, 0xe8, 0x9c, 0x66,
-  0xbc, 0xe1, 0x41, 0x73, 0xea, 0x42, 0xdc, 0x4f, 0xdc, 0x35, 0xfd, 0x73,
-  0xcf, 0x48, 0xeb, 0xfb, 0xb7, 0xe0, 0x22, 0x8b, 0x7f, 0x9b, 0x40, 0x89,
-  0x38, 0x09, 0x4f, 0x86, 0x1b, 0x02, 0x9f, 0x17, 0x88, 0x9b, 0x0d, 0xa5,
-  0xe0, 0x39, 0x2b, 0xc6, 0x22, 0x45, 0x62, 0x37, 0xe4, 0x43, 0x2d, 0xb8,
-  0x8d, 0xc0, 0x6f, 0x5f, 0x6e, 0xec, 0x7c, 0xbb, 0x94, 0x20, 0x8b, 0xc9,
-  0x9f, 0x94, 0x2c, 0x15, 0xaa, 0xb1, 0x02, 0x62, 0x27, 0x12, 0x79, 0xe4,
-  0x04, 0x00, 0x9d, 0x66, 0xcb, 0x7a, 0xfc, 0x5e, 0x99, 0xc4, 0x4c, 0x59,
-  0x18, 0xa8, 0x81, 0xd8, 0x5b, 0xaa, 0x80, 0x00, 0x55, 0x64, 0xf6, 0x01,
-  0x97, 0x21, 0x0f, 0x6a, 0xf9, 0x7e, 0x74, 0x50, 0xc8, 0xb2, 0xe0, 0x14,
-  0x16, 0xfa, 0x39, 0x14, 0x5e, 0x90, 0x1d, 0xad, 0x1c, 0x01, 0xad, 0xfb,
-  0x21, 0x71, 0xd1, 0x68, 0x8a, 0x07, 0xa6, 0x42, 0x3c, 0x24, 0x6d, 0x55,
-  0x2f, 0x7f, 0x72, 0xf1, 0xaa, 0xc8, 0x3e, 0xbf, 0x42, 0x80, 0x95, 0x73,
-  0x37, 0x3b, 0xa1, 0xc7, 0x06, 0xd7, 0x7c, 0xad, 0xeb, 0xf9, 0xdc, 0x7d,
-  0xf1, 0x41, 0x03, 0x47, 0xf7, 0xf7, 0x4f, 0x71, 0xf7, 0xe7, 0x72, 0x9e,
-  0x93, 0xe5, 0x8c, 0x8d, 0xb7, 0x37, 0x3d, 0xe3, 0xcf, 0x3c, 0xab, 0x33,
-  0x07, 0xa8, 0xf5, 0xee, 0x38, 0xce, 0x3b, 0xf9, 0x3c, 0xda, 0x73, 0xe1,
-  0xb1, 0x2a, 0x6a, 0x59, 0x7b, 0x0c, 0xa2, 0x40, 0x1a, 0xb5, 0xc5, 0xbd,
-  0xb2, 0x7f, 0x5d, 0x36, 0xe0, 0x9d, 0x8c, 0xae, 0x0a, 0x30, 0xdc, 0xb6,
-  0x17, 0x4e, 0xf1, 0x6f, 0xa2, 0x50, 0xaa, 0x23, 0xa8, 0xe0, 0x47, 0x27,
-  0x0d, 0xef, 0x49, 0x4f, 0xdf, 0xb4, 0xc9, 0xd1, 0x3a, 0xbe, 0x11, 0x42,
-  0xa5, 0x38, 0xad, 0xd6, 0xd0, 0x8f, 0xa0, 0x93, 0x89, 0x0e, 0xfd, 0x14,
-  0xd8, 0x86, 0x08, 0xf0, 0x81, 0xba, 0x95, 0x52, 0x6c, 0xcc, 0x8a, 0x0b,
-  0xda, 0x62, 0x10, 0x04, 0x50, 0x2b, 0xda, 0xe4, 0xce, 0x62, 0x12, 0xa4,
-  0x00, 0x83, 0x72, 0x81, 0x02, 0x96, 0xb2, 0xa9, 0x21, 0x10, 0x00, 0x38,
-  0x21, 0x1a, 0x8a, 0x00, 0x00, 0x00, 0x01, 0xff, 0x37, 0x65, 0xa4, 0xd8,
-  0xe8, 0x6c, 0x3a, 0x13, 0x08, 0x5e, 0x0b, 0xd5, 0x64, 0xaa, 0xd5, 0x54,
-  0xd1, 0x1a, 0x5c, 0x0b, 0x01, 0x0e, 0x02, 0x47, 0xef, 0x0f, 0x26, 0xc9,
-  0xa0, 0xc1, 0x81, 0x36, 0x65, 0x73, 0x52, 0xd8, 0xac, 0x42, 0x5f, 0x8a,
-  0xe6, 0x3b, 0x57, 0xea, 0xef, 0x38, 0x5b, 0x1d, 0x62, 0x33, 0x45, 0x26,
-  0x95, 0xa3, 0xa5, 0x99, 0x76, 0x4f, 0xf7, 0x7f, 0x9b, 0xe6, 0x55, 0xca,
-  0x29, 0xbe, 0x0a, 0xe1, 0x8c, 0x31, 0xa0, 0x12, 0x34, 0x58, 0x56, 0x17,
-  0xa4, 0xf5, 0x67, 0x66, 0x69, 0x0c, 0x55, 0x15, 0x9f, 0x98, 0x52, 0xe3,
-  0x6b, 0xfc, 0x3d, 0x1d, 0x86, 0xd1, 0x86, 0xc2, 0xfd, 0xf6, 0xf3, 0x20,
-  0xa4, 0x8a, 0x1b, 0x25, 0xf8, 0x2f, 0x74, 0xad, 0xdb, 0xae, 0xe8, 0x83,
-  0x5b, 0x1e, 0x79, 0xdd, 0x5a, 0x3e, 0x34, 0x21, 0x9c, 0xfb, 0x72, 0xa0,
-  0x1f, 0x2c, 0x05, 0xd7, 0xec, 0xf4, 0xea, 0xc8, 0x60, 0x90, 0xc6, 0x9c,
-  0xc4, 0xb9, 0x51, 0x3d, 0x79, 0xd3, 0x6d, 0xb4, 0x26, 0x79, 0xd8, 0x45,
-  0x08, 0x82, 0x15, 0xb7, 0x39, 0x72, 0x4c, 0x18, 0x93, 0xbc, 0x52, 0x12,
-  0x5a, 0x05, 0x2d, 0x2b, 0xca, 0xe1, 0x22, 0x84, 0xaf, 0x6b, 0x25, 0x71,
-  0x9a, 0x57, 0x5d, 0xc7, 0xd7, 0x78, 0xff, 0x45, 0x48, 0xd1, 0x28, 0xda,
-  0x69, 0x98, 0x07, 0x52, 0xa0, 0x00, 0x8f, 0xe5, 0x7b, 0xed, 0x6e, 0x67,
-  0x00, 0x9b, 0xb8, 0xd1, 0x58, 0xa2, 0xf5, 0x46, 0xfc, 0x2e, 0xd6, 0xaa,
-  0xeb, 0x34, 0x17, 0x77, 0x22, 0x1a, 0xa3, 0xa2, 0xcb, 0x06, 0xec, 0xf1,
-  0x12, 0xb0, 0x6e, 0x66, 0xce, 0xd1, 0xa7, 0xa4, 0xd2, 0x4c, 0x34, 0xb7,
-  0x32, 0x6a, 0x7d, 0x47, 0xd6, 0x5a, 0x97, 0x08, 0x20, 0x30, 0xf4, 0xd7,
-  0x9b, 0x4a, 0x91, 0x37, 0x07, 0x12, 0xe6, 0x0e, 0x62, 0xf2, 0xbe, 0xb2,
-  0xab, 0x3e, 0x6e, 0x07, 0xc7, 0x9f, 0x19, 0xeb, 0xc9, 0x7c, 0x8f, 0x52,
-  0xc8, 0x9c, 0xc3, 0x99, 0x9f, 0x73, 0x16, 0xa1, 0x2a, 0x9a, 0xf8, 0x5a,
-  0x47, 0xc3, 0x20, 0x17, 0xbb, 0x27, 0x6a, 0x4f, 0xe4, 0xfc, 0xac, 0x4d,
-  0xaf, 0xaa, 0xbf, 0xf2, 0xc4, 0xd2, 0x2a, 0xbc, 0x6b, 0x7e, 0x9f, 0x4d,
-  0x96, 0xe0, 0x0d, 0xdc, 0xb1, 0xcf, 0xb0, 0x78, 0x3a, 0xe3, 0x56, 0x8e,
-  0x91, 0x13, 0xd3, 0xf6, 0x59, 0x61, 0x00, 0xf8, 0x4b, 0xa4, 0x72, 0xcb,
-  0x26, 0xdb, 0x66, 0x74, 0x65, 0xd5, 0xa2, 0x71, 0xb1, 0x44, 0x65, 0x47,
-  0x06, 0xc9, 0x73, 0x1b, 0xa2, 0x96, 0x03, 0xa4, 0x60, 0xa0, 0x88, 0xde,
-  0x38, 0x75, 0xf6, 0xf6, 0xe5, 0xd6, 0xda, 0xe1, 0x5b, 0xda, 0x97, 0x10,
-  0x4f, 0xa6, 0x6c, 0x31, 0xdf, 0x2c, 0x1b, 0x97, 0x50, 0xb1, 0x78, 0xcd,
-  0xba, 0xfd, 0x10, 0xa3, 0x92, 0x43, 0x1a, 0x66, 0xa3, 0x21, 0x11, 0x10,
-  0x2a, 0x8a, 0x94, 0xd0, 0x2d, 0xd3, 0x12, 0xeb, 0x80, 0x9c, 0x77, 0x38,
-  0xc1, 0x30, 0x0e, 0x21, 0x1a, 0x8e, 0x80, 0x04, 0x0f, 0x01, 0xbf, 0x33,
-  0x67, 0xa3, 0xd8, 0x60, 0xa8, 0x36, 0x18, 0xc0, 0x11, 0xe2, 0x5c, 0xc6,
-  0xa5, 0x62, 0xe2, 0xd1, 0x6b, 0xd6, 0xa1, 0x85, 0xc0, 0x1c, 0x18, 0x88,
-  0x91, 0xa4, 0x3e, 0x3b, 0x9b, 0xb9, 0xd7, 0x21, 0x07, 0xbc, 0x34, 0x47,
-  0x8e, 0xf6, 0x56, 0x9d, 0x84, 0x6c, 0xfc, 0x1b, 0x16, 0x05, 0x71, 0xaa,
-  0x12, 0x53, 0x3d, 0x5b, 0xc4, 0xa4, 0x9b, 0xbb, 0xb9, 0xd8, 0x7f, 0xd0,
-  0x6c, 0x8d, 0x8b, 0x84, 0xdf, 0xf3, 0xe7, 0x5b, 0x68, 0x33, 0x4f, 0x41,
-  0xaa, 0xca, 0xbf, 0x6f, 0xbc, 0x95, 0x1c, 0x37, 0xa2, 0xd4, 0xd0, 0xce,
-  0x25, 0x57, 0x43, 0xb9, 0x4f, 0x53, 0xc5, 0x93, 0x04, 0xe2, 0xba, 0x39,
-  0x68, 0xd1, 0xfe, 0x0a, 0xaa, 0xb3, 0xa8, 0xd9, 0x30, 0xbd, 0x9a, 0x67,
-  0x0b, 0x6c, 0x29, 0xef, 0x08, 0x4d, 0x51, 0x82, 0x93, 0x32, 0xce, 0x82,
-  0x00, 0xa8, 0x07, 0x83, 0x77, 0x6a, 0xc1, 0x23, 0x50, 0x0b, 0x45, 0x25,
-  0x60, 0x1e, 0x68, 0x01, 0x75, 0xa4, 0x4b, 0x33, 0xad, 0x1e, 0x9f, 0xd0,
-  0x01, 0x6e, 0xb1, 0xbc, 0x27, 0x44, 0x5b, 0xae, 0x08, 0xdf, 0x32, 0x4a,
-  0xd2, 0x3c, 0xb0, 0x01, 0x38, 0xee, 0x13, 0xa6, 0x99, 0x2d, 0x1d, 0x83,
-  0x73, 0x13, 0x74, 0x6a, 0xdd, 0xa8, 0x26, 0xed, 0xd4, 0x84, 0x08, 0x0c,
-  0x54, 0x41, 0x12, 0x99, 0x8d, 0x89, 0x16, 0xa9, 0x15, 0xc4, 0x8e, 0x85,
-  0xb8, 0x97, 0x0b, 0x4a, 0x0d, 0xc8, 0xc5, 0xd9, 0x1b, 0xb3, 0x2e, 0x34,
-  0x8b, 0xf3, 0x67, 0xc7, 0xd6, 0x83, 0xe1, 0xdb, 0x9a, 0xb7, 0x27, 0xd4,
-  0x74, 0x63, 0xe3, 0x75, 0xf6, 0x54, 0xca, 0x3e, 0x65, 0xa7, 0xdd, 0x98,
-  0x6d, 0xb5, 0x8a, 0x61, 0xaf, 0xeb, 0x6b, 0x9d, 0xa5, 0xd0, 0xf3, 0xfe,
-  0xd1, 0x8c, 0xf7, 0xe7, 0xd9, 0xfd, 0x32, 0xcb, 0xf6, 0x6b, 0x6e, 0xd6,
-  0x61, 0xed, 0xb6, 0x01, 0x69, 0x1d, 0xd1, 0xf2, 0x1c, 0xc6, 0xca, 0xaf,
-  0x0d, 0x1d, 0xc6, 0xae, 0x78, 0xf8, 0xdc, 0x3f, 0x29, 0xc0, 0xf9, 0x16,
-  0x70, 0x5a, 0xdc, 0x3f, 0x63, 0xac, 0xc0, 0xd9, 0x97, 0x57, 0xf3, 0x8c,
-  0x4c, 0x16, 0x71, 0x42, 0xb5, 0x57, 0xc3, 0x70, 0x89, 0x52, 0xe4, 0x11,
-  0xf5, 0x5e, 0xff, 0x85, 0x3f, 0x7d, 0xeb, 0xc7, 0x26, 0xc9, 0x95, 0x12,
-  0x2b, 0x4e, 0x33, 0x26, 0x02, 0x7d, 0x48, 0xbb, 0x64, 0x7f, 0x1e, 0xd6,
-  0x14, 0xe9, 0x40, 0x94, 0x92, 0x4a, 0x45, 0xed, 0x24, 0x37, 0x76, 0xb5,
-  0xfe, 0xc8, 0xb9, 0x91, 0x11, 0xd3, 0x68, 0x74, 0x6c, 0x73, 0x88, 0x46,
-  0x4a, 0x50, 0x8a, 0xf3, 0x82, 0xc0, 0xe0, 0x21, 0x1a, 0x8e, 0x00, 0x9e,
-  0x0f, 0x1f, 0xff, 0x33, 0x65, 0xa7, 0x31, 0x86, 0x20, 0x55, 0xa9, 0xce,
-  0x32, 0xd9, 0xd1, 0x68, 0x4b, 0xf3, 0x70, 0x09, 0x6b, 0x2d, 0x94, 0x38,
-  0x02, 0x47, 0x2e, 0x64, 0x9d, 0x01, 0xf5, 0x6b, 0x1c, 0x76, 0x31, 0x48,
-  0x30, 0xf9, 0x7f, 0x1e, 0x9b, 0x00, 0x0a, 0x49, 0xb4, 0xf8, 0x6a, 0x07,
-  0xda, 0xa5, 0x6e, 0x0e, 0xf1, 0x9b, 0xd5, 0x97, 0xee, 0x86, 0xe2, 0x96,
-  0x35, 0x86, 0x21, 0xc0, 0x6e, 0x78, 0x4f, 0x0d, 0x21, 0x6a, 0x74, 0xce,
-  0x79, 0xc0, 0xde, 0x68, 0x8b, 0xaa, 0xea, 0xf1, 0xe2, 0x94, 0x18, 0xa8,
-  0x3b, 0x65, 0x58, 0x10, 0xcb, 0xf8, 0x7e, 0x2a, 0x13, 0xc5, 0xf5, 0xa4,
-  0x66, 0xa3, 0x3c, 0xe6, 0xa2, 0x04, 0x84, 0x04, 0xa8, 0x48, 0x57, 0xb8,
-  0x64, 0xa7, 0x26, 0x56, 0xe3, 0x22, 0xdc, 0x00, 0xa5, 0x00, 0x7f, 0x7a,
-  0xdc, 0x46, 0x51, 0x85, 0xec, 0x56, 0x65, 0x4d, 0xa7, 0xd6, 0xff, 0x87,
-  0xef, 0xa0, 0xb0, 0x84, 0x04, 0xd5, 0x26, 0x46, 0x69, 0xb8, 0x9f, 0xdc,
-  0xcc, 0x23, 0x7a, 0x41, 0x2d, 0x28, 0x53, 0x37, 0xc9, 0x9e, 0x8a, 0x2d,
-  0x24, 0xa0, 0x69, 0x2c, 0x28, 0x44, 0xca, 0x0a, 0x02, 0x2c, 0x88, 0x65,
-  0xdb, 0x44, 0xe4, 0xb0, 0x52, 0x5b, 0xa9, 0x0c, 0x10, 0x22, 0x08, 0x5a,
-  0xf6, 0x3c, 0x50, 0x5e, 0x81, 0x34, 0x35, 0xe6, 0xda, 0x85, 0x90, 0x80,
-  0x60, 0x31, 0x4d, 0xf5, 0x5f, 0xe2, 0x1d, 0xee, 0x0e, 0x32, 0xc3, 0xbe,
-  0xa7, 0x78, 0xfa, 0x64, 0xcc, 0x16, 0x54, 0x9b, 0x8f, 0xa9, 0xf3, 0x4d,
-  0xb3, 0x14, 0x58, 0x32, 0x1c, 0xc9, 0x29, 0xf3, 0xdc, 0x95, 0x03, 0x92,
-  0xb4, 0x3c, 0x5d, 0xbd, 0x11, 0xfb, 0x2b, 0x62, 0xa1, 0x66, 0xbc, 0xb8,
-  0x71, 0x02, 0xe0, 0x62, 0x83, 0x89, 0xc8, 0xd1, 0x16, 0x55, 0x0f, 0xb8,
-  0x7c, 0x04, 0x6c, 0xd7, 0xae, 0x02, 0x3e, 0xb2, 0x93, 0xaf, 0x2f, 0xcb,
-  0x4c, 0x36, 0x85, 0x32, 0xf1, 0x0c, 0xac, 0xb0, 0x5a, 0xd5, 0x5e, 0x87,
-  0xe0, 0xe5, 0x29, 0x56, 0xc5, 0xab, 0xc2, 0x43, 0xf5, 0x81, 0x3c, 0x07,
-  0x88, 0x45, 0x05, 0xb8, 0x18, 0x67, 0xf0, 0xfb, 0xfe, 0xbe, 0x60, 0x8a,
-  0x35, 0x44, 0x51, 0xbc, 0x5d, 0x5a, 0x54, 0x98, 0x98, 0x39, 0x4e, 0x96,
-  0x5d, 0x24, 0xe7, 0x23, 0x15, 0x4a, 0xc2, 0x6a, 0x24, 0x5b, 0x74, 0x80,
-  0x80, 0x22, 0x23, 0x14, 0x97, 0x79, 0xbf, 0x50, 0x07, 0x21, 0x1a, 0x8f,
-  0xc0, 0x3e, 0x3f, 0xfe, 0xff, 0x3b, 0x68, 0xa3, 0x58, 0xe0, 0xec, 0x24,
-  0x10, 0xb8, 0xdf, 0x15, 0x2e, 0x77, 0xc2, 0x29, 0x4b, 0x2e, 0x4a, 0x59,
-  0x1c, 0x23, 0x0e, 0x0b, 0x2d, 0x94, 0xbc, 0x00, 0x97, 0x1c, 0xe2, 0x7f,
-  0x48, 0x1f, 0x23, 0x98, 0xe4, 0x8f, 0x5b, 0x72, 0x5b, 0x72, 0x03, 0x7b,
-  0x47, 0x2d, 0x53, 0xb4, 0xdf, 0x96, 0x90, 0xba, 0xe5, 0x01, 0x62, 0xd3,
-  0x5a, 0x97, 0x95, 0xae, 0xd3, 0x2a, 0x4b, 0x75, 0x3b, 0x39, 0xa4, 0x32,
-  0xf5, 0xbc, 0xe6, 0xe7, 0xca, 0x4b, 0x56, 0xe0, 0x3b, 0x77, 0x52, 0xb8,
-  0xe6, 0x64, 0xa9, 0x78, 0xde, 0x86, 0x87, 0x4a, 0xc2, 0x7d, 0x8f, 0x22,
-  0x37, 0xbd, 0xb9, 0x77, 0xec, 0xd7, 0x6f, 0x65, 0xd4, 0xec, 0xd8, 0x50,
-  0x92, 0x28, 0x15, 0x35, 0x93, 0x6d, 0xf9, 0xf5, 0xd7, 0x90, 0xb4, 0x28,
-  0xc6, 0x25, 0xc3, 0x3a, 0xd9, 0x21, 0xeb, 0x69, 0xf7, 0xed, 0x76, 0x95,
-  0xb6, 0xd3, 0xdf, 0xc1, 0x4e, 0xb2, 0xd3, 0x60, 0x51, 0x5d, 0x00, 0x57,
-  0x55, 0x97, 0x36, 0x5a, 0xd6, 0x27, 0x22, 0xa1, 0x51, 0x83, 0xe1, 0xc9,
-  0x02, 0x0c, 0x06, 0x93, 0xc9, 0x1d, 0xe2, 0xf5, 0x90, 0x02, 0x44, 0x00,
-  0x8d, 0xa5, 0xf4, 0x9d, 0x33, 0x64, 0xc9, 0xbe, 0x22, 0xc1, 0x5b, 0xf9,
-  0x4c, 0x31, 0x77, 0xdb, 0x18, 0xcd, 0x2b, 0x8a, 0xa0, 0x05, 0x3b, 0x2a,
-  0x17, 0x2a, 0x05, 0xe5, 0xbe, 0x51, 0xab, 0x9f, 0x49, 0x74, 0x6a, 0xb8,
-  0x4e, 0x5a, 0xa8, 0x6c, 0x5a, 0x1a, 0x04, 0x0c, 0x23, 0x01, 0xa0, 0x40,
-  0x62, 0x42, 0x9b, 0x52, 0xd7, 0x4a, 0x35, 0x75, 0x6b, 0xfc, 0x22, 0xec,
-  0x5b, 0x29, 0x78, 0x01, 0x2e, 0x39, 0x12, 0x27, 0x8a, 0x76, 0x47, 0x3f,
-  0xac, 0x91, 0xdc, 0x70, 0xea, 0x33, 0xca, 0x26, 0x60, 0xe6, 0xee, 0x8a,
-  0x76, 0xc9, 0xa1, 0xa8, 0x83, 0xde, 0x5c, 0xc3, 0x71, 0xdb, 0x52, 0x6d,
-  0x2f, 0x9a, 0x8f, 0x0c, 0x96, 0x93, 0x59, 0xd4, 0x31, 0x1e, 0xa1, 0x79,
-  0x73, 0x14, 0xcb, 0x95, 0xc4, 0xaa, 0xf7, 0x94, 0xd3, 0x52, 0x7d, 0xfe,
-  0x8f, 0x50, 0xf2, 0x8e, 0x39, 0x8c, 0xac, 0x40, 0x40, 0x92, 0xef, 0x72,
-  0xe8, 0x19, 0x72, 0x36, 0xb5, 0x8c, 0x6e, 0x30, 0x3f, 0x79, 0xef, 0x47,
-  0xb5, 0xe5, 0xbf, 0x57, 0xf8, 0x94, 0x80, 0x11, 0x12, 0x9d, 0xa2, 0xe4,
-  0xbf, 0x84, 0xa3, 0xeb, 0x68, 0x3b, 0x4f, 0xae, 0x58, 0x73, 0xf5, 0x63,
-  0x0c, 0x60, 0x11, 0x94, 0x60, 0x1c
-};
-
-guint seg_1_m4f_len = 49554;
-guint seg_1_moof_size = 1120;
-guint seg_1_sample_0_offset = 1128;
-
-static const guint seg_1_sample_sizes[] = {
-  371, 372, 477, 530, 489, 462, 441, 421, 420, 410, 402, 398, 381, 381, 386,
-  386, 369, 370, 362, 346, 357, 355, 376, 336, 341, 358, 350, 362, 333, 415,
-  386, 364, 344, 386, 358, 365, 404, 342, 361, 366, 361, 350, 390, 348, 366,
-  359, 357, 360, 349, 356, 365, 393, 353, 385, 381, 348, 345, 414, 372, 369,
-  401, 391, 333, 339, 423, 343, 445, 425, 422, 415, 406, 389, 395, 375, 356,
-  442, 432, 391, 385, 339, 277, 293, 316, 327, 309, 389, 359, 427, 326, 420,
-  407, 316, 362, 419, 349, 387, 326, 328, 367, 344, 425, 329, 379, 403, 314,
-  397, 368, 389, 380, 373, 342, 343, 368, 436, 359, 352, 361, 366, 350, 419,
-  331, 426, 401, 382, 326, 411, 364, 338, 345
-};
-
-/* in timescale */
-GstClockTime seg_1_sample_duration = 1024;
-guint32 seg_1_timescale = 44100;
-
-
-/* Fragments taken from http://dash.akamaized.net/dash264/TestCases/5c/nomor/4_1a.mpd
- *
- * Audio stream (aac)
- * Header + first Fragments
- */
-
-/* http://dash.akamaized.net/dash264/TestCases/5c/nomor/BBB_32k_init.mp4 */
-static const guint8 BBB_32k_init_mp4[] = {
-  0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x35,
-  0x00, 0x00, 0x00, 0x01, 0x69, 0x73, 0x6f, 0x35, 0x64, 0x61, 0x73, 0x68,
-  0x00, 0x00, 0x00, 0x08, 0x66, 0x72, 0x65, 0x65, 0x00, 0x00, 0x00, 0x3c,
-  0x66, 0x72, 0x65, 0x65, 0x49, 0x73, 0x6f, 0x4d, 0x65, 0x64, 0x69, 0x61,
-  0x20, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63,
-  0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x50, 0x41, 0x43,
-  0x20, 0x30, 0x2e, 0x35, 0x2e, 0x31, 0x2d, 0x44, 0x45, 0x56, 0x2d, 0x72,
-  0x65, 0x76, 0x34, 0x37, 0x33, 0x36, 0x4d, 0x00, 0x00, 0x00, 0x02, 0xac,
-  0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c, 0x6d, 0x76, 0x68, 0x64,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
-  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x6d, 0x76, 0x65, 0x78,
-  0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x09, 0x10, 0x50, 0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x65, 0x78,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
-  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x9e, 0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c,
-  0x74, 0x6b, 0x68, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
-  0xce, 0x60, 0xc8, 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x6d, 0x64, 0x69, 0x61,
-  0x00, 0x00, 0x00, 0x20, 0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x55, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
-  0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x73, 0x6f, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e,
-  0x64, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x6d, 0x69, 0x6e,
-  0x66, 0x00, 0x00, 0x00, 0x10, 0x73, 0x6d, 0x68, 0x64, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e,
-  0x66, 0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c,
-  0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa9, 0x73, 0x74, 0x62,
-  0x6c, 0x00, 0x00, 0x00, 0x5d, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4d, 0x6d, 0x70, 0x34,
-  0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
-  0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x65, 0x73, 0x64,
-  0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1b, 0x00, 0x01, 0x00, 0x04, 0x13,
-  0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x02, 0x00, 0x00, 0x7d,
-  0x02, 0x05, 0x04, 0xeb, 0x09, 0x88, 0x00, 0x06, 0x01, 0x02, 0x00, 0x00,
-  0x00, 0x10, 0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74,
-  0x73, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x75, 0x64,
-  0x74, 0x61, 0x00, 0x00, 0x00, 0x5a, 0x6d, 0x65, 0x74, 0x61, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x64, 0x69, 0x72, 0x61, 0x70,
-  0x70, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x2d, 0x69, 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x25, 0xa9,
-  0x74, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x1d, 0x64, 0x61, 0x74, 0x61, 0x00,
-  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x76, 0x66, 0x35,
-  0x35, 0x2e, 0x31, 0x36, 0x2e, 0x31, 0x30, 0x31
-};
-
-static const guint BBB_32k_init_mp4_len = 776;
-
-/* http://dash.akamaized.net/dash264/TestCases/5c/nomor/BBB_32k_1.mp4 */
-static const guint8 BBB_32k_1_mp4[] = {
-  0x00, 0x00, 0x00, 0x18, 0x73, 0x74, 0x79, 0x70, 0x6d, 0x73, 0x64, 0x68,
-  0x00, 0x00, 0x00, 0x00, 0x6d, 0x73, 0x64, 0x68, 0x6d, 0x73, 0x69, 0x78,
-  0x00, 0x00, 0x00, 0x2c, 0x73, 0x69, 0x64, 0x78, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0xa3,
-  0x00, 0x01, 0x70, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
-  0x6d, 0x6f, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x66, 0x68, 0x64,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8,
-  0x74, 0x72, 0x61, 0x66, 0x00, 0x00, 0x00, 0x14, 0x74, 0x66, 0x68, 0x64,
-  0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x10, 0x74, 0x66, 0x64, 0x74, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x74, 0x72, 0x75, 0x6e,
-  0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x01, 0x18,
-  0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0xc7,
-  0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0xcb,
-  0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, 0xc4,
-  0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0xbf,
-  0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0xa9,
-  0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9a,
-  0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x9a,
-  0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xa0,
-  0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0xa7,
-  0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x9f,
-  0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0xa8,
-  0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0xa3,
-  0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xac,
-  0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0xb1,
-  0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xa4,
-  0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x1f, 0x93, 0x6d, 0x64, 0x61, 0x74,
-  0x01, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xf5, 0x20, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x04, 0x60, 0x06, 0xf8, 0x91, 0x0a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
-  0x5a, 0x5e, 0x01, 0x30, 0x34, 0x03, 0xe9, 0x6a, 0xcb, 0x34, 0xd3, 0x87,
-  0xc3, 0xff, 0xfd, 0x12, 0xb9, 0x3c, 0x5b, 0xc6, 0x34, 0x08, 0x00, 0x00,
-  0x00, 0x00, 0x1c, 0x3e, 0x1d, 0x72, 0x48, 0xff, 0x51, 0xa7, 0xee, 0x4b,
-  0x3f, 0xf9, 0x25, 0x66, 0x02, 0xeb, 0x2c, 0x42, 0x4b, 0x42, 0xff, 0x00,
-  0x1f, 0xfc, 0x00, 0xfa, 0xa7, 0xaf, 0xd3, 0xb6, 0x97, 0xa8, 0x37, 0xb1,
-  0x08, 0x52, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2,
-  0xd2, 0xd2, 0xd2, 0xd2, 0xf0, 0x01, 0x00, 0x34, 0x2d, 0xa8, 0x95, 0x39,
-  0xad, 0xa1, 0x39, 0xc6, 0xa9, 0x75, 0x09, 0x96, 0x54, 0x2a, 0xc2, 0x02,
-  0xcd, 0x59, 0x45, 0xa6, 0x97, 0xf8, 0x00, 0x7a, 0x25, 0x29, 0x37, 0xc4,
-  0x06, 0x0a, 0xef, 0x57, 0x48, 0x91, 0x76, 0x8b, 0xfc, 0x9e, 0x7e, 0xff,
-  0x24, 0x47, 0x63, 0xe2, 0x7f, 0x27, 0xfe, 0xa9, 0x1e, 0xac, 0x67, 0x87,
-  0xfe, 0x0f, 0x65, 0xc9, 0x1a, 0x31, 0x85, 0x8a, 0x72, 0xc4, 0xab, 0xb7,
-  0x9c, 0xfb, 0x07, 0xf3, 0xfb, 0x7a, 0xef, 0x0e, 0xd2, 0xdf, 0x9b, 0x03,
-  0xba, 0xbc, 0x29, 0xa4, 0xbf, 0x4f, 0x5e, 0x4c, 0xfd, 0xfd, 0x93, 0x5d,
-  0x21, 0x0b, 0xa1, 0x72, 0x55, 0xe9, 0x1b, 0xde, 0xe7, 0x66, 0xcd, 0xa2,
-  0x54, 0xdd, 0xc4, 0xd7, 0xaf, 0xff, 0xff, 0x47, 0x48, 0xd3, 0xf2, 0x09,
-  0x67, 0xa0, 0xd3, 0xc1, 0xd3, 0xfb, 0x67, 0x21, 0x3b, 0x1d, 0x64, 0x2b,
-  0xaa, 0x0a, 0xa4, 0x97, 0x40, 0xea, 0xe9, 0xcf, 0xd3, 0x94, 0x8d, 0x3c,
-  0x11, 0xe9, 0xfc, 0x4a, 0x58, 0x2e, 0x6f, 0xca, 0x0d, 0x66, 0x9e, 0x73,
-  0x71, 0xac, 0x59, 0x2b, 0xa2, 0x8e, 0x20, 0x6f, 0x18, 0xd0, 0x20, 0x00,
-  0x00, 0x00, 0x00, 0x4a, 0x7c, 0x3a, 0xe4, 0x91, 0xfe, 0xa3, 0x4f, 0xdc,
-  0x96, 0x7f, 0xf2, 0x4a, 0xcc, 0x05, 0xd6, 0x58, 0x84, 0x96, 0x85, 0xfe,
-  0x00, 0x3f, 0xf8, 0x01, 0xf5, 0x4f, 0x5f, 0xa7, 0x6d, 0x2f, 0x50, 0xe0,
-  0x01, 0x0c, 0x34, 0x14, 0x90, 0x7b, 0x3c, 0x14, 0x85, 0x04, 0x61, 0xc0,
-  0x48, 0x2e, 0x24, 0x11, 0x88, 0xc2, 0x9b, 0xdd, 0xdc, 0x34, 0xb2, 0x82,
-  0xd9, 0x29, 0x69, 0x88, 0x29, 0x6b, 0x09, 0xa4, 0xca, 0xec, 0xe8, 0xa6,
-  0xfa, 0x43, 0x9c, 0xfa, 0x33, 0x28, 0x0d, 0x08, 0x77, 0xad, 0xb2, 0x35,
-  0x01, 0x1b, 0x1d, 0xea, 0x2a, 0xbd, 0x19, 0xc5, 0x14, 0x62, 0x8c, 0x0d,
-  0x69, 0xd6, 0xd8, 0xd5, 0x16, 0x0e, 0xc0, 0xf1, 0x38, 0xcc, 0xcd, 0x70,
-  0x40, 0x01, 0xbb, 0x05, 0xb2, 0x0e, 0xa7, 0x82, 0x61, 0x2d, 0xb6, 0x59,
-  0xd2, 0x60, 0x35, 0x76, 0xea, 0x73, 0xc0, 0x00, 0xd5, 0x6f, 0xe8, 0x66,
-  0x2d, 0x88, 0x47, 0xf5, 0x93, 0x80, 0x10, 0x97, 0x0b, 0xe4, 0x59, 0xed,
-  0xeb, 0xa3, 0x00, 0xe3, 0x5a, 0xc8, 0x79, 0xe0, 0x8c, 0xe0, 0x01, 0x96,
-  0xb3, 0x05, 0x12, 0x52, 0xb5, 0xf0, 0xbc, 0xc4, 0xe8, 0xc6, 0xcc, 0xd1,
-  0x10, 0x2e, 0x0d, 0x7c, 0x91, 0xe5, 0xf2, 0xc1, 0x7b, 0xeb, 0x6c, 0x66,
-  0x66, 0x64, 0xbc, 0x8e, 0x77, 0x18, 0x67, 0x1a, 0x4d, 0x37, 0x0d, 0x67,
-  0x77, 0x8b, 0xa1, 0xc9, 0x90, 0xde, 0x27, 0x5c, 0xe1, 0xa0, 0xcf, 0x4f,
-  0x34, 0x02, 0xd0, 0x98, 0x49, 0x2a, 0xcb, 0x2c, 0xfd, 0x7e, 0x6f, 0xff,
-  0xaa, 0xbe, 0x6a, 0xd8, 0xe8, 0x4b, 0x85, 0x3c, 0x7f, 0xf0, 0x47, 0x36,
-  0x18, 0xe6, 0xbc, 0x3a, 0x9c, 0x37, 0x8f, 0xb2, 0x19, 0x9d, 0x5e, 0xb6,
-  0x0a, 0xcb, 0x0e, 0xfe, 0x00, 0xde, 0x15, 0xa0, 0x50, 0x03, 0x7d, 0x7d,
-  0x86, 0xc9, 0x5c, 0xac, 0x7a, 0xcc, 0x8b, 0x88, 0x54, 0xb5, 0x17, 0x73,
-  0x64, 0xd7, 0xc0, 0xbb, 0x69, 0x98, 0xa1, 0xc0, 0x01, 0x08, 0x34, 0x10,
-  0x90, 0x7b, 0x0d, 0x19, 0xcf, 0x02, 0x40, 0xb8, 0x98, 0x2e, 0x12, 0x13,
-  0x85, 0x4a, 0x52, 0xab, 0x96, 0x04, 0x4a, 0xc2, 0x6b, 0x1a, 0x55, 0x2d,
-  0x32, 0xd5, 0x6b, 0x05, 0x8e, 0x3d, 0xe8, 0x61, 0xe2, 0xd0, 0x32, 0x8a,
-  0xc2, 0x34, 0x50, 0x26, 0x9f, 0x2a, 0xda, 0xe6, 0xc8, 0x4b, 0xe2, 0x83,
-  0x4f, 0x34, 0x48, 0xed, 0xeb, 0x84, 0x01, 0x50, 0x20, 0x11, 0xa4, 0x54,
-  0xb1, 0x79, 0x62, 0xe9, 0x53, 0x6f, 0x7a, 0xff, 0xc5, 0x52, 0xe0, 0x11,
-  0x8e, 0x35, 0xbb, 0x6f, 0xc6, 0xd1, 0xb5, 0x67, 0xaf, 0xc6, 0xfd, 0xdc,
-  0x49, 0x41, 0x81, 0x95, 0x84, 0x88, 0x55, 0x25, 0x13, 0x4d, 0xdc, 0x76,
-  0x39, 0x66, 0x40, 0xa2, 0xd5, 0x57, 0x42, 0x9d, 0x14, 0xc6, 0x28, 0xad,
-  0x4f, 0xa9, 0xc8, 0xa2, 0x71, 0xbc, 0x33, 0xa9, 0x12, 0x66, 0x67, 0x01,
-  0x7a, 0xcf, 0x5f, 0xf1, 0xe1, 0xab, 0x16, 0x80, 0xb0, 0xd4, 0xe1, 0x1f,
-  0xe1, 0xd2, 0xcb, 0xf1, 0x37, 0x9b, 0x53, 0x71, 0x96, 0x6e, 0x57, 0x23,
-  0xdb, 0x9c, 0x66, 0x54, 0x00, 0x04, 0x6d, 0xad, 0xda, 0x0c, 0xe7, 0x96,
-  0x9a, 0x49, 0x01, 0x7d, 0xbf, 0x96, 0x63, 0x2c, 0xeb, 0x79, 0x33, 0x74,
-  0x21, 0x58, 0x8d, 0xeb, 0x35, 0x10, 0xdc, 0x58, 0x2e, 0xd9, 0x9d, 0x5b,
-  0x37, 0x7a, 0xf9, 0x80, 0x06, 0xf0, 0xdd, 0x02, 0x82, 0x2e, 0xed, 0x97,
-  0xd7, 0x25, 0x30, 0xf8, 0x0c, 0x31, 0x7b, 0xcd, 0x9d, 0xcc, 0x7b, 0xdb,
-  0xf7, 0xa6, 0xef, 0x9f, 0xbd, 0xeb, 0x79, 0x28, 0x00, 0x0e, 0x01, 0x08,
-  0x34, 0x08, 0x8c, 0x7b, 0x23, 0x0a, 0xc3, 0x2c, 0x60, 0xb8, 0x50, 0x4a,
-  0x24, 0x18, 0x05, 0xd9, 0x8d, 0xca, 0x15, 0x20, 0x2d, 0x49, 0x59, 0xa4,
-  0xb2, 0x63, 0x54, 0x15, 0xa4, 0xa0, 0x2a, 0xb4, 0x99, 0x19, 0x26, 0x59,
-  0x32, 0xd9, 0x6b, 0x2b, 0xad, 0x91, 0x50, 0x2c, 0xb0, 0x28, 0xc4, 0x4f,
-  0x01, 0x47, 0x41, 0x85, 0x5f, 0x04, 0x0a, 0xed, 0x1c, 0xe6, 0xea, 0x9d,
-  0x11, 0xf9, 0xe0, 0x0b, 0x56, 0x3c, 0xce, 0x66, 0x51, 0x0a, 0xcf, 0xc3,
-  0x7b, 0x5e, 0x56, 0xa6, 0x0b, 0xbb, 0x10, 0x65, 0x2a, 0xe3, 0x75, 0x1f,
-  0x23, 0xd3, 0x73, 0x66, 0x72, 0x30, 0xbc, 0xb1, 0xef, 0x76, 0xeb, 0x31,
-  0xc8, 0x45, 0x69, 0xf8, 0x7e, 0xb1, 0x73, 0x71, 0x9e, 0x86, 0x0a, 0x48,
-  0x29, 0x86, 0x20, 0x64, 0x9a, 0x5e, 0xbe, 0xfb, 0x87, 0x78, 0x62, 0x95,
-  0x10, 0x70, 0x51, 0x1a, 0xfa, 0xfb, 0xf3, 0x3b, 0x66, 0x8a, 0x18, 0xd9,
-  0x6f, 0xbf, 0xe9, 0x81, 0xc1, 0x2f, 0xab, 0x5e, 0x8e, 0x12, 0x25, 0x41,
-  0xb0, 0x95, 0xf7, 0x55, 0x71, 0x96, 0x48, 0xcc, 0x7a, 0xcb, 0xab, 0xa1,
-  0xdf, 0xf4, 0x55, 0x35, 0x20, 0x51, 0x9c, 0x77, 0x8c, 0xcf, 0x57, 0x1e,
-  0xf5, 0x4d, 0xdf, 0x7b, 0x26, 0x27, 0x53, 0x78, 0x56, 0x81, 0x51, 0x16,
-  0x50, 0x61, 0x6b, 0x43, 0xe0, 0x10, 0xb8, 0xce, 0xe2, 0x55, 0xb0, 0xb3,
-  0x46, 0x5f, 0x75, 0xdf, 0x7d, 0xd8, 0xc8, 0xc0, 0x07, 0x01, 0x08, 0x34,
-  0x19, 0x76, 0x18, 0x2d, 0x85, 0xda, 0x88, 0x30, 0x98, 0x3b, 0x36, 0x04,
-  0x16, 0xdc, 0xd6, 0x22, 0x4a, 0xb2, 0x13, 0x00, 0x8a, 0x19, 0xa3, 0x41,
-  0xf8, 0x7f, 0x3b, 0xd6, 0x6c, 0x3d, 0x1c, 0xb2, 0x59, 0x5b, 0x39, 0xa4,
-  0xb0, 0x2e, 0x06, 0x29, 0xe1, 0x32, 0xa8, 0xf2, 0xd6, 0xb8, 0x6d, 0xb5,
-  0x8a, 0x50, 0xdc, 0xa1, 0x20, 0xb4, 0xf5, 0x8f, 0x9d, 0x43, 0x8a, 0xca,
-  0x26, 0x81, 0xa7, 0x9d, 0x53, 0x44, 0xd8, 0xcb, 0xaa, 0xd1, 0x9a, 0x63,
-  0x2a, 0xab, 0x88, 0xc2, 0x73, 0x94, 0xee, 0xa8, 0xd6, 0xf2, 0x7d, 0x6f,
-  0x7d, 0xa1, 0x73, 0x73, 0x15, 0xc8, 0xae, 0x4e, 0xa6, 0x9e, 0xd9, 0xc2,
-  0x2a, 0x6a, 0x6b, 0x47, 0xe0, 0xb3, 0x34, 0x63, 0x2d, 0x5c, 0x2e, 0x57,
-  0x2d, 0x48, 0xd1, 0xab, 0xe0, 0x6a, 0xeb, 0x7f, 0x6f, 0xa6, 0xc7, 0x3b,
-  0x24, 0x21, 0xad, 0xf6, 0x49, 0x3c, 0xf3, 0xbc, 0x93, 0xab, 0x59, 0x33,
-  0x3b, 0x27, 0x8a, 0xd5, 0x3b, 0x39, 0x07, 0xa8, 0x3e, 0xab, 0xe6, 0x7e,
-  0xbb, 0x78, 0x5b, 0x99, 0xef, 0x89, 0x81, 0x75, 0x55, 0xb4, 0x41, 0x12,
-  0x6e, 0x44, 0x10, 0x58, 0xbe, 0x7a, 0x61, 0x24, 0x15, 0x2f, 0x72, 0xb2,
-  0xd0, 0x53, 0x00, 0xb4, 0xdc, 0x13, 0x4d, 0x4e, 0x37, 0x85, 0x7d, 0x7e,
-  0xf7, 0x59, 0x00, 0x37, 0x84, 0x68, 0x16, 0x11, 0x5d, 0xa8, 0x0e, 0x0d,
-  0xcc, 0xec, 0x37, 0x5b, 0x38, 0x79, 0x8e, 0xd9, 0x3f, 0xcb, 0x4d, 0x96,
-  0xc8, 0xb0, 0x00, 0x70, 0x01, 0x08, 0x34, 0x08, 0x8c, 0x7b, 0x0c, 0x98,
-  0x86, 0xe6, 0x62, 0xa0, 0x94, 0xcc, 0x29, 0xda, 0x87, 0x36, 0x33, 0x4c,
-  0xd4, 0x2a, 0xa2, 0xd1, 0x05, 0x83, 0x16, 0x31, 0x15, 0x75, 0xfe, 0xa9,
-  0x3a, 0x43, 0x07, 0x6a, 0x69, 0xa2, 0x83, 0xd6, 0xf3, 0x33, 0xd2, 0x62,
-  0x2a, 0x9c, 0x3d, 0xdb, 0xff, 0xb1, 0xf7, 0x0c, 0x26, 0xd9, 0xd5, 0x63,
-  0xc0, 0xe8, 0x78, 0x3d, 0xc1, 0xb6, 0x62, 0xf4, 0x1e, 0xdb, 0xbb, 0x7b,
-  0x8a, 0x8a, 0xd6, 0xe4, 0x7f, 0xaf, 0x1d, 0x32, 0xe6, 0x12, 0x82, 0x24,
-  0x86, 0x58, 0x74, 0x74, 0x7b, 0x0c, 0x23, 0x3a, 0x8c, 0x09, 0x16, 0xe9,
-  0xf3, 0x24, 0x00, 0x00, 0x45, 0x03, 0xfe, 0x89, 0xa0, 0xd9, 0xa2, 0x1c,
-  0x0c, 0x00, 0x01, 0x0a, 0x12, 0x91, 0x9b, 0xf3, 0xdf, 0x4c, 0x8c, 0x65,
-  0x01, 0x08, 0x6e, 0x3a, 0xa7, 0x31, 0x2a, 0xf1, 0x98, 0x77, 0x31, 0x18,
-  0x00, 0x1a, 0x79, 0x4d, 0x6e, 0xbb, 0x7e, 0x9c, 0xd2, 0x5b, 0x99, 0x44,
-  0x42, 0xc9, 0xb1, 0xbe, 0x5a, 0xa6, 0xe4, 0xb6, 0x21, 0xa6, 0xec, 0x26,
-  0x62, 0xa9, 0x88, 0x99, 0x4e, 0xdb, 0x9b, 0xe9, 0xe8, 0xc6, 0x4d, 0x5c,
-  0xd6, 0xe3, 0x74, 0xa1, 0x10, 0x4a, 0x23, 0x59, 0xdd, 0xe1, 0x7f, 0x1d,
-  0x4e, 0x40, 0x03, 0x78, 0x3e, 0x81, 0x61, 0x17, 0xd7, 0x08, 0x4c, 0xc8,
-  0x31, 0xc8, 0x7b, 0x74, 0xdc, 0x7d, 0xfd, 0xc9, 0x89, 0x9e, 0x0e, 0xed,
-  0x20, 0x87, 0x01, 0x08, 0x34, 0x11, 0x72, 0x44, 0x14, 0xb5, 0x14, 0x19,
-  0xe8, 0x66, 0x40, 0xb1, 0x89, 0x65, 0x17, 0x97, 0x60, 0xb0, 0xc4, 0x85,
-  0x11, 0xdf, 0x32, 0xfd, 0x3b, 0xf1, 0x44, 0x3d, 0xf5, 0x1e, 0x7f, 0xfd,
-  0x3f, 0x7d, 0xc6, 0x77, 0xa7, 0x1e, 0x07, 0x4f, 0x3a, 0x7f, 0xb2, 0xe6,
-  0x49, 0x3e, 0x9c, 0xd2, 0x1b, 0x3f, 0x25, 0xe8, 0x6e, 0xe2, 0x0a, 0x59,
-  0x59, 0x65, 0xd6, 0xe7, 0x33, 0x1b, 0x17, 0x86, 0x55, 0x8c, 0x93, 0x12,
-  0x2a, 0x71, 0x9a, 0x4e, 0xcc, 0x75, 0xfb, 0x0e, 0xe3, 0xa3, 0x8e, 0x41,
-  0x5b, 0x3b, 0xde, 0xb6, 0xe2, 0x29, 0x37, 0x50, 0xe7, 0x45, 0x19, 0xc6,
-  0x37, 0xd3, 0xee, 0xd9, 0x55, 0xdd, 0xcb, 0x53, 0xe5, 0x7f, 0x20, 0x0f,
-  0x6e, 0xad, 0xec, 0xce, 0x11, 0x64, 0x79, 0x08, 0x6e, 0x14, 0x88, 0x8e,
-  0xbc, 0x05, 0x2c, 0x1e, 0xe9, 0xf1, 0xef, 0x68, 0xaf, 0x2c, 0xb2, 0x55,
-  0x8c, 0x5b, 0x00, 0x57, 0xda, 0xab, 0xcd, 0x0d, 0xa7, 0xdd, 0xde, 0xf5,
-  0x6c, 0x7b, 0x9c, 0x61, 0x9b, 0x03, 0xbd, 0x20, 0xa9, 0xb6, 0x41, 0x25,
-  0x03, 0x4c, 0x20, 0x89, 0xde, 0x0a, 0x13, 0xac, 0xbf, 0xff, 0x0d, 0x3a,
-  0x18, 0xee, 0xee, 0xef, 0xbc, 0x27, 0x40, 0xb2, 0x8c, 0xd6, 0x9b, 0x5b,
-  0x90, 0x0f, 0x5c, 0x91, 0x34, 0xcb, 0xe8, 0x38, 0x30, 0x69, 0xe0, 0xa4,
-  0xf1, 0x00, 0x68, 0xd9, 0x03, 0x07, 0x01, 0x0c, 0x34, 0x10, 0xb4, 0x77,
-  0x4b, 0x09, 0xcc, 0x86, 0x50, 0x90, 0xcc, 0x20, 0x56, 0x65, 0x78, 0x24,
-  0x05, 0x59, 0x02, 0x2c, 0x00, 0x47, 0x61, 0x7e, 0xec, 0x30, 0x9a, 0x84,
-  0xd5, 0xca, 0xd1, 0x76, 0x9c, 0x27, 0x27, 0x9e, 0x2f, 0x81, 0xa7, 0x3c,
-  0x2f, 0xd2, 0x4b, 0xdd, 0xf7, 0x1c, 0xf4, 0xac, 0x18, 0xd6, 0x19, 0x76,
-  0x1d, 0xa6, 0xda, 0x5d, 0xe2, 0x48, 0xaa, 0xcb, 0x88, 0x89, 0x90, 0xbf,
-  0xe7, 0xdd, 0x70, 0xd4, 0xca, 0x89, 0xb9, 0xaa, 0x5b, 0x16, 0x5a, 0xaf,
-  0x81, 0xca, 0xbb, 0x83, 0x39, 0x4f, 0x17, 0x66, 0x22, 0xd5, 0x94, 0x61,
-  0xd1, 0xa7, 0x9c, 0x30, 0x4f, 0xf6, 0x4e, 0xa2, 0xc7, 0x38, 0x52, 0x4f,
-  0x9f, 0x96, 0xdc, 0x5d, 0x56, 0x98, 0x1b, 0xd7, 0xe8, 0x8a, 0x5c, 0xb7,
-  0x1a, 0xf7, 0x22, 0x59, 0x4d, 0x34, 0x6f, 0x1b, 0x8e, 0x82, 0x89, 0xca,
-  0x35, 0x9c, 0xa4, 0x0d, 0xaf, 0x48, 0xf5, 0xc4, 0xec, 0xd6, 0x7c, 0x97,
-  0x25, 0x40, 0xc5, 0xcb, 0x95, 0x40, 0xa5, 0x80, 0x7f, 0x73, 0xb0, 0x31,
-  0x55, 0x9d, 0x62, 0xb9, 0x59, 0x7e, 0x09, 0xfd, 0xd1, 0x8f, 0x40, 0x0d,
-  0xe1, 0x1a, 0x05, 0x94, 0x76, 0xe3, 0x35, 0x72, 0xba, 0x1d, 0x86, 0x31,
-  0x95, 0x36, 0x22, 0xdf, 0xe5, 0x9e, 0xd9, 0x9d, 0xa1, 0x2d, 0x80, 0x1c,
-  0x01, 0x0c, 0x34, 0x11, 0x76, 0x19, 0x59, 0x0d, 0xc2, 0x8b, 0x21, 0x18,
-  0x6c, 0xc5, 0x6e, 0xa2, 0x08, 0xcb, 0x41, 0x02, 0x0b, 0x05, 0xaa, 0x2f,
-  0x73, 0xe5, 0x3a, 0xe7, 0x9f, 0xab, 0xf0, 0xbc, 0xd3, 0x47, 0x1b, 0xdd,
-  0x8f, 0xe1, 0x93, 0x15, 0xa7, 0xe0, 0xf4, 0x33, 0xe5, 0xea, 0x48, 0x9b,
-  0x9b, 0xcf, 0x2d, 0x6f, 0xc6, 0xe5, 0x99, 0x9d, 0x27, 0x06, 0x55, 0x51,
-  0xab, 0x3a, 0x48, 0x6a, 0x71, 0x3d, 0x53, 0x46, 0xcc, 0x82, 0xf2, 0xde,
-  0x37, 0xdb, 0xfd, 0xf3, 0xf5, 0x7f, 0x99, 0x84, 0xd5, 0xad, 0x1f, 0xc1,
-  0xb9, 0x19, 0x79, 0xef, 0xeb, 0xa2, 0x9d, 0x0b, 0x28, 0xc6, 0x77, 0x06,
-  0x15, 0x85, 0xc9, 0x39, 0xbf, 0xed, 0xbe, 0xdd, 0x25, 0xa2, 0x31, 0xa3,
-  0x69, 0x06, 0x10, 0x42, 0xb7, 0x53, 0x83, 0x11, 0x02, 0xa6, 0x42, 0xe8,
-  0x73, 0x66, 0x70, 0x40, 0xef, 0x9b, 0xba, 0xea, 0x1b, 0xec, 0x6b, 0xd9,
-  0x60, 0xf2, 0x04, 0x6c, 0x53, 0xbe, 0x39, 0x72, 0x11, 0x40, 0x7a, 0x9c,
-  0x8c, 0x2b, 0x03, 0x3e, 0x58, 0xbf, 0x87, 0x93, 0x2b, 0x4d, 0x80, 0xbd,
-  0x6e, 0xbb, 0x9b, 0x1c, 0x40, 0x00, 0x0d, 0xe1, 0x5a, 0x07, 0x55, 0x45,
-  0x1d, 0x75, 0xd0, 0xf8, 0x05, 0x99, 0x17, 0x3c, 0xe3, 0x96, 0x89, 0xd8,
-  0x58, 0xb5, 0xfc, 0x9e, 0x73, 0x57, 0x82, 0x60, 0x1c, 0x01, 0x08, 0x34,
-  0x08, 0x8c, 0x7b, 0x22, 0xb5, 0x14, 0x62, 0x6d, 0x55, 0xbc, 0xb5, 0x1d,
-  0xe5, 0xc6, 0x74, 0x60, 0x16, 0x16, 0x80, 0x2f, 0xc4, 0x63, 0x25, 0xbb,
-  0x6f, 0x71, 0xbe, 0xa1, 0xb2, 0x9e, 0xf5, 0xbd, 0x5b, 0xff, 0x3e, 0xbc,
-  0x84, 0xa2, 0x86, 0x35, 0x1b, 0xd8, 0xa1, 0x02, 0x8a, 0xad, 0x99, 0xba,
-  0x4d, 0xb3, 0x3e, 0xec, 0xd4, 0x6e, 0x33, 0xaa, 0xc1, 0x29, 0xae, 0xaf,
-  0xa7, 0xb1, 0x11, 0x1f, 0xa7, 0x7f, 0xfb, 0xba, 0x42, 0x12, 0x81, 0x18,
-  0x81, 0x58, 0xed, 0xfe, 0xfd, 0x31, 0x3b, 0x69, 0x75, 0xf1, 0xea, 0xf6,
-  0xc6, 0xce, 0xe6, 0x2b, 0xff, 0x64, 0x75, 0x7e, 0x87, 0x61, 0x18, 0x8d,
-  0x5d, 0xcc, 0x3b, 0xef, 0x42, 0x76, 0xca, 0x6a, 0x51, 0xba, 0x37, 0x39,
-  0x2c, 0x78, 0x3f, 0x4d, 0x92, 0x85, 0x99, 0xfc, 0x5e, 0x88, 0xbe, 0xc6,
-  0x01, 0x84, 0x00, 0x5d, 0xde, 0xa4, 0xa6, 0x68, 0x0d, 0x24, 0x0a, 0x3b,
-  0xa1, 0x20, 0x1a, 0xf5, 0xef, 0x64, 0xfa, 0x36, 0x45, 0x50, 0x55, 0x60,
-  0x39, 0x66, 0xde, 0x4c, 0x9c, 0xbe, 0xc5, 0xb7, 0x58, 0x0a, 0xdf, 0x45,
-  0x5c, 0xff, 0x67, 0x97, 0xd2, 0xb9, 0x60, 0x6f, 0x08, 0xd0, 0x2a, 0xa3,
-  0xb7, 0x03, 0xaa, 0xf9, 0xa1, 0x3b, 0x0e, 0xf2, 0x2c, 0xad, 0xcb, 0x35,
-  0xb6, 0xce, 0xfa, 0xcd, 0x9a, 0x4d, 0x80, 0xe0, 0x01, 0x0e, 0x34, 0x08,
-  0x94, 0xb7, 0x3a, 0xb0, 0xcc, 0xa8, 0x2b, 0xb6, 0x30, 0x06, 0xb9, 0x52,
-  0xea, 0xc6, 0x68, 0x2d, 0x80, 0xe1, 0x68, 0x7a, 0x62, 0xd7, 0x76, 0x24,
-  0xa6, 0xce, 0x76, 0xe4, 0xc0, 0x42, 0x94, 0x40, 0xbc, 0xa4, 0x34, 0x40,
-  0x89, 0xc2, 0x24, 0xbc, 0x0c, 0x29, 0xb9, 0xd7, 0xe0, 0xb7, 0x5e, 0x51,
-  0x75, 0x82, 0x3f, 0x47, 0x00, 0x5a, 0x27, 0x12, 0xe3, 0x64, 0x22, 0xb3,
-  0xdd, 0xd5, 0xea, 0x9b, 0x88, 0x10, 0x22, 0x42, 0x26, 0x67, 0x5f, 0xd3,
-  0x79, 0xc6, 0xe0, 0xab, 0xe8, 0xee, 0xe3, 0x8a, 0xc2, 0x60, 0xab, 0xcf,
-  0xc7, 0x52, 0x31, 0xce, 0xed, 0x60, 0x8a, 0x6a, 0xaf, 0x8f, 0x0c, 0xcc,
-  0x5d, 0x28, 0x26, 0xa9, 0x15, 0x02, 0xa2, 0x2c, 0x5d, 0x1b, 0x5e, 0x50,
-  0x5e, 0x7e, 0xc2, 0x89, 0xd0, 0xa9, 0x4e, 0x8b, 0x2a, 0x45, 0x93, 0x6e,
-  0xd1, 0x9d, 0x4e, 0x99, 0xa8, 0xe9, 0xd4, 0xaf, 0x1d, 0xac, 0x34, 0x20,
-  0x12, 0x2f, 0xad, 0x1c, 0xf1, 0x03, 0x78, 0x46, 0x81, 0x55, 0x1d, 0xf5,
-  0xc5, 0x8d, 0x3c, 0x03, 0x39, 0x0f, 0x2c, 0x61, 0x35, 0x8e, 0xd3, 0xf9,
-  0xa2, 0xc9, 0x85, 0xac, 0x00, 0x07, 0x01, 0x0e, 0x34, 0x19, 0x76, 0x15,
-  0x7a, 0x0d, 0x48, 0x66, 0x30, 0x65, 0x0d, 0xd6, 0xea, 0x33, 0x5c, 0x36,
-  0x04, 0xa8, 0xb2, 0x73, 0xa0, 0x01, 0xb9, 0xb0, 0x01, 0x51, 0x4d, 0x59,
-  0xb6, 0x58, 0x0f, 0x92, 0x95, 0x50, 0x00, 0xea, 0x44, 0x43, 0x35, 0x87,
-  0x4f, 0x46, 0x7a, 0xbf, 0x0f, 0xd9, 0xe1, 0xce, 0x3a, 0xd8, 0x86, 0xb5,
-  0x9e, 0x69, 0xaa, 0x99, 0xd4, 0x52, 0xd3, 0x8f, 0x76, 0xb5, 0x6d, 0xcf,
-  0x67, 0x8f, 0xde, 0xaa, 0x22, 0x6d, 0x18, 0x51, 0x34, 0x84, 0xf5, 0x7d,
-  0xbe, 0x18, 0xc4, 0xd5, 0xc9, 0x5e, 0x65, 0xd9, 0x29, 0x23, 0x05, 0x75,
-  0xbf, 0xd5, 0x5c, 0xd8, 0xd2, 0xd9, 0x4a, 0x98, 0x5a, 0x25, 0xf5, 0xf5,
-  0x61, 0x2b, 0x48, 0x5c, 0xad, 0x84, 0x6e, 0x34, 0xdc, 0xa1, 0x84, 0xab,
-  0x59, 0x51, 0xa9, 0x9b, 0x9f, 0xfe, 0x1d, 0x26, 0x39, 0xa3, 0xa0, 0x64,
-  0xea, 0x79, 0xfc, 0xa2, 0xd1, 0x04, 0xe9, 0x24, 0xf5, 0x92, 0x8c, 0xfe,
-  0x05, 0x6c, 0x05, 0xd1, 0x6f, 0xf4, 0x76, 0xf1, 0x81, 0xbc, 0x23, 0x40,
-  0xaa, 0x8f, 0xdb, 0x7b, 0x09, 0xe6, 0x11, 0x1c, 0x87, 0x6c, 0xbb, 0xfb,
-  0x98, 0x83, 0x5b, 0xb1, 0xdc, 0xa4, 0x69, 0x34, 0x03, 0x07, 0x01, 0x06,
-  0x34, 0x19, 0x4e, 0x26, 0x4a, 0x91, 0x04, 0xc1, 0x41, 0x19, 0xc4, 0x66,
-  0x1b, 0xde, 0x54, 0x78, 0x2a, 0xc1, 0x96, 0x80, 0x2c, 0x00, 0x00, 0xa8,
-  0x8c, 0xdc, 0x9f, 0xb7, 0xef, 0x7a, 0x4c, 0x76, 0xdb, 0x48, 0xca, 0x4f,
-  0xfe, 0x7c, 0x0a, 0x27, 0x40, 0x0b, 0xe1, 0xbe, 0x5f, 0xd0, 0x0b, 0x38,
-  0xc8, 0xea, 0x15, 0xef, 0x63, 0x42, 0x12, 0x54, 0x31, 0x22, 0x73, 0x8e,
-  0xbc, 0xc0, 0x86, 0xfe, 0x89, 0x8a, 0x0a, 0x21, 0x4a, 0x49, 0xa7, 0x2d,
-  0xa0, 0xc9, 0x35, 0x91, 0x32, 0xf1, 0x12, 0x45, 0xc5, 0xf0, 0x04, 0xf1,
-  0xaa, 0xde, 0xa8, 0x04, 0x4c, 0x57, 0xef, 0xae, 0x81, 0xdc, 0xa0, 0x8c,
-  0xe3, 0xbc, 0xd2, 0x97, 0x85, 0xe8, 0x61, 0x08, 0xf4, 0xaa, 0xd6, 0x5d,
-  0x4f, 0x67, 0x2e, 0xb2, 0xaa, 0x8d, 0xb2, 0xf2, 0xf0, 0x38, 0x99, 0xab,
-  0x15, 0x84, 0x7b, 0xd0, 0xc7, 0x5d, 0xd8, 0xba, 0xc2, 0xaf, 0x5f, 0x60,
-  0x0d, 0x7d, 0x7e, 0xc0, 0x37, 0x87, 0x68, 0x31, 0x54, 0x7f, 0x71, 0x8e,
-  0xeb, 0x11, 0xee, 0x8a, 0xa0, 0xfa, 0x63, 0xd0, 0xfe, 0xfe, 0xd4, 0x5d,
-  0xfd, 0xac, 0x27, 0xa7, 0xf3, 0x35, 0xd3, 0xd6, 0x01, 0x00, 0x70, 0x01,
-  0x0a, 0x34, 0x10, 0x90, 0xb5, 0x6a, 0x14, 0xce, 0x14, 0xde, 0x37, 0xc8,
-  0x16, 0x60, 0x15, 0xa4, 0x02, 0xd8, 0x1d, 0x08, 0x17, 0x96, 0x29, 0xf1,
-  0xa9, 0x2a, 0xe2, 0x35, 0xfa, 0xc4, 0x9b, 0x54, 0x06, 0x87, 0xfa, 0xd7,
-  0x9c, 0x63, 0x52, 0x81, 0x50, 0xf0, 0x20, 0xc1, 0x25, 0x53, 0xb8, 0xb4,
-  0xa3, 0xbf, 0xe8, 0xc5, 0xef, 0x38, 0x81, 0x4b, 0xdd, 0xf6, 0x74, 0x5c,
-  0xaa, 0xfd, 0x33, 0xfe, 0x60, 0x00, 0xdd, 0x66, 0x6a, 0x33, 0x16, 0xe3,
-  0xdb, 0xf3, 0x98, 0x5a, 0x53, 0xe7, 0x5d, 0xdb, 0xa5, 0x57, 0x73, 0xfa,
-  0xb4, 0xc4, 0x3c, 0xa7, 0x6d, 0x9d, 0xcb, 0x33, 0x28, 0x31, 0xcf, 0xfc,
-  0xcb, 0x61, 0x25, 0xb8, 0x65, 0x59, 0x76, 0x19, 0xb9, 0x1c, 0x91, 0xe6,
-  0x55, 0x12, 0x3b, 0xac, 0xc8, 0x15, 0xb4, 0xa9, 0x7e, 0xab, 0xec, 0x98,
-  0x0a, 0x46, 0x6a, 0x9c, 0x1c, 0x8c, 0xaa, 0x34, 0xfa, 0x53, 0xa1, 0x59,
-  0xa5, 0x92, 0xe4, 0x09, 0xad, 0x6b, 0xcf, 0x58, 0x0d, 0xe1, 0x3a, 0x04,
-  0x54, 0x7f, 0x5b, 0xb1, 0x33, 0x1e, 0x9c, 0x76, 0x14, 0xa9, 0x15, 0x25,
-  0x9b, 0x8d, 0x0e, 0xd2, 0xbd, 0xb5, 0x6a, 0x86, 0xc0, 0x1c, 0x01, 0x08,
-  0x34, 0x19, 0x76, 0x16, 0x1a, 0x19, 0x4e, 0x6c, 0x36, 0x73, 0x4c, 0x1c,
-  0xe1, 0x0b, 0xc0, 0x65, 0x96, 0x11, 0x14, 0x00, 0x24, 0x28, 0x07, 0xbf,
-  0x34, 0xdd, 0x3c, 0x94, 0x26, 0xcd, 0x53, 0xbd, 0x78, 0x5a, 0xf5, 0x5c,
-  0x06, 0x08, 0x8a, 0x0d, 0x2f, 0x6a, 0x41, 0xa8, 0x2d, 0x28, 0xb3, 0x43,
-  0xbc, 0xb4, 0x9a, 0x06, 0xb3, 0x97, 0x2c, 0xa8, 0x1f, 0x76, 0xe5, 0x4e,
-  0xef, 0x64, 0xf4, 0x42, 0xe6, 0x02, 0x16, 0x55, 0x2d, 0x3f, 0x1f, 0x5b,
-  0x20, 0xbc, 0xb5, 0xce, 0x26, 0x0b, 0x4b, 0x79, 0x82, 0x7d, 0x37, 0x13,
-  0xc7, 0x1b, 0xbf, 0xc7, 0x7c, 0x5a, 0x06, 0xda, 0xf5, 0x36, 0x18, 0xd1,
-  0x57, 0x6b, 0x9f, 0x41, 0xe6, 0xea, 0x14, 0xa0, 0xad, 0xf2, 0xa1, 0x79,
-  0x2e, 0x9b, 0x05, 0xb4, 0x27, 0xa9, 0x90, 0xae, 0xba, 0x7a, 0x5b, 0x78,
-  0xc0, 0x5b, 0xae, 0xa6, 0xff, 0xb4, 0x03, 0x78, 0x5e, 0x83, 0x15, 0x48,
-  0xf7, 0x4f, 0x2a, 0xec, 0x46, 0xad, 0x53, 0x83, 0x81, 0xc8, 0x76, 0xbb,
-  0xc4, 0x39, 0x4d, 0xb3, 0x34, 0x7d, 0xb3, 0x76, 0x04, 0x07, 0x01, 0x0e,
-  0x34, 0x08, 0xb0, 0x79, 0x5a, 0xb0, 0x4a, 0xdc, 0x0f, 0x0c, 0x60, 0x16,
-  0x78, 0xb8, 0x2c, 0x21, 0x0a, 0x03, 0x94, 0x50, 0x6c, 0xb3, 0x89, 0xa0,
-  0xc0, 0xd9, 0x26, 0x41, 0x10, 0xc0, 0x52, 0xb6, 0xdd, 0xe3, 0x06, 0x9c,
-  0xdb, 0x5b, 0xf8, 0xbd, 0x6b, 0xd9, 0x0b, 0x94, 0xe7, 0xcf, 0xe2, 0xf7,
-  0xcc, 0x76, 0xd9, 0x49, 0xab, 0x89, 0xcf, 0x10, 0xbc, 0xfc, 0x3f, 0x36,
-  0x42, 0x43, 0x2b, 0x80, 0x35, 0xd9, 0xdf, 0xd3, 0x90, 0x1d, 0x7c, 0x7b,
-  0xfd, 0x96, 0x21, 0x4a, 0xd6, 0x62, 0x6d, 0x11, 0x8f, 0xb7, 0x70, 0xca,
-  0x81, 0x2c, 0x75, 0x4f, 0x6c, 0x48, 0x2c, 0x5c, 0xd5, 0xc1, 0x49, 0xea,
-  0x55, 0x84, 0xcc, 0xe6, 0xac, 0x58, 0x8a, 0xdf, 0x56, 0x00, 0x15, 0xb6,
-  0xbb, 0x2d, 0xe3, 0xd9, 0x2b, 0x2a, 0x12, 0x9f, 0xf2, 0x9a, 0x8b, 0xd6,
-  0x1a, 0x95, 0x00, 0x29, 0x5f, 0x54, 0xc0, 0xde, 0x11, 0xa0, 0x45, 0x48,
-  0xed, 0xcd, 0xca, 0xda, 0x87, 0x1c, 0x87, 0xad, 0xac, 0xba, 0xda, 0x34,
-  0x1f, 0x59, 0xae, 0xb6, 0x08, 0x60, 0x01, 0xc0, 0x01, 0x04, 0x34, 0x08,
-  0x70, 0x36, 0x44, 0x05, 0x62, 0x28, 0x18, 0x7a, 0x73, 0x8e, 0xe0, 0x16,
-  0x12, 0x88, 0x21, 0x64, 0xec, 0x65, 0x9a, 0xe0, 0x60, 0xd0, 0xd2, 0xc8,
-  0xcb, 0xc6, 0x51, 0xc5, 0xa2, 0xeb, 0x18, 0x43, 0x73, 0x88, 0x91, 0xb5,
-  0xa8, 0x25, 0x0f, 0x07, 0x46, 0x34, 0x4e, 0xf2, 0x5f, 0x7f, 0x96, 0x2c,
-  0x0b, 0xb5, 0x65, 0xdf, 0xf0, 0x89, 0xb6, 0xbd, 0xdf, 0xed, 0xd1, 0x00,
-  0x16, 0x02, 0xc9, 0xf7, 0x63, 0xe1, 0xc3, 0x94, 0x85, 0x77, 0x71, 0xf7,
-  0xe8, 0x04, 0x73, 0xcf, 0x76, 0x34, 0x95, 0xd5, 0x74, 0x90, 0xa2, 0xd5,
-  0x55, 0xc2, 0xa7, 0x19, 0x4a, 0x81, 0x3a, 0x71, 0xe5, 0xc7, 0xff, 0xfd,
-  0x46, 0x1a, 0xea, 0x4a, 0xdf, 0xdf, 0xf1, 0xe2, 0x27, 0xa1, 0xca, 0xd0,
-  0xc3, 0xe1, 0xfd, 0xf5, 0xf8, 0xc8, 0xd6, 0xb5, 0x4b, 0x76, 0xa4, 0xda,
-  0xe3, 0x4f, 0x7d, 0xb3, 0x53, 0xbf, 0xd2, 0x8d, 0x64, 0xc2, 0x00, 0x37,
-  0x84, 0x68, 0x15, 0x52, 0x1d, 0xc0, 0x6d, 0x72, 0xe8, 0x76, 0x14, 0xb1,
-  0xcd, 0x65, 0xa6, 0xc9, 0x60, 0xaa, 0xeb, 0x08, 0xdb, 0x30, 0x00, 0x70,
-  0x01, 0x0e, 0x34, 0x08, 0x90, 0x69, 0xb2, 0x0c, 0x84, 0x63, 0x59, 0x99,
-  0xdd, 0x4e, 0x5c, 0xea, 0x81, 0xc3, 0x05, 0x17, 0x46, 0x80, 0x04, 0xbb,
-  0x4a, 0xc4, 0x55, 0x5e, 0x89, 0x04, 0x42, 0x94, 0x08, 0x12, 0x2b, 0xb7,
-  0x29, 0x51, 0x59, 0x5b, 0xa5, 0x9c, 0x3d, 0x27, 0xf6, 0xee, 0x81, 0x9d,
-  0xb1, 0x9a, 0xce, 0x77, 0xf4, 0x15, 0x01, 0x9e, 0x2b, 0xc6, 0xab, 0x53,
-  0x89, 0xaa, 0x23, 0x6e, 0x3d, 0xa5, 0x45, 0xac, 0xce, 0xc8, 0x8a, 0x59,
-  0x68, 0xb7, 0x6f, 0xcd, 0xda, 0x80, 0x4f, 0x1f, 0xbe, 0xcd, 0x55, 0x08,
-  0x89, 0xad, 0x1d, 0x36, 0x74, 0x5c, 0x69, 0xe8, 0xe9, 0x96, 0xb2, 0x6a,
-  0xeb, 0x7e, 0x04, 0x80, 0x5a, 0x5e, 0xd1, 0x9b, 0x28, 0x05, 0x32, 0xab,
-  0x2b, 0x05, 0x68, 0x7c, 0x71, 0xb2, 0xd9, 0xcb, 0xf7, 0x55, 0xed, 0x70,
-  0x0c, 0x4a, 0xc2, 0x5c, 0x7f, 0xd7, 0xf9, 0xcf, 0x8a, 0x16, 0x28, 0x4e,
-  0xa4, 0x6f, 0xd7, 0x29, 0xa1, 0x1d, 0x5f, 0x99, 0x25, 0xac, 0x49, 0x4a,
-  0xa2, 0xa6, 0x3e, 0x39, 0x81, 0xbc, 0x1b, 0x40, 0xea, 0xcb, 0x52, 0x8a,
-  0x08, 0x76, 0x18, 0xd2, 0xeb, 0xfb, 0xbb, 0x6b, 0x44, 0xff, 0x7a, 0x02,
-  0xda, 0xb5, 0x93, 0x07, 0x01, 0x14, 0x34, 0x08, 0xb0, 0xa5, 0x80, 0xa5,
-  0x56, 0xc5, 0x15, 0xbe, 0xd3, 0x10, 0x80, 0x84, 0x20, 0x58, 0x09, 0x4e,
-  0x8c, 0xd1, 0xa0, 0x78, 0xd3, 0xa4, 0xca, 0x9b, 0xc7, 0xdf, 0x95, 0x35,
-  0x21, 0x8e, 0x38, 0x80, 0xb7, 0x2d, 0xa1, 0x05, 0x05, 0xec, 0x74, 0x2a,
-  0x9a, 0x2f, 0x6d, 0xf1, 0xe7, 0x02, 0xe2, 0xe9, 0xb5, 0x5b, 0x3d, 0x92,
-  0x2f, 0xa3, 0xfd, 0xfe, 0x32, 0x00, 0x16, 0x22, 0x2f, 0x58, 0xe8, 0xfb,
-  0x3b, 0x22, 0xea, 0x10, 0x9f, 0x8c, 0xc0, 0x27, 0x1a, 0xdd, 0xf5, 0x48,
-  0xab, 0xed, 0x9a, 0xc8, 0x17, 0xa5, 0x54, 0x73, 0xfe, 0x35, 0x37, 0xd5,
-  0xfa, 0x4d, 0x8f, 0xc7, 0xca, 0xea, 0xec, 0xc7, 0x9e, 0xaa, 0xd3, 0x15,
-  0x16, 0x01, 0xf5, 0x8d, 0x79, 0xfe, 0x5e, 0x9f, 0xc8, 0x29, 0xaf, 0x93,
-  0x5d, 0x61, 0x3f, 0xd3, 0x8f, 0xe3, 0xe3, 0x7e, 0x13, 0x92, 0x60, 0x4a,
-  0x5b, 0x66, 0x06, 0xf0, 0x7d, 0x02, 0xaa, 0x47, 0xbc, 0x62, 0xda, 0xf3,
-  0x43, 0x90, 0x92, 0xb2, 0x4d, 0xb7, 0x8a, 0x09, 0x5d, 0x28, 0xda, 0xcb,
-  0x40, 0x0e, 0x01, 0x06, 0x34, 0x1a, 0x48, 0x48, 0x0a, 0x2c, 0xcc, 0xc1,
-  0xeb, 0x07, 0xa1, 0xbd, 0x55, 0xad, 0x42, 0x02, 0xc1, 0x6d, 0x80, 0x22,
-  0x0b, 0xcd, 0x6e, 0xcd, 0x47, 0xaa, 0x54, 0x3f, 0xe1, 0x55, 0x35, 0xde,
-  0x74, 0xfa, 0x66, 0x63, 0x02, 0xa2, 0x7b, 0x2a, 0x8d, 0x72, 0x69, 0x15,
-  0x42, 0x41, 0x02, 0x86, 0xca, 0x89, 0x9d, 0xcb, 0x07, 0x28, 0xcf, 0xf0,
-  0x70, 0xc4, 0x34, 0x75, 0x7f, 0xe8, 0x2e, 0x78, 0x26, 0x5b, 0xb5, 0x72,
-  0x08, 0xfe, 0x79, 0xff, 0x97, 0xa6, 0x71, 0xfc, 0x0b, 0xa4, 0x83, 0x95,
-  0x6f, 0x85, 0xdc, 0x09, 0xc1, 0x49, 0xf5, 0x3b, 0xbe, 0x16, 0xdb, 0xd4,
-  0x6e, 0xfc, 0x68, 0xa5, 0x95, 0xad, 0x75, 0x4a, 0x93, 0x03, 0x36, 0x19,
-  0x53, 0xa6, 0xb9, 0x99, 0x95, 0x64, 0x8e, 0xd7, 0xf1, 0xe8, 0x94, 0x4e,
-  0xa1, 0x6e, 0xaa, 0x68, 0xe9, 0xb5, 0xe0, 0xc2, 0xa6, 0xbe, 0x8f, 0x59,
-  0xd2, 0x8e, 0x76, 0x9b, 0x83, 0xa2, 0xf6, 0x93, 0xda, 0xe1, 0xc8, 0x84,
-  0xcd, 0xa1, 0xa3, 0x38, 0x01, 0xbc, 0x1f, 0x40, 0xf2, 0x84, 0x80, 0xad,
-  0x28, 0x7a, 0x15, 0x39, 0xac, 0xf1, 0xf5, 0xa8, 0xb2, 0xcb, 0xee, 0xed,
-  0x44, 0xe9, 0x42, 0x03, 0x07, 0x01, 0x02, 0x34, 0x10, 0xd0, 0x30, 0x0c,
-  0x15, 0x0d, 0x02, 0x11, 0x31, 0x10, 0x84, 0x33, 0x23, 0xd6, 0x07, 0x82,
-  0xab, 0x02, 0xc0, 0x01, 0x60, 0x03, 0xb0, 0xa8, 0xf9, 0x17, 0x61, 0x89,
-  0xd0, 0x19, 0xc9, 0x2d, 0x31, 0x5d, 0x76, 0x02, 0x72, 0x93, 0x2c, 0xd0,
-  0x5c, 0xb4, 0x09, 0xbf, 0x3e, 0x84, 0x25, 0x3c, 0xc1, 0x9c, 0xe8, 0xb6,
-  0xd9, 0x65, 0x8c, 0xe7, 0x81, 0x7a, 0x79, 0x46, 0x08, 0x44, 0xbb, 0x3e,
-  0xc7, 0xdb, 0x7d, 0x42, 0x1b, 0x74, 0xa2, 0x9a, 0x43, 0xdf, 0xb6, 0x7f,
-  0x64, 0xc0, 0x28, 0x00, 0x17, 0xdb, 0x3f, 0xd3, 0x42, 0x10, 0x82, 0x39,
-  0xa0, 0xcb, 0x3a, 0xbe, 0x44, 0xd1, 0x1f, 0x76, 0xb2, 0x55, 0xf2, 0x8b,
-  0x23, 0x83, 0xbe, 0x36, 0x43, 0xcd, 0x59, 0x14, 0xa2, 0x16, 0x2e, 0x1a,
-  0x0c, 0xa0, 0x68, 0x2b, 0xcf, 0xbe, 0x01, 0x37, 0x10, 0x12, 0x15, 0x96,
-  0x13, 0x6e, 0xdd, 0xfe, 0xc3, 0x4c, 0x8a, 0x11, 0xc7, 0x3e, 0x19, 0xf4,
-  0x9d, 0xb9, 0xcb, 0x56, 0xd6, 0x70, 0x98, 0xc1, 0x2e, 0xc3, 0xb5, 0xd0,
-  0x03, 0x78, 0x4e, 0x81, 0x65, 0x21, 0xee, 0x3b, 0x8d, 0xb5, 0x81, 0xe8,
-  0x6b, 0xd4, 0x7f, 0xef, 0xf7, 0xd6, 0x96, 0x1b, 0x6d, 0x7b, 0x54, 0xcc,
-  0x00, 0x07, 0x01, 0x06, 0x34, 0x08, 0xb0, 0xa5, 0x88, 0xa1, 0xb7, 0x6a,
-  0x63, 0x91, 0xb0, 0x69, 0x81, 0x4d, 0x00, 0x46, 0x06, 0x4b, 0x17, 0x9e,
-  0x77, 0xed, 0x67, 0x31, 0x0d, 0xa8, 0x84, 0xa4, 0xa3, 0x39, 0x7f, 0x7c,
-  0x83, 0x94, 0x13, 0x20, 0xf4, 0x43, 0xbb, 0x7b, 0xe3, 0xd6, 0x3a, 0x22,
-  0x2f, 0x26, 0x7d, 0x7e, 0xad, 0x92, 0x5d, 0x5e, 0x2b, 0x2d, 0x73, 0xfa,
-  0x70, 0x9b, 0x8e, 0xdf, 0xfb, 0xff, 0xae, 0xc0, 0x09, 0x17, 0x74, 0x5c,
-  0xe7, 0x8f, 0xf8, 0xbd, 0xec, 0x13, 0x7c, 0xbb, 0x20, 0x17, 0x73, 0x53,
-  0xd1, 0x0d, 0xd4, 0x63, 0xb3, 0x87, 0xc3, 0x50, 0x42, 0x35, 0x28, 0xbe,
-  0xde, 0x9e, 0x09, 0x44, 0x64, 0x08, 0xee, 0x4a, 0xd8, 0x6f, 0x4b, 0xb8,
-  0xc5, 0x45, 0xfd, 0xd3, 0x90, 0x0e, 0xd7, 0xd0, 0xe3, 0x8a, 0x5e, 0x28,
-  0x96, 0x4e, 0xc2, 0x91, 0xed, 0xfc, 0x3f, 0x12, 0x72, 0x00, 0x4b, 0xb8,
-  0x06, 0xf0, 0xdd, 0xea, 0x40, 0x02, 0x2a, 0x47, 0xb8, 0x95, 0xab, 0x81,
-  0xc7, 0xc0, 0x2c, 0xc8, 0x92, 0xc9, 0x72, 0xcb, 0x61, 0x62, 0xca, 0xfe,
-  0x37, 0x0f, 0x57, 0xa2, 0x60, 0x0e, 0x01, 0x0a, 0x34, 0x19, 0x73, 0x53,
-  0x40, 0xde, 0xd9, 0x9b, 0xac, 0x00, 0x80, 0x52, 0xcb, 0x03, 0x5c, 0x81,
-  0x37, 0x04, 0x13, 0xd6, 0x9b, 0xf4, 0xfa, 0x2b, 0xdb, 0x33, 0x15, 0x91,
-  0x61, 0x25, 0xfc, 0x67, 0x9b, 0x75, 0x1b, 0x77, 0x69, 0x69, 0x5b, 0xb2,
-  0xf2, 0x5e, 0xf9, 0xa3, 0x77, 0x08, 0x33, 0x74, 0x38, 0x02, 0xee, 0xc4,
-  0x4e, 0x85, 0x0c, 0xe3, 0x76, 0x1e, 0xc3, 0x6a, 0x8b, 0x62, 0xa2, 0x0c,
-  0xc1, 0x1a, 0x5e, 0x76, 0xae, 0x0b, 0x64, 0x6b, 0xb2, 0x14, 0x18, 0x5d,
-  0x6a, 0x22, 0xca, 0x60, 0xc8, 0x54, 0x12, 0x6b, 0xc5, 0x22, 0x4a, 0x8a,
-  0xa2, 0x5d, 0x23, 0xee, 0xf4, 0x3a, 0x7c, 0x75, 0xb4, 0x65, 0xef, 0xb4,
-  0x6f, 0xa8, 0x8f, 0x23, 0x75, 0xa4, 0x8a, 0x82, 0x78, 0x29, 0xda, 0xf0,
-  0x60, 0xc3, 0xf7, 0xf7, 0x28, 0xad, 0x8b, 0xa9, 0xc7, 0xd8, 0x25, 0x82,
-  0xf3, 0xd4, 0xd7, 0xcd, 0xd3, 0xb1, 0xd3, 0x32, 0xb5, 0x2b, 0x3d, 0xa0,
-  0x1b, 0xc2, 0x34, 0x0a, 0xa9, 0x1e, 0xe3, 0x17, 0x2b, 0x60, 0x76, 0x1b,
-  0xb4, 0xf7, 0xb2, 0xf6, 0x13, 0x6c, 0xfb, 0x2d, 0xf3, 0x2d, 0xa8, 0xc0,
-  0x38, 0x01, 0x04, 0x34, 0x19, 0xb0, 0x25, 0x33, 0x0d, 0x52, 0x27, 0x3b,
-  0x3c, 0x65, 0x54, 0x76, 0x50, 0x68, 0x02, 0xa1, 0x08, 0x40, 0x68, 0x35,
-  0x80, 0x4b, 0xbd, 0x9e, 0x8f, 0x2a, 0xd2, 0xe4, 0xf2, 0x4d, 0x95, 0xd2,
-  0xba, 0x7f, 0x54, 0x01, 0x81, 0xaa, 0x6d, 0x05, 0x12, 0x7f, 0x7f, 0x44,
-  0x9b, 0x2a, 0x95, 0x3a, 0xf0, 0x95, 0x24, 0x60, 0xd2, 0x66, 0xf2, 0xb8,
-  0x11, 0x0c, 0xd9, 0xfa, 0xea, 0x62, 0x54, 0x28, 0x55, 0x89, 0x41, 0x68,
-  0xe3, 0xe5, 0xfa, 0x79, 0x48, 0x5a, 0xf9, 0xf2, 0xed, 0x99, 0x05, 0xaf,
-  0x57, 0x35, 0x1b, 0x8e, 0xfc, 0x42, 0x09, 0x04, 0xbb, 0x71, 0x1c, 0x88,
-  0x00, 0x9d, 0x5e, 0xea, 0xd0, 0xae, 0x79, 0x71, 0x53, 0xf9, 0xe8, 0xbc,
-  0x8f, 0xb7, 0x6f, 0x3c, 0x56, 0x2e, 0xb2, 0x5f, 0x2d, 0x51, 0x79, 0xb7,
-  0x08, 0x21, 0x8b, 0x80, 0xa6, 0x49, 0xcb, 0xf0, 0x9e, 0x47, 0x07, 0x3a,
-  0x81, 0xc7, 0xdb, 0x20, 0x37, 0x83, 0x68, 0x1d, 0x51, 0x41, 0xb0, 0x10,
-  0x3b, 0x0f, 0x7e, 0xb2, 0x9d, 0x8b, 0x9a, 0xab, 0xdc, 0xbb, 0x2b, 0xc9,
-  0x81, 0x80, 0x70, 0x01, 0x0e, 0x34, 0x08, 0x94, 0x88, 0x1a, 0x15, 0x5c,
-  0x66, 0x30, 0x3b, 0x1e, 0x83, 0x41, 0xb2, 0x05, 0x5a, 0x37, 0xa9, 0x33,
-  0x06, 0x11, 0xa6, 0x21, 0x00, 0xd3, 0x9a, 0x29, 0x49, 0xc5, 0x39, 0x0e,
-  0x80, 0x9a, 0xb4, 0x47, 0x6e, 0xc4, 0x02, 0x03, 0x29, 0x50, 0x88, 0x23,
-  0x97, 0x2b, 0xd1, 0x78, 0xfd, 0x39, 0x00, 0xbf, 0x96, 0x8f, 0x1c, 0xa4,
-  0x10, 0x1d, 0xfe, 0x11, 0x25, 0xbf, 0x57, 0xaf, 0x44, 0x51, 0x2c, 0xa5,
-  0x12, 0x9a, 0xc6, 0x9f, 0x2f, 0xaf, 0xf7, 0xeb, 0x77, 0x48, 0xa8, 0xe7,
-  0xc3, 0xa6, 0xf7, 0x9a, 0xa6, 0x66, 0x5d, 0xdd, 0xf6, 0x54, 0xaf, 0xa6,
-  0xe8, 0x54, 0x2a, 0x13, 0x17, 0xd5, 0x0c, 0x44, 0x85, 0x8b, 0x46, 0xd0,
-  0x4d, 0xd4, 0xa4, 0x26, 0xaf, 0x35, 0x1b, 0x9d, 0x07, 0x54, 0x24, 0xa1,
-  0x47, 0x45, 0xed, 0xa1, 0xa6, 0x88, 0x2d, 0x6d, 0xe5, 0x38, 0x79, 0xbf,
-  0x0f, 0xaa, 0xdc, 0x84, 0xc4, 0x01, 0x55, 0x80, 0x6f, 0x0d, 0xd0, 0x62,
-  0xa9, 0x1b, 0xb7, 0x95, 0x4c, 0x4e, 0x98, 0x75, 0x63, 0x81, 0xe8, 0x46,
-  0x77, 0x22, 0x6c, 0xab, 0x8d, 0xc5, 0x3e, 0x1a, 0x4b, 0xca, 0xcb, 0x68,
-  0x00, 0xe0, 0x01, 0x0e, 0x34, 0x14, 0x90, 0x60, 0x0c, 0x0e, 0x58, 0xc1,
-  0x42, 0x11, 0x0c, 0xa2, 0x87, 0xce, 0x28, 0x19, 0x56, 0x10, 0x42, 0x0b,
-  0x00, 0xa4, 0xf3, 0xde, 0x88, 0x8d, 0xbf, 0x24, 0xbf, 0xc4, 0x25, 0x96,
-  0xb2, 0x54, 0x1f, 0x53, 0x34, 0xcb, 0x52, 0x86, 0xcd, 0x98, 0xde, 0xe9,
-  0x3e, 0xf7, 0xd6, 0x6c, 0x0c, 0xb5, 0x38, 0x98, 0x56, 0x16, 0xbc, 0xe5,
-  0x17, 0x64, 0x1d, 0x1e, 0xa0, 0x65, 0x87, 0x03, 0xe2, 0xd5, 0x48, 0x67,
-  0x22, 0x69, 0x11, 0x28, 0xcd, 0x5c, 0x59, 0xb1, 0xd0, 0xa8, 0x30, 0x24,
-  0x66, 0xd4, 0x8f, 0x84, 0x1e, 0xde, 0x26, 0x65, 0xb7, 0xe5, 0x85, 0x4d,
-  0x5a, 0x45, 0xd9, 0x4d, 0xfb, 0xed, 0x87, 0x53, 0xc6, 0x79, 0xdc, 0x38,
-  0x2e, 0x17, 0x74, 0xc6, 0x9a, 0xb9, 0xf2, 0x4f, 0x18, 0xa3, 0x6a, 0xc3,
-  0x01, 0xf6, 0x32, 0xd2, 0x09, 0xef, 0x3c, 0x2c, 0x8e, 0xdb, 0xfd, 0x1e,
-  0x15, 0x16, 0x3d, 0x5b, 0x4a, 0x96, 0xe3, 0x9e, 0x7e, 0x1b, 0x71, 0xf6,
-  0x3b, 0xd4, 0xac, 0x52, 0x53, 0x8b, 0x5f, 0x4b, 0x37, 0x4c, 0x03, 0x78,
-  0x3e, 0x81, 0x15, 0x25, 0x2b, 0xa1, 0x73, 0x10, 0xe3, 0x90, 0xb2, 0x92,
-  0x2e, 0xe4, 0x5b, 0x0a, 0x55, 0x09, 0xee, 0x98, 0xc0, 0x07, 0x01, 0x0a,
-  0x34, 0x1c, 0xc8, 0x58, 0x23, 0x15, 0xce, 0x83, 0x35, 0x0c, 0x76, 0xac,
-  0xdb, 0x60, 0x00, 0x20, 0x5b, 0x7a, 0x00, 0x4e, 0x58, 0xb5, 0xeb, 0xa7,
-  0xeb, 0xe9, 0x73, 0x0c, 0xd6, 0x41, 0x61, 0x6e, 0x9f, 0xc6, 0xf0, 0x4e,
-  0xe0, 0xb8, 0x3e, 0x3a, 0xa2, 0xd8, 0xd6, 0x84, 0x26, 0x68, 0x77, 0xf7,
-  0xc8, 0x7d, 0xa9, 0x71, 0x9b, 0x9d, 0x24, 0xe2, 0xd4, 0x39, 0x1c, 0x94,
-  0x29, 0xad, 0x3d, 0xe6, 0x31, 0x09, 0x25, 0x21, 0x08, 0x18, 0x72, 0x7c,
-  0x1d, 0x0c, 0xc1, 0x17, 0xd5, 0xaf, 0xc8, 0xe1, 0x98, 0x7b, 0x67, 0x75,
-  0xe9, 0x7d, 0x56, 0xda, 0xab, 0x57, 0xe2, 0xfe, 0xb2, 0x68, 0x37, 0x59,
-  0xbf, 0xab, 0xae, 0x55, 0x96, 0xe1, 0xef, 0xc6, 0x38, 0x46, 0xa8, 0x06,
-  0x6b, 0x6c, 0x41, 0x6d, 0x1b, 0x92, 0x00, 0xbe, 0xda, 0x7c, 0xbe, 0x64,
-  0x52, 0x00, 0xad, 0xe9, 0xb2, 0x03, 0x78, 0x56, 0x83, 0x55, 0x49, 0x2f,
-  0x17, 0x4c, 0xf0, 0x6d, 0xb0, 0x70, 0x39, 0x0f, 0xaf, 0x2b, 0xef, 0xb2,
-  0x12, 0xc5, 0xa2, 0xb2, 0x36, 0x50, 0x00, 0x07, 0x01, 0x12, 0x34, 0x14,
-  0xac, 0x9a, 0x0a, 0xcc, 0x4c, 0x56, 0x31, 0x85, 0x72, 0x02, 0x50, 0x8c,
-  0xd0, 0x5b, 0x71, 0xae, 0xec, 0x2d, 0xaa, 0x25, 0xdf, 0xb6, 0xcb, 0xfa,
-  0xee, 0x89, 0x68, 0xd1, 0x5e, 0x98, 0x5b, 0xb4, 0xca, 0x4c, 0x29, 0x97,
-  0x98, 0xc0, 0x5e, 0x8f, 0xa8, 0x93, 0x5c, 0xe5, 0xc7, 0x49, 0xdf, 0xfd,
-  0xf6, 0x30, 0xbb, 0x98, 0x9d, 0x0c, 0xd7, 0xd1, 0xb3, 0x6e, 0x3f, 0x7f,
-  0xf6, 0x9e, 0x42, 0xd0, 0x10, 0x44, 0xa5, 0x13, 0xeb, 0xed, 0xa8, 0x2c,
-  0x8e, 0x8e, 0x1e, 0x88, 0x21, 0xa9, 0x9d, 0xcb, 0x84, 0xa9, 0x0e, 0xff,
-  0x9e, 0xb2, 0xc3, 0x53, 0x03, 0x17, 0xd4, 0xef, 0xb9, 0x02, 0x84, 0xf1,
-  0xe7, 0xa3, 0x1b, 0xac, 0x5c, 0x08, 0xa2, 0xf9, 0xee, 0x74, 0x58, 0xe1,
-  0x84, 0x01, 0xae, 0x38, 0xcc, 0xf3, 0x03, 0xc1, 0x35, 0xf8, 0xfe, 0xec,
-  0x9e, 0xc0, 0x88, 0x17, 0x4e, 0x19, 0xa0, 0x06, 0xf0, 0xbd, 0x06, 0x6a,
-  0x93, 0xbb, 0x2a, 0xa8, 0x4f, 0xb9, 0xa6, 0xee, 0x00, 0x72, 0x1f, 0x6e,
-  0xdf, 0x7d, 0xb3, 0x63, 0x61, 0x33, 0x53, 0xa8, 0x53, 0x00, 0x0e, 0x01,
-  0x0c, 0x34, 0x04, 0x6c, 0xb9, 0x91, 0x0c, 0xca, 0xc1, 0xe1, 0x95, 0xe2,
-  0xa7, 0x5b, 0x06, 0xb6, 0x16, 0x05, 0x80, 0x14, 0x94, 0xe9, 0xa3, 0xbb,
-  0x42, 0x6b, 0x3a, 0xba, 0xaa, 0xc3, 0xad, 0x95, 0x8a, 0x6b, 0x34, 0x82,
-  0xbd, 0x60, 0x1e, 0x7a, 0x51, 0xf7, 0x7f, 0x8e, 0x68, 0x65, 0xad, 0x95,
-  0x55, 0xc6, 0x75, 0xd0, 0xca, 0x65, 0x75, 0x2b, 0xa4, 0x61, 0xce, 0xe0,
-  0x33, 0xa5, 0xea, 0x78, 0x5d, 0x1e, 0x2c, 0xa9, 0x13, 0x19, 0x82, 0x11,
-  0x71, 0x8e, 0x5a, 0x9e, 0xb1, 0x85, 0x01, 0x3c, 0x9e, 0x87, 0x6e, 0x94,
-  0x42, 0x17, 0x26, 0xa6, 0xa5, 0x4d, 0x5e, 0xdb, 0xd7, 0xd1, 0xdb, 0xb2,
-  0x96, 0x82, 0xa9, 0x1a, 0x3c, 0x7d, 0x2d, 0x19, 0x82, 0xc5, 0xd1, 0xfc,
-  0xaf, 0x82, 0x07, 0xf9, 0x30, 0x0e, 0x9c, 0x7c, 0x68, 0xd3, 0x8b, 0x1b,
-  0xd4, 0x46, 0x4a, 0x63, 0x5f, 0x6b, 0x8f, 0xe2, 0x3a, 0xae, 0x6a, 0x02,
-  0x79, 0x28, 0x25, 0xa5, 0xc3, 0xad, 0xb8, 0xa2, 0xdb, 0x74, 0x22, 0x0a,
-  0xe5, 0xe8, 0x5b, 0x00, 0x6f, 0x09, 0xd0, 0x22, 0xa4, 0xb6, 0xbe, 0x83,
-  0x78, 0xc3, 0x8e, 0xc2, 0x8b, 0x12, 0x1a, 0xce, 0xb1, 0x44, 0x6f, 0xad,
-  0xec, 0xf6, 0x0c, 0x00, 0x00, 0xe0, 0x01, 0x0e, 0x34, 0x04, 0x70, 0x49,
-  0xb2, 0x11, 0x42, 0x81, 0x12, 0xb0, 0x29, 0x8f, 0x70, 0x10, 0xee, 0xd6,
-  0xcb, 0x59, 0x0c, 0x58, 0x1b, 0x35, 0x05, 0x26, 0x05, 0xd7, 0x0e, 0x86,
-  0xec, 0xf1, 0x98, 0x3a, 0xdb, 0x87, 0xda, 0x33, 0x5e, 0x9c, 0x74, 0x35,
-  0x8e, 0x51, 0xa3, 0xf8, 0xff, 0x91, 0x88, 0xc3, 0x26, 0x75, 0x37, 0xd0,
-  0x72, 0xe9, 0x92, 0x51, 0x59, 0x46, 0x54, 0x9c, 0xf1, 0x80, 0xad, 0x6e,
-  0xbf, 0x69, 0x69, 0x2c, 0x5a, 0xe6, 0x21, 0x3b, 0xa7, 0xc7, 0xf4, 0x98,
-  0x0a, 0x1a, 0xf9, 0xd6, 0x85, 0x50, 0x21, 0xaf, 0xa5, 0x76, 0x56, 0xbb,
-  0xc3, 0x63, 0x47, 0x1a, 0x55, 0x46, 0x72, 0x3d, 0x83, 0xd7, 0x12, 0xac,
-  0x8c, 0xce, 0x1b, 0x95, 0x93, 0x34, 0x98, 0x26, 0xf3, 0xbf, 0x19, 0xc4,
-  0x25, 0x53, 0x26, 0x12, 0x21, 0x1e, 0x41, 0x60, 0x4b, 0x14, 0xa5, 0x2c,
-  0xf0, 0x80, 0x73, 0xed, 0xf7, 0x4e, 0xb4, 0xaf, 0xbf, 0xc1, 0x43, 0x17,
-  0xef, 0x50, 0xea, 0xd7, 0xa7, 0xf9, 0x5a, 0x20, 0x6f, 0x0c, 0xd0, 0x62,
-  0xa9, 0x4e, 0x6e, 0x4e, 0x84, 0xea, 0xf3, 0x57, 0x77, 0x8e, 0x07, 0x21,
-  0xa7, 0x75, 0xba, 0xf3, 0xb6, 0x4c, 0x66, 0xc6, 0xe5, 0x5b, 0x10, 0x00,
-  0xe0, 0x01, 0x0e, 0x34, 0x10, 0x94, 0xb3, 0x34, 0x09, 0x48, 0x86, 0x13,
-  0x83, 0xc1, 0x55, 0xbc, 0x30, 0x58, 0x13, 0x12, 0xc1, 0x05, 0x0c, 0x52,
-  0x57, 0xed, 0x5f, 0x9b, 0x90, 0x64, 0x53, 0x0b, 0x18, 0x46, 0xa0, 0xac,
-  0x83, 0xc6, 0x4d, 0xce, 0x89, 0x0c, 0xe4, 0x10, 0x3c, 0x22, 0x08, 0x72,
-  0x93, 0x16, 0x39, 0xfa, 0xb0, 0x4b, 0xe2, 0xa7, 0x40, 0x9c, 0x5b, 0x54,
-  0x23, 0x10, 0xb5, 0x9c, 0x26, 0xd9, 0xeb, 0x8a, 0x50, 0x17, 0x56, 0x81,
-  0x35, 0x55, 0x38, 0xf5, 0x7e, 0xff, 0x14, 0x31, 0x07, 0xbf, 0x0a, 0x79,
-  0xf2, 0xaa, 0xfe, 0xd2, 0x33, 0x88, 0xe5, 0xd2, 0xd4, 0xb1, 0xc9, 0xcc,
-  0x0d, 0xfc, 0xf7, 0xf1, 0x28, 0xde, 0x5b, 0xbe, 0x25, 0x75, 0x12, 0xdc,
-  0x79, 0x12, 0xd8, 0xb0, 0x64, 0x56, 0x70, 0xc7, 0x9b, 0x9d, 0xd8, 0x13,
-  0x1d, 0xfb, 0xe7, 0xe5, 0xfe, 0x75, 0x5f, 0x29, 0xd2, 0x95, 0xc3, 0xc1,
-  0x4e, 0xdf, 0xea, 0xdb, 0xbf, 0x6b, 0xd4, 0x11, 0x05, 0xad, 0x3a, 0x81,
-  0xbc, 0x23, 0x41, 0xfa, 0xa6, 0xc5, 0x8c, 0x3a, 0x2e, 0xaa, 0x01, 0xc8,
-  0x7c, 0x1d, 0xd3, 0xb7, 0x98, 0xc1, 0xd4, 0xd5, 0xad, 0x90, 0x58, 0x03,
-  0x07, 0x01, 0x10, 0x34, 0x08, 0xac, 0x8a, 0x13, 0xa1, 0x4c, 0x84, 0x13,
-  0x8d, 0xed, 0x43, 0x7c, 0x81, 0x01, 0x0c, 0xd1, 0x02, 0x00, 0xbf, 0x21,
-  0x4e, 0xdf, 0x1d, 0x3a, 0x7b, 0x29, 0xdb, 0xea, 0xa8, 0xf7, 0xb7, 0x12,
-  0xfd, 0x9d, 0x61, 0x02, 0x07, 0x2d, 0xd8, 0x83, 0x6e, 0xf0, 0xe1, 0x56,
-  0x6b, 0x64, 0xc3, 0x53, 0xa9, 0xcd, 0x77, 0x08, 0x98, 0x9a, 0x94, 0xed,
-  0xd9, 0xb2, 0xe4, 0xd4, 0xdf, 0xde, 0x6b, 0xe7, 0x4d, 0x45, 0xce, 0x2b,
-  0x5b, 0x09, 0xa5, 0x47, 0xc7, 0xd3, 0x9d, 0xe0, 0x0c, 0x6b, 0xb3, 0xc3,
-  0x15, 0x34, 0x8a, 0x9b, 0xe8, 0xf7, 0xf6, 0x61, 0x6f, 0x42, 0xe2, 0x4d,
-  0xda, 0xf8, 0xb2, 0x63, 0xf9, 0x9d, 0x31, 0x23, 0x52, 0xbc, 0xb7, 0xef,
-  0x16, 0x11, 0x93, 0x22, 0x16, 0xb7, 0x2f, 0xf8, 0xc3, 0x49, 0x57, 0x26,
-  0x34, 0xe4, 0x09, 0x6d, 0xe1, 0x38, 0xfd, 0x35, 0x16, 0xe3, 0x41, 0xef,
-  0x68, 0xc5, 0x86, 0xf4, 0xc9, 0x8e, 0x98, 0x98, 0x04, 0x02, 0x5e, 0xff,
-  0xa4, 0x40, 0xde, 0x19, 0xa0, 0xcd, 0x52, 0xb7, 0x5b, 0x92, 0xe1, 0x39,
-  0x62, 0xab, 0x00, 0xf4, 0x33, 0xbd, 0xed, 0xdf, 0x97, 0x76, 0xa8, 0x1f,
-  0x2d, 0xa7, 0x3f, 0x5a, 0x23, 0x41, 0xc0, 0x01, 0x12, 0x34, 0x08, 0x8c,
-  0x98, 0x2a, 0x11, 0x86, 0x85, 0x61, 0x0a, 0x1b, 0x29, 0xbc, 0x8e, 0xc0,
-  0x0c, 0xb4, 0xac, 0xb4, 0x4a, 0x42, 0x61, 0x6b, 0x15, 0x80, 0x82, 0x5a,
-  0x4f, 0x92, 0x8b, 0xe6, 0xf7, 0xea, 0x05, 0xb0, 0x4c, 0x6c, 0x40, 0x7f,
-  0xed, 0x80, 0x8d, 0x20, 0x99, 0x6a, 0x82, 0x0d, 0x85, 0x54, 0x04, 0xb2,
-  0xe4, 0x94, 0xe7, 0x1b, 0x04, 0x72, 0xd7, 0x95, 0x79, 0xa8, 0xe5, 0xc6,
-  0x07, 0x9e, 0x56, 0x3e, 0x32, 0xf3, 0x2b, 0x33, 0x96, 0x55, 0xa9, 0x4f,
-  0x31, 0xbb, 0xcb, 0x36, 0xd2, 0x91, 0xfe, 0xf4, 0x4f, 0x27, 0xce, 0xb7,
-  0x29, 0xdf, 0x67, 0x77, 0xb7, 0x8a, 0xad, 0xf1, 0xb3, 0x53, 0x05, 0x80,
-  0xa0, 0x1c, 0xaf, 0x77, 0x7e, 0x41, 0x2e, 0x52, 0x4b, 0x79, 0x8a, 0x88,
-  0x82, 0x9f, 0x93, 0x2e, 0x08, 0xaf, 0xba, 0x3f, 0x68, 0x74, 0xd4, 0x37,
-  0x4b, 0x8f, 0xf2, 0xdc, 0xa0, 0x64, 0xe1, 0x5e, 0xb2, 0x8d, 0xfe, 0x5d,
-  0x45, 0xa5, 0x0a, 0x32, 0xd2, 0xb1, 0xa5, 0x71, 0x16, 0x00, 0xaa, 0xcc,
-  0xb3, 0x03, 0x78, 0x4e, 0x83, 0xf5, 0x4a, 0x67, 0x8d, 0x8b, 0x1a, 0x54,
-  0x03, 0xb0, 0xf6, 0xfd, 0x7c, 0xd2, 0x3b, 0x35, 0x19, 0xfa, 0xbb, 0xec,
-  0xe5, 0x24, 0x07, 0x01, 0x0e, 0x34, 0x1c, 0xac, 0xd2, 0x1b, 0x0a, 0x46,
-  0x67, 0x70, 0x80, 0x5c, 0x26, 0x25, 0x08, 0x04, 0x1e, 0xfb, 0x37, 0x58,
-  0xa0, 0xb1, 0x84, 0xe6, 0x2c, 0x5b, 0x7c, 0x60, 0x27, 0x05, 0xf2, 0xb3,
-  0xd1, 0xfe, 0xd9, 0xb5, 0x97, 0x84, 0xa3, 0x4b, 0xad, 0x70, 0xf7, 0xd0,
-  0x4a, 0x48, 0x24, 0xf9, 0x80, 0x5d, 0xcf, 0xf0, 0x6e, 0x6e, 0x11, 0x02,
-  0x25, 0xe0, 0x21, 0x5c, 0x4c, 0x4a, 0xfb, 0xa3, 0x38, 0x11, 0x34, 0x3f,
-  0xbf, 0xad, 0x90, 0xcf, 0x18, 0x96, 0xfe, 0x4f, 0x75, 0x7a, 0xb7, 0xff,
-  0xbf, 0xd4, 0x34, 0xac, 0x96, 0xd0, 0x85, 0xa2, 0x08, 0x34, 0x4f, 0xa0,
-  0xf8, 0xd1, 0x74, 0xae, 0x93, 0xf7, 0x2d, 0x40, 0x54, 0xa5, 0xd7, 0xd6,
-  0x7e, 0x27, 0xc0, 0xfb, 0x3d, 0x49, 0xb0, 0x00, 0x90, 0x27, 0x77, 0x51,
-  0xe0, 0x7c, 0x5a, 0x00, 0x02, 0x45, 0x9b, 0x24, 0xba, 0x3a, 0x3d, 0xbc,
-  0x30, 0x00, 0x0d, 0xe1, 0xfa, 0x0c, 0xd5, 0x24, 0x92, 0xa4, 0x65, 0x5b,
-  0x55, 0x68, 0x07, 0xc0, 0xa1, 0x63, 0x73, 0x2d, 0x9b, 0xde, 0xfb, 0x6d,
-  0x97, 0x98, 0xed, 0xda, 0x91, 0xf7, 0x3d, 0xe6, 0x00, 0x1c, 0x01, 0x0e,
-  0x34, 0x1a, 0x46, 0x68, 0x0b, 0x84, 0x46, 0x81, 0x11, 0x20, 0x80, 0x2c,
-  0x13, 0x20, 0x7a, 0xae, 0x6a, 0x63, 0x6a, 0x06, 0xb3, 0x62, 0x8e, 0x00,
-  0x40, 0x01, 0x82, 0x89, 0x35, 0x97, 0x2d, 0xd5, 0xa2, 0x82, 0x2d, 0x69,
-  0x15, 0x94, 0x7e, 0x8c, 0x9c, 0x06, 0x1b, 0xa8, 0x5b, 0x2f, 0x3c, 0x80,
-  0x8c, 0x90, 0x51, 0x44, 0x87, 0xda, 0x28, 0x60, 0x46, 0x9b, 0x49, 0x3f,
-  0xde, 0xde, 0xda, 0xfd, 0xa0, 0x69, 0xf8, 0xae, 0x6e, 0x3e, 0xd7, 0xaa,
-  0x9c, 0x68, 0x5f, 0xee, 0x15, 0xdc, 0x12, 0xf8, 0xc7, 0xa7, 0xee, 0xa4,
-  0xf8, 0xdd, 0x18, 0x1c, 0x3c, 0xfa, 0x40, 0xe9, 0xe9, 0x73, 0x91, 0x05,
-  0x92, 0xf1, 0x2f, 0xeb, 0x5a, 0x2e, 0xe3, 0xbb, 0x84, 0x00, 0x2a, 0x94,
-  0x21, 0x22, 0xa3, 0xde, 0x9d, 0xd2, 0x6b, 0x6f, 0x68, 0x20, 0x2e, 0xdc,
-  0x7a, 0x47, 0xc1, 0xea, 0xf9, 0x3e, 0x99, 0xbd, 0xfc, 0xae, 0x95, 0xb5,
-  0xc5, 0xd6, 0x80, 0x5c, 0xd4, 0x03, 0x78, 0x76, 0x81, 0x1a, 0x2c, 0x70,
-  0xed, 0x25, 0xe1, 0x0e, 0x3e, 0x05, 0x72, 0x45, 0xf4, 0x5a, 0x9c, 0x7f,
-  0xdf, 0xe3, 0xc8, 0x4b, 0xea, 0xeb, 0x56, 0x31, 0xaf, 0xb7, 0x88, 0x07,
-  0x01, 0x10, 0x34, 0x19, 0xe8, 0x75, 0x41, 0x95, 0x46, 0x22, 0x29, 0xe3,
-  0xbd, 0x28, 0xd9, 0x59, 0x4e, 0x19, 0xc8, 0x42, 0xaf, 0x58, 0xa4, 0x50,
-  0x03, 0x19, 0x80, 0xb6, 0x42, 0x43, 0x2e, 0x15, 0x12, 0xad, 0xef, 0xf8,
-  0x00, 0x3d, 0x95, 0x31, 0x39, 0x95, 0x7e, 0xd0, 0xad, 0x70, 0x60, 0xa9,
-  0x2e, 0xe1, 0x1f, 0x1d, 0xa0, 0x04, 0x50, 0xcd, 0xd5, 0xac, 0x5b, 0xba,
-  0xfd, 0x7f, 0x75, 0x44, 0x6e, 0xff, 0x5d, 0x65, 0x9c, 0x72, 0x8b, 0x95,
-  0x0a, 0xd4, 0x72, 0x80, 0x11, 0x3b, 0xb0, 0x0b, 0xbd, 0xf7, 0xfe, 0xce,
-  0xba, 0x98, 0x34, 0xa5, 0x48, 0xf2, 0xe0, 0x23, 0x3f, 0xcb, 0xe8, 0xb5,
-  0x69, 0x5a, 0x56, 0xb8, 0x02, 0x8a, 0x66, 0xf6, 0xf5, 0x69, 0x4e, 0xfa,
-  0xe9, 0x94, 0xd2, 0xad, 0xa4, 0x5a, 0x1b, 0x93, 0x62, 0xd9, 0x96, 0x73,
-  0x18, 0xdf, 0x0a, 0x9d, 0xed, 0x18, 0xb9, 0x00, 0x02, 0x54, 0x99, 0x70,
-  0x1b, 0xc4, 0x34, 0x18, 0xb4, 0x5c, 0xa5, 0xba, 0xd1, 0x6e, 0x63, 0x77,
-  0x31, 0xc0, 0xf8, 0x0c, 0x37, 0x74, 0x92, 0x11, 0x45, 0x49, 0x54, 0xb5,
-  0xac, 0x76, 0xf9, 0x43, 0x46, 0x32, 0x68, 0x00, 0x38, 0x01, 0x08, 0x34,
-  0x19, 0xea, 0x86, 0x0a, 0x98, 0x48, 0xa1, 0x60, 0xa0, 0x84, 0x41, 0xfc,
-  0x46, 0x0e, 0x58, 0x1d, 0x3b, 0x56, 0x2c, 0x0b, 0x15, 0x6a, 0x02, 0x07,
-  0x4c, 0xd0, 0x1f, 0xfc, 0x80, 0x82, 0x79, 0x51, 0xa0, 0x68, 0x59, 0xae,
-  0x2f, 0xca, 0x03, 0x23, 0xb0, 0x93, 0x2f, 0x5f, 0xbd, 0xc4, 0x22, 0x7e,
-  0xca, 0xaa, 0x96, 0xb4, 0xc6, 0x8c, 0x8a, 0x71, 0x90, 0x7c, 0xfa, 0x2f,
-  0x3d, 0x9e, 0x3d, 0x2d, 0xd0, 0xf0, 0xc6, 0xae, 0xfb, 0x26, 0x8b, 0x4e,
-  0xbb, 0x0b, 0xab, 0xa5, 0xb5, 0x40, 0xa5, 0x97, 0x7d, 0x5e, 0xbe, 0x3f,
-  0x5f, 0x21, 0x03, 0xce, 0x4e, 0x5c, 0x04, 0x3e, 0x3a, 0xbf, 0x81, 0xf2,
-  0xf6, 0x64, 0x78, 0xec, 0xb8, 0x58, 0xa2, 0x1b, 0xe9, 0x9e, 0xaf, 0xa3,
-  0x5b, 0xe3, 0x34, 0x38, 0x2d, 0x99, 0xd8, 0x9a, 0x30, 0x00, 0x04, 0x77,
-  0x8d, 0x40, 0x06, 0x2b, 0xdd, 0x5f, 0x05, 0x84, 0x88, 0xee, 0x04, 0xaa,
-  0xc1, 0x0f, 0x62, 0x17, 0x52, 0x1c, 0x4c, 0x99, 0x60, 0x00, 0xde, 0x15,
-  0xa0, 0xfd, 0x50, 0xdb, 0x40, 0x0b, 0xbb, 0xae, 0xe4, 0x69, 0xd8, 0x63,
-  0x42, 0xea, 0x5d, 0xd2, 0x25, 0xac, 0x93, 0x35, 0x18, 0xf0, 0xc8, 0x01,
-  0xc0, 0x01, 0x0e, 0x34, 0x08, 0xac, 0x35, 0x75, 0x09, 0x5a, 0x63, 0x66,
-  0x55, 0x76, 0xa1, 0xcb, 0xb2, 0x1a, 0x30, 0x04, 0x25, 0x20, 0x03, 0x0a,
-  0x64, 0xec, 0x7c, 0x71, 0xb7, 0x22, 0x2b, 0x59, 0xbe, 0x73, 0xe8, 0xd2,
-  0x66, 0xb5, 0x4b, 0x7f, 0xb6, 0x40, 0xe3, 0xe3, 0x35, 0xf2, 0x80, 0x0c,
-  0xc9, 0x58, 0xbd, 0xa2, 0xc0, 0x44, 0x71, 0x18, 0x24, 0xe6, 0xf2, 0x82,
-  0x7a, 0x08, 0xf6, 0x95, 0x57, 0x33, 0x22, 0x75, 0xab, 0x80, 0xa5, 0xda,
-  0x39, 0x05, 0x52, 0x35, 0xeb, 0xdf, 0x46, 0x2e, 0x86, 0xf2, 0x4d, 0x70,
-  0x54, 0x06, 0xe5, 0x77, 0x7a, 0xd7, 0x5f, 0x64, 0x02, 0x14, 0x57, 0x20,
-  0x04, 0x0d, 0x80, 0x08, 0xbd, 0x6b, 0xfb, 0xfb, 0x23, 0x3d, 0x86, 0x11,
-  0x75, 0x5c, 0x7f, 0x8a, 0xb5, 0x33, 0x2b, 0x23, 0x5d, 0x9f, 0xc7, 0x87,
-  0xc7, 0xf7, 0x5a, 0x20, 0xb2, 0x77, 0xfe, 0xa9, 0xcb, 0x0c, 0xfd, 0xd8,
-  0x04, 0xdc, 0x90, 0x4a, 0xa0, 0x0d, 0xe1, 0xba, 0x0c, 0xd5, 0x2f, 0x74,
-  0xb2, 0xeb, 0xc6, 0x5c, 0xf1, 0x72, 0xc0, 0x3d, 0x0d, 0x65, 0x68, 0xaa,
-  0x8b, 0x45, 0x4a, 0x19, 0x7f, 0x4c, 0xde, 0x25, 0x18, 0xc0, 0x1c, 0x01,
-  0x12, 0x34, 0x08, 0x70, 0x96, 0x32, 0x12, 0x04, 0x82, 0x50, 0xa1, 0x0c,
-  0x48, 0x51, 0x10, 0x56, 0x73, 0x8a, 0xda, 0x80, 0xbd, 0x8c, 0xeb, 0x08,
-  0x55, 0x8b, 0x50, 0x99, 0x17, 0x16, 0xb5, 0xb3, 0x8f, 0x92, 0xfb, 0x62,
-  0x53, 0x68, 0x3c, 0xa3, 0x9c, 0x76, 0x9a, 0x37, 0xd4, 0xe7, 0x19, 0x7b,
-  0x88, 0xe7, 0xf4, 0x7c, 0x52, 0x56, 0x72, 0xa6, 0x2b, 0x2d, 0xdf, 0x04,
-  0x07, 0x7b, 0xca, 0xaa, 0x43, 0x98, 0x40, 0xda, 0xd1, 0xc3, 0xb8, 0xf6,
-  0xbc, 0x28, 0xcd, 0x25, 0x28, 0xa1, 0x14, 0xb2, 0x7e, 0xc8, 0xa5, 0xf4,
-  0xbf, 0xdf, 0xbb, 0x20, 0xc9, 0x0f, 0xf1, 0x64, 0x7c, 0x6f, 0x82, 0x6e,
-  0xe0, 0x94, 0xf3, 0x4a, 0xde, 0x15, 0xb0, 0xbf, 0x10, 0xbf, 0x47, 0x25,
-  0xb1, 0x1d, 0x2a, 0xf6, 0xde, 0xc9, 0x91, 0x2c, 0xa7, 0x30, 0x2b, 0x85,
-  0xfe, 0x4b, 0x96, 0xfa, 0x9b, 0x46, 0x66, 0xcc, 0x66, 0x9b, 0xc8, 0xff,
-  0x95, 0xb2, 0xd9, 0xb6, 0xb2, 0x0b, 0xac, 0x82, 0x82, 0x06, 0x92, 0x68,
-  0x15, 0x10, 0x57, 0x2c, 0x42, 0xce, 0x50, 0x66, 0xec, 0x8c, 0x39, 0x6c,
-  0x7a, 0x53, 0xee, 0x11, 0xa6, 0xaa, 0xa0, 0x03, 0x78, 0x46, 0x83, 0xb5,
-  0x66, 0x46, 0xc0, 0xad, 0x77, 0x8e, 0xa8, 0x60, 0xdc, 0x34, 0x6a, 0x7f,
-  0x6b, 0x0f, 0xce, 0xf3, 0x59, 0x8d, 0x82, 0x47, 0x01, 0x12, 0x34, 0x08,
-  0x8c, 0x79, 0x22, 0x2d, 0xc2, 0x83, 0x22, 0x20, 0x48, 0x80, 0x16, 0x29,
-  0xdd, 0xe5, 0x4d, 0xb6, 0x0b, 0x66, 0xe1, 0x64, 0x2c, 0x17, 0x60, 0xe9,
-  0xb2, 0xac, 0xb8, 0x15, 0x74, 0x4c, 0x95, 0xc4, 0xaf, 0x9d, 0x33, 0x74,
-  0x7f, 0xec, 0x71, 0x32, 0x9a, 0x5b, 0x5a, 0xae, 0xfe, 0x29, 0xe0, 0xf3,
-  0x39, 0x80, 0x8d, 0x03, 0xdf, 0x4b, 0x8c, 0xb6, 0xba, 0xc0, 0xf5, 0x52,
-  0xb3, 0x0e, 0xda, 0xf8, 0x3f, 0xad, 0xd5, 0xda, 0x2c, 0xf0, 0x7c, 0xf4,
-  0xe8, 0x9f, 0xb4, 0xed, 0xb6, 0x58, 0x17, 0xfa, 0x79, 0xd5, 0x9d, 0x54,
-  0x94, 0xb4, 0xee, 0x78, 0x8e, 0xd8, 0x69, 0x0b, 0x40, 0x91, 0x1d, 0x10,
-  0x99, 0x4d, 0xb0, 0x8d, 0xd8, 0x32, 0x34, 0xe2, 0xb6, 0x15, 0x64, 0x0f,
-  0x5b, 0x19, 0x38, 0xd7, 0xee, 0xbd, 0x77, 0x31, 0x07, 0xae, 0xb5, 0xe3,
-  0xda, 0x24, 0x21, 0x7f, 0x90, 0x62, 0xc2, 0x0f, 0x27, 0x22, 0x49, 0xc2,
-  0xbf, 0xe5, 0x1e, 0x74, 0xf8, 0x06, 0xfe, 0xf0, 0x2c, 0x0d, 0xe1, 0xfa,
-  0x0d, 0xd5, 0x29, 0x19, 0x3b, 0xbc, 0xd9, 0xb3, 0x37, 0xb0, 0x0f, 0x80,
-  0xd9, 0x91, 0x5c, 0x9b, 0x16, 0x35, 0x9b, 0xcb, 0x62, 0xc7, 0xda, 0x6a,
-  0x9b, 0x25, 0x97, 0xa0, 0x1c, 0x01, 0x0c, 0x34, 0x00, 0x30, 0xe4, 0x33,
-  0x89, 0x04, 0xe2, 0x42, 0x10, 0xcc, 0x24, 0x40, 0x0b, 0x75, 0x95, 0x5d,
-  0xa8, 0x98, 0x06, 0xc4, 0x2c, 0x21, 0x60, 0x19, 0x04, 0x54, 0x02, 0x03,
-  0x06, 0xd5, 0x6f, 0xc6, 0x40, 0x10, 0xa0, 0x32, 0x14, 0xe5, 0x43, 0x9a,
-  0xa0, 0x00, 0x2e, 0x56, 0x12, 0xdf, 0xbf, 0xa1, 0xab, 0x56, 0xda, 0x71,
-  0x49, 0xbf, 0x0a, 0x79, 0x53, 0x64, 0x33, 0x2b, 0x63, 0x53, 0x62, 0xcd,
-  0x6d, 0x0f, 0x6e, 0xf0, 0x3e, 0x9e, 0x27, 0x42, 0x67, 0x00, 0x97, 0x4f,
-  0xa6, 0x4b, 0x19, 0x15, 0xf8, 0x4e, 0x87, 0xbc, 0xad, 0x61, 0x9a, 0x45,
-  0xd0, 0xa8, 0x5e, 0xe8, 0xb7, 0x81, 0x56, 0xc7, 0xae, 0x88, 0x08, 0xc8,
-  0xe6, 0xd5, 0xe2, 0xbc, 0x14, 0x67, 0x13, 0x4e, 0xc3, 0xb2, 0xe9, 0xa3,
-  0xbc, 0xf8, 0x78, 0x62, 0xe2, 0x10, 0x45, 0xe9, 0xd7, 0xf4, 0xec, 0xec,
-  0xc1, 0x74, 0x29, 0x86, 0x50, 0x43, 0x2e, 0x14, 0x32, 0xe6, 0x40, 0xa0,
-  0xe2, 0xfc, 0x8e, 0x65, 0x03, 0x83, 0xef, 0x09, 0xd0, 0x76, 0xad, 0x58,
-  0xf6, 0x41, 0x3e, 0xe0, 0xb9, 0xd8, 0x06, 0xe1, 0x4b, 0x8b, 0x49, 0x59,
-  0x4b, 0x11, 0x64, 0xa2, 0x93, 0x1a, 0x80, 0xe0, 0x01, 0x0a, 0x34, 0x08,
-  0x90, 0xb5, 0x3b, 0x0d, 0x50, 0x27, 0x7c, 0xc2, 0x98, 0xae, 0xca, 0x05,
-  0xf2, 0x2c, 0x41, 0x64, 0x4a, 0xd8, 0x23, 0x97, 0x03, 0xe1, 0x9e, 0xda,
-  0x10, 0x55, 0x56, 0x8a, 0xa6, 0x88, 0x5f, 0x74, 0x03, 0x7d, 0xe8, 0x42,
-  0x4b, 0x91, 0xf1, 0x00, 0x92, 0x74, 0x79, 0xca, 0xcc, 0xcc, 0xe2, 0x32,
-  0xf2, 0xff, 0x33, 0x31, 0x65, 0xe1, 0x45, 0xbe, 0x49, 0x21, 0x2d, 0x3d,
-  0x71, 0x36, 0x02, 0xc6, 0x46, 0x6d, 0x5a, 0x99, 0xea, 0xd1, 0x15, 0x15,
-  0xe9, 0xaa, 0xaa, 0x12, 0x7b, 0xfb, 0x7a, 0xf0, 0xa9, 0xb5, 0x1a, 0xa8,
-  0xe0, 0x17, 0x70, 0xa1, 0x28, 0x22, 0xe7, 0x7b, 0xb2, 0x80, 0x17, 0x7d,
-  0x37, 0x6c, 0x67, 0x49, 0x8e, 0xf2, 0xc7, 0x3f, 0x16, 0xeb, 0xd3, 0x54,
-  0xad, 0xf9, 0x28, 0xc5, 0x23, 0x3d, 0x7a, 0x7a, 0xf4, 0xa1, 0x92, 0xb3,
-  0x90, 0x96, 0x28, 0x9a, 0x65, 0x8b, 0x6f, 0x9e, 0xb7, 0xa4, 0xc0, 0x2f,
-  0x30, 0x06, 0xf0, 0xed, 0x06, 0xea, 0x93, 0xa6, 0x37, 0xeb, 0xbf, 0x03,
-  0x6d, 0xb6, 0xf6, 0x46, 0x3d, 0x0b, 0x53, 0x2e, 0x6e, 0x4e, 0xe2, 0xc5,
-  0x4d, 0x8f, 0xec, 0xdd, 0x18, 0x02, 0xf4, 0x0e, 0x01, 0x0e, 0x34, 0x19,
-  0xd0, 0x16, 0x18, 0x8d, 0x8c, 0x85, 0x34, 0x29, 0x59, 0xdd, 0x33, 0x18,
-  0xde, 0x5d, 0xd3, 0x54, 0x02, 0x22, 0xac, 0xcb, 0x60, 0x20, 0x04, 0x47,
-  0x1e, 0x0b, 0x82, 0xe5, 0x59, 0x08, 0x49, 0x23, 0x4e, 0xc9, 0x76, 0xf7,
-  0x07, 0x08, 0x27, 0xc1, 0xd1, 0x2d, 0x8c, 0x77, 0x2a, 0x98, 0xd9, 0x62,
-  0x8b, 0xea, 0x50, 0x56, 0x32, 0xaf, 0xde, 0x4f, 0x68, 0x01, 0xac, 0xb5,
-  0xd9, 0xd0, 0x6a, 0xb6, 0xca, 0x77, 0x1b, 0x00, 0xde, 0x0a, 0xac, 0x06,
-  0x6a, 0xaa, 0x3d, 0x6f, 0x04, 0x51, 0x10, 0x1e, 0x1d, 0xb1, 0xad, 0xe4,
-  0xa1, 0x06, 0x96, 0x6e, 0xcb, 0x82, 0x8c, 0xb1, 0x2e, 0x50, 0x2b, 0xfb,
-  0x81, 0x89, 0xe2, 0x26, 0xa7, 0x5b, 0x89, 0x1c, 0xc5, 0x77, 0xdb, 0x35,
-  0x87, 0x61, 0x83, 0x26, 0x1e, 0x55, 0x18, 0x97, 0x09, 0x4f, 0x66, 0x05,
-  0x80, 0x74, 0xea, 0x96, 0x3b, 0x94, 0xa6, 0xc2, 0x60, 0x0a, 0x2f, 0x10,
-  0x37, 0x85, 0xe8, 0x3b, 0x57, 0x62, 0x6d, 0xa4, 0x56, 0xa1, 0xca, 0xc0,
-  0x3d, 0x0f, 0xde, 0x87, 0x8b, 0xf9, 0x86, 0x6d, 0xd5, 0x72, 0xfd, 0xe9,
-  0x92, 0xb5, 0x00, 0x70, 0x01, 0x14, 0x34, 0x19, 0xe8, 0x76, 0x12, 0x28,
-  0xcc, 0x39, 0xa5, 0x51, 0xcf, 0x2c, 0xb1, 0x66, 0xd2, 0xb8, 0x40, 0x42,
-  0x2c, 0x08, 0xc0, 0x81, 0x01, 0xa2, 0x36, 0x1e, 0x20, 0x35, 0x80, 0x7f,
-  0xff, 0x68, 0xfc, 0x0f, 0xec, 0x8a, 0x06, 0x64, 0x14, 0x22, 0xd9, 0xe1,
-  0xf4, 0x8b, 0x78, 0x28, 0x40, 0x7b, 0xdd, 0xe4, 0x93, 0x8d, 0x26, 0x64,
-  0x6f, 0x27, 0x51, 0x48, 0xb6, 0xbd, 0xe4, 0xca, 0xd3, 0x23, 0x86, 0x35,
-  0x8c, 0x25, 0xca, 0xb5, 0xfd, 0x27, 0xea, 0x29, 0x92, 0x6d, 0x34, 0x21,
-  0x97, 0x98, 0x9c, 0x1f, 0x8e, 0x0a, 0xb9, 0xae, 0xbf, 0xbf, 0xdc, 0xcf,
-  0x88, 0xa0, 0xaf, 0xec, 0x64, 0x48, 0x38, 0xea, 0xdd, 0xf3, 0x4b, 0x92,
-  0x6b, 0x28, 0xc8, 0xe7, 0x6d, 0xaa, 0x43, 0x15, 0x5a, 0xad, 0xff, 0x56,
-  0x09, 0xec, 0xa5, 0xdf, 0xd7, 0xd6, 0xbb, 0x98, 0x8d, 0xef, 0x1d, 0xb6,
-  0x1e, 0x8b, 0x09, 0xd7, 0x83, 0x63, 0xa6, 0x62, 0x9e, 0x4c, 0x1c, 0x9b,
-  0xb9, 0xaa, 0x1c, 0x6a, 0xcf, 0x99, 0x10, 0x37, 0x85, 0xe8, 0x11, 0x52,
-  0x92, 0x8d, 0x5b, 0x72, 0x1c, 0x7c, 0x02, 0x15, 0xb9, 0x3b, 0xf4, 0xff,
-  0x42, 0x2e, 0x54, 0x91, 0xd1, 0xbe, 0x2c, 0x6b, 0x2c, 0x00, 0x70
-};
-
-static const guint BBB_32k_1_mp4_len = 8423;
diff --git a/tests/check/elements/rtp-payloading.c b/tests/check/elements/rtp-payloading.c
index bd34794bb7a30fad27e94b502e8c66daf3dc6db2..fc0d97fe6ca0bb0b8c5ec59700a39a01a79bfc7c 100644
--- a/tests/check/elements/rtp-payloading.c
+++ b/tests/check/elements/rtp-payloading.c
@@ -1162,6 +1162,95 @@ GST_START_TEST (rtp_klv_fragmented)
 
 GST_END_TEST;
 
+typedef struct
+{
+  gint buffer_count;
+  gint drop_index;
+} KlvDepayProbeData;
+
+static GstPadProbeReturn
+rtp_klv_do_packet_loss_depay_probe_cb (GstPad * pad, GstPadProbeInfo * info,
+    KlvDepayProbeData * data)
+{
+  GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+  if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
+    GstBufferList *buffer_list = info->data;
+    gint length = gst_buffer_list_length (buffer_list);
+
+    if (data->drop_index >= 0) {
+      gint drop_offset = data->drop_index - data->buffer_count;
+
+      if (0 <= drop_offset && drop_offset < length)
+        gst_buffer_list_remove (buffer_list, drop_offset, 1);
+    }
+
+    data->buffer_count += length;
+  } else {
+    if (data->buffer_count == data->drop_index)
+      ret = GST_PAD_PROBE_DROP;
+    data->buffer_count++;
+  }
+
+  return ret;
+}
+
+GST_START_TEST (rtp_klv_fragmented_packet_loss)
+{
+  /* Number of KLV frames to push through the pipeline */
+  const guint FRAME_COUNT = 5;
+
+  /* Repeat frame data */
+  int frame_data_size = sizeof (rtp_KLV_frame_data);
+  guint8 *frame_data = malloc (FRAME_COUNT * sizeof (rtp_KLV_frame_data));
+  for (guint i = 0; i < FRAME_COUNT; i++)
+    memcpy (frame_data + i * frame_data_size, rtp_KLV_frame_data,
+        frame_data_size);
+
+  /* Create RTP pipeline. */
+  rtp_pipeline *p =
+      rtp_pipeline_create (frame_data, frame_data_size, FRAME_COUNT,
+      "meta/x-klv, parsed=(bool)true", "rtpklvpay", "rtpklvdepay");
+
+  if (p == NULL) {
+    return;
+  }
+
+  /* Force super-small mtu of 60 to fragment KLV unit (4 fragments per unit) */
+  g_object_set (p->rtppay, "mtu", 60, NULL);
+
+  /* Drop the 7:th fragment on the depayloader's sink pad */
+  KlvDepayProbeData sink_probe_data = {.buffer_count = 0,.drop_index = 7 };
+  GstPad *depay_sink = gst_element_get_static_pad (p->rtpdepay, "sink");
+  gst_pad_add_probe (depay_sink,
+      GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
+      (GstPadProbeCallback) rtp_klv_do_packet_loss_depay_probe_cb,
+      &sink_probe_data, NULL);
+  gst_object_unref (depay_sink);
+
+  /* Count buffers on the depayloader's source pad */
+  KlvDepayProbeData src_probe_data = {.buffer_count = 0,.drop_index = -1 };
+  GstPad *depay_src = gst_element_get_static_pad (p->rtpdepay, "src");
+  gst_pad_add_probe (depay_src,
+      GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
+      (GstPadProbeCallback) rtp_klv_do_packet_loss_depay_probe_cb,
+      &src_probe_data, NULL);
+  gst_object_unref (depay_src);
+
+  /* Run RTP pipeline. */
+  rtp_pipeline_run (p);
+
+  /* Destroy RTP pipeline. */
+  rtp_pipeline_destroy (p);
+
+  free (frame_data);
+
+  /* We should be able to decode all RTP buffers except for the second one */
+  g_assert_cmpuint (src_probe_data.buffer_count, ==, FRAME_COUNT - 1);
+}
+
+GST_END_TEST;
+
 static const guint8 rtp_L16_frame_data[] =
     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
@@ -1801,6 +1890,7 @@ rtp_payloading_suite (void)
   tcase_add_test (tc_chain, rtp_h265_list_gt_mtu_hvc1);
   tcase_add_test (tc_chain, rtp_klv);
   tcase_add_test (tc_chain, rtp_klv_fragmented);
+  tcase_add_test (tc_chain, rtp_klv_fragmented_packet_loss);
   tcase_add_test (tc_chain, rtp_L16);
   tcase_add_test (tc_chain, rtp_L24);
   tcase_add_test (tc_chain, rtp_mp2t);
diff --git a/tests/check/elements/rtpbin_buffer_list.c b/tests/check/elements/rtpbin_buffer_list.c
index b804744352a7da101350e538c28c138560cf96b0..1c3d5cdd3a7a14ea9b67205197b46ac5fe0ad4eb 100644
--- a/tests/check/elements/rtpbin_buffer_list.c
+++ b/tests/check/elements/rtpbin_buffer_list.c
@@ -25,6 +25,12 @@
 #include <gst/rtp/gstrtpbuffer.h>
 #include <gst/rtp/gstrtcpbuffer.h>
 
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define READ_UINT64(mem) GST_READ_UINT64_BE(mem)
+#else
+#define READ_UINT64(mem) GST_READ_UINT64_LE(mem)
+#endif
+
 /* UDP/IP is assumed for bandwidth calculation */
 #define UDP_IP_HEADER_OVERHEAD 28
 
@@ -236,8 +242,8 @@ check_header (GstBuffer * buffer, guint index)
    * most likely be changed in gstrtpbin.
    */
   fail_unless (info.data != NULL);
-  fail_unless_equals_uint64 (*(guint64 *) info.data,
-      *(guint64 *) rtp_header[index]);
+  fail_unless_equals_uint64 (READ_UINT64 (info.data),
+      READ_UINT64 (rtp_header[index]));
   fail_unless (*(guint16 *) (info.data + 12) ==
       *(guint16 *) (rtp_header[index] + 12));
 
diff --git a/tests/check/elements/rtpcollision.c b/tests/check/elements/rtpcollision.c
index dca5a6b5515abffcd5188c3f81a042451a5feaa9..145aab0fbbf8c4aefe45c91db57f1af3229fdfcd 100644
--- a/tests/check/elements/rtpcollision.c
+++ b/tests/check/elements/rtpcollision.c
@@ -95,15 +95,15 @@ create_rtcp_app (guint32 ssrc, guint count)
 
   /* need to begin with rr */
   gst_rtcp_buffer_map (rtcp_buffer, GST_MAP_READWRITE, &rtcp);
-  rtcp_packet = g_slice_new0 (GstRTCPPacket);
+  rtcp_packet = g_new0 (GstRTCPPacket, 1);
   gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_RR, rtcp_packet);
   gst_rtcp_packet_rr_set_ssrc (rtcp_packet, ssrc);
-  g_slice_free (GstRTCPPacket, rtcp_packet);
+  g_free (rtcp_packet);
 
   /* useful to make the rtcp buffer valid */
-  rtcp_packet = g_slice_new0 (GstRTCPPacket);
+  rtcp_packet = g_new0 (GstRTCPPacket, 1);
   gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_APP, rtcp_packet);
-  g_slice_free (GstRTCPPacket, rtcp_packet);
+  g_free (rtcp_packet);
   gst_rtcp_buffer_unmap (&rtcp);
 
   return rtcp_buffer;
diff --git a/tests/check/elements/rtpfunnel.c b/tests/check/elements/rtpfunnel.c
index 676127ca18d35b86e40e4de11ae46b42fb9d56b6..a7da56ad007dc0d893e8d1b70c71806ef58bf73d 100644
--- a/tests/check/elements/rtpfunnel.c
+++ b/tests/check/elements/rtpfunnel.c
@@ -491,6 +491,45 @@ GST_START_TEST (rtpfunnel_twcc_passthrough_then_mux)
 
 GST_END_TEST;
 
+GST_START_TEST (rtpfunnel_flush)
+{
+  GstHarness *h = gst_harness_new_with_padnames ("rtpfunnel", NULL, "src");
+  GstHarness *h0 = gst_harness_new_with_element (h->element, "sink_0", NULL);
+  GstEvent *event;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h0, "application/x-rtp, ssrc=(uint)123");
+
+  /* Push a buffer */
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h0,
+          generate_test_buffer (0, 123, 0)));
+
+  buffer = gst_harness_try_pull (h);
+  gst_buffer_unref (buffer);
+
+  /* Flush */
+  fail_unless (gst_harness_push_event (h0, gst_event_new_flush_start ()));
+  fail_unless (gst_harness_push_event (h0, gst_event_new_flush_stop (TRUE)));
+
+  while ((event = gst_harness_try_pull_event (h)))
+    gst_event_unref (event);
+
+  /* Reset caps and segment */
+  gst_harness_set_src_caps_str (h0, "application/x-rtp, ssrc=(uint)123");
+
+  /* Push another buffer, this shouldn't generate "got data flow before segment event" criticals */
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h0,
+          generate_test_buffer (1, 123, 0)));
+
+  buffer = gst_harness_try_pull (h);
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h0);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtpfunnel_suite (void)
 {
@@ -511,6 +550,8 @@ rtpfunnel_suite (void)
   tcase_add_test (tc_chain, rtpfunnel_twcc_mux);
   tcase_add_test (tc_chain, rtpfunnel_twcc_passthrough_then_mux);
 
+  tcase_add_test (tc_chain, rtpfunnel_flush);
+
   return s;
 }
 
diff --git a/tests/check/elements/rtph264.c b/tests/check/elements/rtph264.c
index 79c2495788d48b4138f94c3eaccdf409ddfacf4a..1b90a257becf4c72461a2e717e3bb447b27ab820 100644
--- a/tests/check/elements/rtph264.c
+++ b/tests/check/elements/rtph264.c
@@ -1455,6 +1455,156 @@ GST_START_TEST (test_rtph264pay_avc_incomplete_nal)
 
 GST_END_TEST;
 
+/* A buffer consists of two memory chunks: a primary slice with
+ * fist_mb_in_slice set to 0 and a secondary one. Only the first slice
+ * should trigger generation of SPS and PPS buffers */
+GST_START_TEST (test_rtph264pay_avc_two_slices_per_buffer_config_interval)
+{
+  GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
+      " name=p config-interval=-1");
+  GstFlowReturn ret;
+  GstBuffer *slice1;
+  GstBuffer *slice2;
+  GstBuffer *buffer;
+  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+  guint8 *rest_of_image;
+  gsize rest_of_slice_1_size;
+  gsize rest_of_image_size;
+  guint num_buffers;
+  guint8 *payload = NULL;
+  guint8 nal_type;
+
+  gst_harness_set_src_caps_str (h,
+      "video/x-h264,alignment=au,stream-format=avc,"
+      "codec_data=(buffer)01f4000dffe1001c67f4000d919b2884d80b50606064000003"
+      "000400000300f23c50a65801000668ebec448440");
+
+  /* first slice */
+  slice1 = wrap_static_buffer (h264_idr_slice_1_avc, 1);
+  rest_of_slice_1_size = sizeof (h264_idr_slice_1_avc) - 1;
+
+  rest_of_image_size = rest_of_slice_1_size + sizeof (h264_idr_slice_2_avc);
+  rest_of_image = g_malloc (rest_of_image_size);
+
+  memcpy (rest_of_image, h264_idr_slice_1_avc + 1, rest_of_slice_1_size);
+  memcpy (rest_of_image + rest_of_slice_1_size, h264_idr_slice_2_avc,
+      sizeof (h264_idr_slice_2_avc));
+
+  /* second slice */
+  slice2 =
+      wrap_static_buffer_full (rest_of_image, rest_of_image_size,
+      rest_of_image, g_free);
+  buffer = gst_buffer_append (slice1, slice2);
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+  /* SPS PPS IDR (Slice1) IDR (Slice2) */
+  num_buffers = gst_harness_buffers_in_queue (h);
+  fail_unless_equals_int (num_buffers, 4);
+
+  for (guint i = 0; i < num_buffers; i++) {
+    buffer = gst_harness_pull (h);
+    fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
+    payload = gst_rtp_buffer_get_payload (&rtp);
+    gst_rtp_buffer_unmap (&rtp);
+    nal_type = (GST_READ_UINT8 (payload)) & 0x1f;
+    GST_INFO ("nal_type=%d", nal_type);
+    if (i == 0) {
+      fail_unless_equals_int (nal_type, 7);
+    } else if (i == 1) {
+      fail_unless_equals_int (nal_type, 8);
+    } else if (i == 2) {
+      fail_unless_equals_int (nal_type, 5);
+    } else if (i == 3) {
+      fail_unless_equals_int (nal_type, 5);
+    }
+
+    gst_buffer_unref (buffer);
+  }
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtph264pay_avc_delta_unit_multiple_nal)
+{
+  GstHarness *h = gst_harness_new_parse ("rtph264pay mtu=40");
+  GstFlowReturn ret;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h,
+      "video/x-h264,alignment=au,stream-format=avc,"
+      "codec_data=(buffer)01f4000dffe1001c67f4000d919b2884d80b50606064000003"
+      "000400000300f23c50a65801000668ebec448440");
+
+  /* append two NAL's, each in separate memory blocks to the input buffer */
+  buffer = wrap_static_buffer_with_pts (h264_idr_slice_1_avc,
+      sizeof (h264_idr_slice_1_avc), 0);
+  buffer = gst_buffer_append (buffer,
+      wrap_static_buffer_with_pts (h264_idr_slice_2_avc,
+          sizeof (h264_idr_slice_2_avc), 0));
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  /* each NAL should be split into two buffers and pushed as a buffer list,
+   * only the first buffer of the first buffer list should be marked as a
+   * non-delta unit */
+  buffer = gst_harness_pull (h);
+  fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtph264pay_avc_delta_unit_single_nal_multiple_memories)
+{
+  GstHarness *h = gst_harness_new_parse ("rtph264pay mtu=40");
+  GstFlowReturn ret;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h,
+      "video/x-h264,alignment=au,stream-format=avc,"
+      "codec_data=(buffer)01f4000dffe1001c67f4000d919b2884d80b50606064000003"
+      "000400000300f23c50a65801000668ebec448440");
+
+  /* append one NAL spanning over two memory blocks to the input buffer */
+  gsize second_mem_size = 10;
+  gsize first_mem_size = sizeof (h264_idr_slice_1_avc) - second_mem_size;
+  buffer = wrap_static_buffer (h264_idr_slice_1_avc, first_mem_size);
+  buffer = gst_buffer_append (buffer,
+      wrap_static_buffer (h264_idr_slice_1_avc + first_mem_size,
+          second_mem_size));
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  /* the NAL should be split into two buffers and pushed as a buffer list, only
+   * the first buffer in the buffer list should be marked as a non-delta unit */
+  buffer = gst_harness_pull (h);
+  fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtph264_suite (void)
 {
@@ -1486,7 +1636,11 @@ rtph264_suite (void)
   tcase_add_test (tc_chain, test_rtph264pay_avc);
   tcase_add_test (tc_chain, test_rtph264pay_avc_two_slices_per_buffer);
   tcase_add_test (tc_chain, test_rtph264pay_avc_incomplete_nal);
-
+  tcase_add_test (tc_chain,
+      test_rtph264pay_avc_two_slices_per_buffer_config_interval);
+  tcase_add_test (tc_chain, test_rtph264pay_avc_delta_unit_multiple_nal);
+  tcase_add_test (tc_chain,
+      test_rtph264pay_avc_delta_unit_single_nal_multiple_memories);
   return s;
 }
 
diff --git a/tests/check/elements/rtph265.c b/tests/check/elements/rtph265.c
index c35d05c52ae31027d22a4428028eeaba6d12c97f..d5474b50450e02b5d4e055711015a9ed43bdd0fb 100644
--- a/tests/check/elements/rtph265.c
+++ b/tests/check/elements/rtph265.c
@@ -1181,6 +1181,87 @@ GST_START_TEST (test_rtph265pay_delta_unit_flag)
 
 GST_END_TEST;
 
+GST_START_TEST (test_rtph265pay_delta_unit_multiple_nal)
+{
+  GstHarness *h = gst_harness_new_parse ("rtph265pay mtu=28");
+  GstFlowReturn ret;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h,
+      "video/x-h265,alignment=au,stream-format=hvc1,"
+      "codec_data=(buffer)0104080000009e28000000003ff000fcfff8f800000f032000"
+      "01001740010c01ffff0408000003009e2800000300003fba0240210001002f4201010"
+      "408000003009e2800000300003f90041020b2dd492657ff80008000b5060606040000"
+      "03000400000300782022000100074401c172b02240");
+
+  /* append two NAL's, each in separate memory blocks to the input buffer */
+  buffer = wrap_static_buffer (h265_hvc1_idr_data, sizeof (h265_hvc1_idr_data));
+  buffer = gst_buffer_append (buffer,
+      wrap_static_buffer (h265_hvc1_idr_data, sizeof (h265_hvc1_idr_data)));
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  /* each NAL should be split into two buffers and pushed as a buffer list,
+   * only the first buffer of the first buffer list should be marked as a
+   * non-delta unit */
+  buffer = gst_harness_pull (h);
+  fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtph265pay_delta_unit_single_nal_multiple_memories)
+{
+  GstHarness *h = gst_harness_new_parse ("rtph265pay mtu=28");
+  GstFlowReturn ret;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h,
+      "video/x-h265,alignment=au,stream-format=hvc1,"
+      "codec_data=(buffer)0104080000009e28000000003ff000fcfff8f800000f032000"
+      "01001740010c01ffff0408000003009e2800000300003fba0240210001002f4201010"
+      "408000003009e2800000300003f90041020b2dd492657ff80008000b5060606040000"
+      "03000400000300782022000100074401c172b02240");
+
+  /* append one NAL spanning over two memory blocks to the input buffer */
+  gsize second_mem_size = 10;
+  gsize first_mem_size = sizeof (h265_hvc1_idr_data) - second_mem_size;
+  buffer = wrap_static_buffer (h265_hvc1_idr_data, first_mem_size);
+  buffer = gst_buffer_append (buffer,
+      wrap_static_buffer (h265_hvc1_idr_data + first_mem_size,
+          second_mem_size));
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  /* the NAL should be split into two buffers and pushed as a buffer list, only
+   * the first buffer in the buffer list should be marked as a non-delta unit */
+  buffer = gst_harness_pull (h);
+  fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_rtph265pay_delta_unit_flag_config_interval)
 {
   GstHarness *h = gst_harness_new_parse ("rtph265pay timestamp-offset=123"
@@ -1305,6 +1386,9 @@ rtph265_suite (void)
   tcase_add_test (tc_chain, test_rtph265pay_aggregate_until_vcl);
   tcase_add_test (tc_chain, test_rtph265pay_aggregate_verify_nalu_hdr);
   tcase_add_test (tc_chain, test_rtph265pay_delta_unit_flag);
+  tcase_add_test (tc_chain, test_rtph265pay_delta_unit_multiple_nal);
+  tcase_add_test (tc_chain,
+      test_rtph265pay_delta_unit_single_nal_multiple_memories);
   tcase_add_test (tc_chain, test_rtph265pay_delta_unit_flag_config_interval);
 
   return s;
diff --git a/tests/check/elements/rtpjitterbuffer.c b/tests/check/elements/rtpjitterbuffer.c
index de8f06af14514d1aea2feadf5e92c1d667d12a36..8984c350818b9dacce4f5a7a093f873e0178cd89 100644
--- a/tests/check/elements/rtpjitterbuffer.c
+++ b/tests/check/elements/rtpjitterbuffer.c
@@ -29,6 +29,7 @@
 #include <gst/check/gsttestclock.h>
 #include <gst/check/gstharness.h>
 #include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
 
 /* For ease of programming we use globals to keep refs for our floating
  * src and sink pads we create; otherwise we always have to do get_pad,
@@ -58,6 +59,11 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
     GST_STATIC_CAPS ("application/x-rtp, "
         "clock-rate = (int) [ 1, 2147483647 ]")
     );
+static GstStaticPadTemplate rtcpsrctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
 
 static void
 buffer_dropped (G_GNUC_UNUSED gpointer data, GstMiniObject * obj)
@@ -709,6 +715,71 @@ construct_deterministic_initial_state (GstHarness * h, gint latency_ms)
   return next_seqnum;
 }
 
+static GstBuffer *
+setup_rtcp_sender_report (GstElement * jitterbuffer,
+    guint64 ntp_time_seconds, guint32 rtp_time)
+{
+  GstRTCPBuffer rtcp_buf = GST_RTCP_BUFFER_INIT;
+  GstRTCPPacket packet;
+  GstBuffer *srep_buf;
+
+  srep_buf = gst_rtcp_buffer_new (1000);
+
+  if (gst_rtcp_buffer_map (srep_buf, GST_MAP_READWRITE, &rtcp_buf)) {
+    if (gst_rtcp_buffer_add_packet (&rtcp_buf, GST_RTCP_TYPE_SR, &packet)) {
+      gst_rtcp_packet_sr_set_sender_info (&packet, TEST_BUF_SSRC,       /* SSRC */
+          /* ntp_time_seconds is the test time in seconds since Jan 1 1900.
+             Here it is converted to NTP format  */
+          (guint64) ntp_time_seconds << 32,     /* NTP timestamp */
+          rtp_time,             /* RTP timestamp */
+          1,                    /* sender's packet count */
+          100);                 /* sender's octet count */
+    }
+
+    gst_rtcp_buffer_unmap (&rtcp_buf);
+  }
+
+  return srep_buf;
+}
+
+static GstPad *
+setup_rtcp_pads (GstElement * jitterbuffer)
+{
+  GstPad *rtcp_fxsrc_pad;
+  GstPad *rtcp_sink_pad;
+  GstPadTemplate *pad_tmp;
+  GstCaps *rtcp_caps;
+
+  pad_tmp = gst_static_pad_template_get (&rtcpsrctemplate);
+
+  rtcp_fxsrc_pad = gst_pad_new_from_template (pad_tmp, "src");
+  fail_if (rtcp_fxsrc_pad == NULL, "Could not create a srcpad");
+
+  rtcp_sink_pad = gst_element_request_pad_simple (jitterbuffer, "sink_rtcp");
+  fail_if (rtcp_sink_pad == NULL, "Could not get sink pad from %s",
+      GST_ELEMENT_NAME (jitterbuffer));
+
+  fail_unless (gst_pad_link (rtcp_fxsrc_pad, rtcp_sink_pad) == GST_PAD_LINK_OK,
+      "Could not link source and %s sink pads",
+      GST_ELEMENT_NAME (jitterbuffer));
+
+  gst_pad_set_active (rtcp_sink_pad, TRUE);
+  gst_pad_set_active (rtcp_fxsrc_pad, TRUE);
+
+
+  rtcp_caps = gst_caps_new_simple ("application/x-rtcp",
+      "clock-rate", G_TYPE_INT, TEST_BUF_CLOCK_RATE, NULL);
+
+  gst_check_setup_events_with_stream_id (rtcp_fxsrc_pad, jitterbuffer,
+      rtcp_caps, GST_FORMAT_TIME, "/test/jitbuf/rtcp");
+
+  gst_object_unref (pad_tmp);
+  gst_caps_unref (rtcp_caps);
+  gst_object_unref (rtcp_sink_pad);
+
+  return rtcp_fxsrc_pad;
+}
+
 GST_START_TEST (test_lost_event)
 {
   GstHarness *h = gst_harness_new ("rtpjitterbuffer");
@@ -3498,6 +3569,65 @@ GST_START_TEST (test_gap_using_rtx_does_not_stall)
 
 GST_END_TEST;
 
+GST_START_TEST (test_early_rtcp_sr_allows_meta)
+{
+  GstElement *jitterbuffer;
+  GstPad *rtcp_fxsrc_pad;
+  GstBuffer *srep_buf;
+  GstBuffer *rtp_buffer;
+  GstReferenceTimestampMeta *meta;
+  GstCaps *ntp_caps;
+
+  // No buffers since we want to control them later
+  jitterbuffer = setup_jitterbuffer (0);
+
+  g_object_set (G_OBJECT (jitterbuffer),
+      "add-reference-timestamp-meta", TRUE, NULL);
+
+  fail_unless (start_jitterbuffer (jitterbuffer)
+      == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+  srep_buf = setup_rtcp_sender_report (jitterbuffer, 3899471400, 1000);
+
+  rtcp_fxsrc_pad = setup_rtcp_pads (jitterbuffer);
+
+  /* rtcp sr is first */
+  gst_pad_push (rtcp_fxsrc_pad, srep_buf);
+
+  /* create rtp buf, with matching rtp timestamp */
+  rtp_buffer = gst_rtp_buffer_new_allocate (0, 0, 0);
+
+  if (rtp_buffer) {
+    GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+    if (gst_rtp_buffer_map (rtp_buffer, GST_MAP_WRITE, &rtp)) {
+      gst_rtp_buffer_set_ssrc (&rtp, TEST_BUF_SSRC);
+      /* first rtp buffer, but second buffer overall, arrives 1 clock unit 
+         after rtcp sr */
+      gst_rtp_buffer_set_timestamp (&rtp, 1001);
+
+      gst_rtp_buffer_unmap (&rtp);
+    }
+  }
+
+  /* RTP buf is second */
+  gst_pad_push (mysrcpad, rtp_buffer);
+
+  ntp_caps = gst_caps_new_empty_simple ("timestamp/x-ntp");
+
+  meta = gst_buffer_get_reference_timestamp_meta (rtp_buffer, ntp_caps);
+
+  /* result should match the test time plus one clock unit. One
+     clock unit is 125000 nanoseconds */
+  fail_unless (meta->timestamp == (3899471400 * GST_SECOND + 125000));
+
+  /* cleanup */
+  cleanup_jitterbuffer (jitterbuffer);
+  gst_object_unref (rtcp_fxsrc_pad);
+  gst_caps_unref (ntp_caps);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtpjitterbuffer_suite (void)
 {
@@ -3577,6 +3707,7 @@ rtpjitterbuffer_suite (void)
   tcase_add_test (tc_chain, test_multiple_lost_do_not_stall);
   tcase_add_test (tc_chain, test_reset_using_rtx_packets_does_not_stall);
   tcase_add_test (tc_chain, test_gap_using_rtx_does_not_stall);
+  tcase_add_test (tc_chain, test_early_rtcp_sr_allows_meta);
 
 
   return s;
diff --git a/tests/check/elements/rtppassthrough.c b/tests/check/elements/rtppassthrough.c
new file mode 100644
index 0000000000000000000000000000000000000000..ad9dcd52d8e411748bbfbe7fca8e937241ad69ff
--- /dev/null
+++ b/tests/check/elements/rtppassthrough.c
@@ -0,0 +1,122 @@
+/* GStreamer
+ *
+ * Copyright (C) 2023 Jonas Danielsson <jonas.danielsson@spiideo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/check/check.h>
+#include <gst/check/gstharness.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+#define buffer_from_array(a) gst_buffer_new_memdup (a, G_N_ELEMENTS (a))
+
+static guint8 klv_data[] = {
+  0x06, 0x0e, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x02, 0x00, 0x03,
+};
+
+GST_START_TEST (test_pay_depay_passthrough)
+{
+  GstHarness *h =
+      gst_harness_new_parse ("rtpklvpay ! rtppassthroughpay ! rtpklvdepay");
+  GstBuffer *buf = buffer_from_array (klv_data);
+
+  gst_harness_set_src_caps_str (h, "meta/x-klv,parsed=true");
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h, buf));
+
+  gst_buffer_unref (gst_harness_pull (h));
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST
+GST_START_TEST (test_read_properties)
+{
+  GstElement *passthrough_pay;
+  GstElement *klv_pay;
+  GstStructure *stats;
+  guint pt;
+  guint ssrc_klv;
+  guint ssrc_passthrough;
+  GstHarness *h =
+      gst_harness_new_parse ("rtpklvpay pt=97 ssrc=424242 ! rtppassthroughpay");
+  GstBuffer *buf = buffer_from_array (klv_data);
+
+  gst_harness_set_src_caps_str (h, "meta/x-klv,parsed=true");
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h, buf));
+
+  passthrough_pay = gst_harness_find_element (h, "rtppassthroughpay");
+  g_object_get (G_OBJECT (passthrough_pay), "pt", &pt, NULL);
+  fail_unless_equals_uint64 (pt, 97U);
+
+  klv_pay = gst_harness_find_element (h, "rtpklvpay");
+  g_object_get (G_OBJECT (klv_pay), "ssrc", &ssrc_klv, NULL);
+  g_object_get (G_OBJECT (passthrough_pay), "stats", &stats, NULL);
+  gst_structure_get_uint (stats, "ssrc", &ssrc_passthrough);
+  fail_unless_equals_uint64 (424242U, ssrc_passthrough);
+
+  gst_structure_free (stats);
+  gst_object_unref (klv_pay);
+  gst_object_unref (passthrough_pay);
+  gst_buffer_unref (gst_harness_pull (h));
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_override_payload_type)
+{
+  GstElement *passthrough_pay;
+  guint pt;
+  GstHarness *h =
+      gst_harness_new_parse ("rtpklvpay pt=97 ! rtppassthroughpay pt=98");
+  GstBuffer *buf = buffer_from_array (klv_data);
+  GstRTPBuffer rtp_buf = GST_RTP_BUFFER_INIT;
+
+  gst_harness_set_src_caps_str (h, "meta/x-klv,parsed=true");
+  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h, buf));
+
+  passthrough_pay = gst_harness_find_element (h, "rtppassthroughpay");
+  g_object_get (G_OBJECT (passthrough_pay), "pt", &pt, NULL);
+  fail_unless_equals_uint64 (pt, 98U);
+
+  buf = gst_harness_pull (h);
+  gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp_buf);
+  fail_unless_equals_uint64 (gst_rtp_buffer_get_payload_type (&rtp_buf), 98U);
+  gst_rtp_buffer_unmap (&rtp_buf);
+
+  gst_object_unref (passthrough_pay);
+  gst_buffer_unref (buf);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtppassthrough_suite (void)
+{
+  Suite *s = suite_create ("rtppassthrough");
+  TCase *tc_chain;
+
+  suite_add_tcase (s, (tc_chain = tcase_create ("rtppassthrough")));
+  tcase_add_test (tc_chain, test_pay_depay_passthrough);
+  tcase_add_test (tc_chain, test_read_properties);
+  tcase_add_test (tc_chain, test_override_payload_type);
+
+  return s;
+}
+
+GST_CHECK_MAIN (rtppassthrough);
diff --git a/tests/check/elements/rtpred.c b/tests/check/elements/rtpred.c
index 99b08fdd7a0628bb65b31534b4acca008c0464d8..80dd2715f5634ade7df4e2c126e770bbe286cffb 100644
--- a/tests/check/elements/rtpred.c
+++ b/tests/check/elements/rtpred.c
@@ -398,7 +398,7 @@ GST_START_TEST (rtpreddec_invalid)
   bufinp =
       _new_rtp_buffer (FALSE, 0, PT_RED, 1, TIMESTAMP_NTH (1), 0xabe2b0b, 1);
   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
-  memcpy (gst_rtp_buffer_get_payload (&rtp), &data, sizeof (data));
+  memcpy (gst_rtp_buffer_get_payload (&rtp), &data, 1);
   gst_rtp_buffer_unmap (&rtp);
   _push_and_check_didnt_go_through (h, bufinp);
 
@@ -406,7 +406,7 @@ GST_START_TEST (rtpreddec_invalid)
   bufinp =
       _new_rtp_buffer (FALSE, 0, PT_RED, 2, TIMESTAMP_NTH (2), 0xabe2b0b, 4);
   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
-  memcpy (gst_rtp_buffer_get_payload (&rtp), &data, sizeof (data));
+  memcpy (gst_rtp_buffer_get_payload (&rtp), &data, 4);
   gst_rtp_buffer_unmap (&rtp);
   _push_and_check_didnt_go_through (h, bufinp);
 
diff --git a/tests/check/elements/rtpsession.c b/tests/check/elements/rtpsession.c
index ab3d4e92afc7e5e2732045527fccb12978ad6690..6ec67bc3afe8684b8bda82ead377b2c11f95ebfb 100644
--- a/tests/check/elements/rtpsession.c
+++ b/tests/check/elements/rtpsession.c
@@ -2816,13 +2816,16 @@ typedef struct
 } TWCCPacket;
 
 #define TWCC_DELTA_UNIT (250 * GST_USECOND)
+#define TWCC_REF_TIME_UNIT (64 * GST_MSECOND)
+#define TWCC_REF_TIME_INITIAL_OFFSET ((1 << 24) * TWCC_REF_TIME_UNIT)
 
 static void
 fail_unless_equals_twcc_clocktime (GstClockTime twcc_packet_ts,
     GstClockTime pkt_ts)
 {
   fail_unless_equals_clocktime (
-      (twcc_packet_ts / TWCC_DELTA_UNIT) * TWCC_DELTA_UNIT, pkt_ts);
+      (twcc_packet_ts / TWCC_DELTA_UNIT) * TWCC_DELTA_UNIT +
+      TWCC_REF_TIME_INITIAL_OFFSET, pkt_ts);
 }
 
 #define twcc_push_packets(h, packets)                                          \
@@ -3466,7 +3469,7 @@ GST_START_TEST (test_twcc_duplicate_seqnums)
   TWCCPacket packets[] = {
     {1, 4 * 32 * GST_MSECOND, FALSE},
     {2, 5 * 32 * GST_MSECOND, FALSE},
-    {2, 6 * 32 * GST_MSECOND, FALSE},
+    {1, 6 * 32 * GST_MSECOND, FALSE},
     {3, 7 * 32 * GST_MSECOND, TRUE},
   };
 
@@ -3673,17 +3676,17 @@ GST_START_TEST (test_twcc_delta_ts_rounding)
   };
 
   TWCCPacket exp_packets[] = {
-    {2002, 9 * GST_SECOND + 366250000, FALSE}
+    {2002, TWCC_REF_TIME_INITIAL_OFFSET + 9 * GST_SECOND + 366250000, FALSE}
     ,
-    {2003, 9 * GST_SECOND + 366250000, FALSE}
+    {2003, TWCC_REF_TIME_INITIAL_OFFSET + 9 * GST_SECOND + 366250000, FALSE}
     ,
-    {2017, 9 * GST_SECOND + 366750000, FALSE}
+    {2017, TWCC_REF_TIME_INITIAL_OFFSET + 9 * GST_SECOND + 366750000, FALSE}
     ,
-    {2019, 9 * GST_SECOND + 391500000, FALSE}
+    {2019, TWCC_REF_TIME_INITIAL_OFFSET + 9 * GST_SECOND + 391500000, FALSE}
     ,
-    {2020, 9 * GST_SECOND + 426750000, FALSE}
+    {2020, TWCC_REF_TIME_INITIAL_OFFSET + 9 * GST_SECOND + 426750000, FALSE}
     ,
-    {2025, 9 * GST_SECOND + 427000000, TRUE}
+    {2025, TWCC_REF_TIME_INITIAL_OFFSET + 9 * GST_SECOND + 427000000, TRUE}
     ,
   };
 
diff --git a/tests/check/elements/rtpvp8.c b/tests/check/elements/rtpvp8.c
index 1bcd0d80852188e7f1f92ab1643461c22fb0e265..e62bcb9ae032c1a0a1100260750346f21f3589ec 100644
--- a/tests/check/elements/rtpvp8.c
+++ b/tests/check/elements/rtpvp8.c
@@ -105,12 +105,10 @@ add_vp8_meta (GstBuffer * buffer, gboolean use_temporal_scaling,
     gboolean layer_sync, guint layer_id, guint tl0picidx)
 {
   GstCustomMeta *meta;
-  GstStructure *s;
 
   meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
   fail_unless (meta != NULL);
-  s = gst_custom_meta_get_structure (meta);
-  gst_structure_set (s,
+  gst_structure_set (meta->structure,
       "use-temporal-scaling", G_TYPE_BOOLEAN, use_temporal_scaling,
       "layer-sync", G_TYPE_BOOLEAN, layer_sync,
       "layer-id", G_TYPE_UINT, layer_id,
@@ -136,18 +134,18 @@ static const struct no_meta_test_data
   guint vp8_payload_control_value;
 } no_meta_test_data[] = {
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x10},       /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x10},   /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0x90},    /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0x90},        /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, 4, 0x90},    /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
-      /* repeated with non reference frame */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, 4, 0x90},        /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+  /* repeated with non reference frame */
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x30},       /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x30},   /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0xB0},    /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0xB0},        /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, 4, 0xB0},    /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, 4, 0xB0},        /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
 };
 
 GST_START_TEST (test_pay_no_meta)
@@ -221,42 +219,42 @@ static const struct with_meta_test_data
   guint vp8_payload_extended_value;
 } with_meta_test_data[] = {
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x10, 0x80},   /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x10, 0x80},       /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, FALSE, FALSE, 3, 0x90, 0x80},        /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, FALSE, FALSE, 3, 0x90, 0x80},    /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, FALSE, FALSE, 4, 0x90, 0x80},        /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, FALSE, FALSE, 4, 0x90, 0x80},    /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0x90, 0x60},    /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0x90, 0x60},        /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, FALSE, 5, 0x90, 0xE0}, /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, FALSE, 5, 0x90, 0xE0},     /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, FALSE, 6, 0x90, 0xE0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, FALSE, 6, 0x90, 0xE0},     /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0x90, 0x60},     /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0x90, 0x60}, /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, TRUE, 5, 0x90, 0xE0},  /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, TRUE, 5, 0x90, 0xE0},      /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, TRUE, 6, 0x90, 0xE0},  /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
-      /* repeated with non reference frame */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, TRUE, 6, 0x90, 0xE0},      /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+  /* repeated with non reference frame */
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x30, 0x80},   /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x30, 0x80},       /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, FALSE, FALSE, 3, 0xB0, 0x80},        /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, FALSE, FALSE, 3, 0xB0, 0x80},    /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, FALSE, FALSE, 4, 0xB0, 0x80},        /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, FALSE, FALSE, 4, 0xB0, 0x80},    /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0xB0, 0x60},    /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0xB0, 0x60},        /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, FALSE, 5, 0xB0, 0xE0}, /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, FALSE, 5, 0xB0, 0xE0},     /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, FALSE, 6, 0xB0, 0xE0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, FALSE, 6, 0xB0, 0xE0},     /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
   {
-  VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0xB0, 0x60},     /* no picture ID single byte header, S set */
+      VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0xB0, 0x60}, /* no picture ID single byte header, S set */
   {
-  VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, TRUE, 5, 0xB0, 0xE0},  /* X bit to allow for I bit means header is three bytes, S and X set */
+      VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, TRUE, 5, 0xB0, 0xE0},      /* X bit to allow for I bit means header is three bytes, S and X set */
   {
-  VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, TRUE, 6, 0xB0, 0xE0},  /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
+      VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, TRUE, 6, 0xB0, 0xE0},      /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
 };
 
 GST_START_TEST (test_pay_with_meta)
@@ -516,6 +514,64 @@ GST_START_TEST (test_pay_tl0picidx_split_buffer)
 
 GST_END_TEST;
 
+GST_START_TEST (test_pay_continuous_picture_id_on_flush)
+{
+  guint8 vp8_bitstream_payload[] = {
+    0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
+    0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
+  };
+  GstHarness *h = gst_harness_new ("rtpvp8pay");
+  const gint header_len = 3;
+  const gint packet_len = 12 + header_len + sizeof (vp8_bitstream_payload);
+  const gint picid_offset = 14;
+  GstBuffer *buffer;
+  GstMapInfo map;
+
+  g_object_set (h->element,
+      "picture-id-mode", VP8_PAY_PICTURE_ID_7BITS,
+      "picture-id-offset", 0, NULL);
+
+  gst_harness_set_src_caps_str (h, "video/x-vp8");
+
+  /* First, push a frame */
+  buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
+  buffer = gst_harness_push_and_pull (h, buffer);
+  fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
+  fail_unless_equals_uint64 (map.size, packet_len);
+  fail_unless_equals_int (map.data[picid_offset], 0x00);
+  gst_buffer_unmap (buffer, &map);
+  gst_buffer_unref (buffer);
+
+  /* Push another one and expect the PictureID to increment */
+  buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
+  buffer = gst_harness_push_and_pull (h, buffer);
+  fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
+  fail_unless_equals_uint64 (map.size, packet_len);
+  fail_unless_equals_int (map.data[picid_offset], 0x01);
+  gst_buffer_unmap (buffer, &map);
+  gst_buffer_unref (buffer);
+
+  /* Yet another frame followed by a FLUSH of the pipeline should result
+   * on an increase rather than a reset to maximize interop. */
+  fail_unless (gst_harness_push_event (h, gst_event_new_flush_start ()));
+  fail_unless (gst_harness_push_event (h, gst_event_new_flush_stop (FALSE)));
+  gst_harness_set_src_caps_str (h, "video/x-vp8");
+
+  buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
+  buffer = gst_harness_push_and_pull (h, buffer);
+  fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
+  fail_unless_equals_uint64 (map.size, packet_len);
+  /* PictureID should increment by 2
+   * One due to the FLUSH_START, and another one due to the new frame */
+  fail_unless_equals_int (map.data[picid_offset], 0x03);
+  gst_buffer_unmap (buffer, &map);
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
 typedef struct _DepayGapEventTestData
 {
   gint seq_num;
@@ -773,15 +829,51 @@ GST_START_TEST (test_depay_resend_gap_event)
 
 GST_END_TEST;
 
+GST_START_TEST (test_pay_delta_unit_flag)
+{
+  guint8 vp8_bitstream_payload[] = {
+    0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
+    0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88,
+    0x99, 0x84, 0x88, 0x21, 0x00
+  };
+
+  /* set mtu so that the buffer is split into multiple packets */
+  GstHarness *h = gst_harness_new_parse ("rtpvp8pay mtu=28");
+  GstFlowReturn ret;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h, "video/x-vp8");
+
+  buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
+      vp8_bitstream_payload, sizeof (vp8_bitstream_payload), 0,
+      sizeof (vp8_bitstream_payload), NULL, NULL);
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  /* the input buffer should be split into two buffers and pushed as a buffer
+   * list, only the first buffer of the first buffer list should be marked as a
+   * non-delta unit */
+  buffer = gst_harness_pull (h);
+  fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtpvp8_suite (void)
 {
   Suite *s = suite_create ("rtpvp8");
   TCase *tc_chain;
-  static const gchar *tags[] = { NULL };
 
   /* Register custom GstVP8Meta manually */
-  gst_meta_register_custom ("GstVP8Meta", tags, NULL, NULL, NULL);
+  gst_meta_register_custom_simple ("GstVP8Meta");
 
   suite_add_tcase (s, (tc_chain = tcase_create ("vp8pay")));
   tcase_add_loop_test (tc_chain, test_pay_no_meta, 0,
@@ -790,6 +882,10 @@ rtpvp8_suite (void)
       G_N_ELEMENTS (with_meta_test_data));
   tcase_add_test (tc_chain, test_pay_continuous_picture_id_and_tl0picidx);
   tcase_add_test (tc_chain, test_pay_tl0picidx_split_buffer);
+  tcase_add_test (tc_chain, test_pay_continuous_picture_id_on_flush);
+  tcase_add_test (tc_chain, test_pay_delta_unit_flag);
+
+  suite_add_tcase (s, (tc_chain = tcase_create ("vp8depay")));
   tcase_add_loop_test (tc_chain, test_depay_stop_gap_events, 0,
       G_N_ELEMENTS (stop_gap_events_test_data));
   tcase_add_loop_test (tc_chain, test_depay_resend_gap_event, 0,
diff --git a/tests/check/elements/rtpvp9.c b/tests/check/elements/rtpvp9.c
index db4a50b70667341aa19bd1200dd87aed342738fd..45c3f91f1131e75a75d11b525f28620cedf8e3fe 100644
--- a/tests/check/elements/rtpvp9.c
+++ b/tests/check/elements/rtpvp9.c
@@ -24,6 +24,7 @@
 
 #include <gst/check/check.h>
 #include <gst/check/gstharness.h>
+#include <gst/rtp/gstrtpbuffer.h>
 
 #define RTP_VP9_CAPS_STR \
   "application/x-rtp,media=video,encoding-name=VP9,clock-rate=90000,payload=96"
@@ -141,34 +142,34 @@ create_rtp_vp9_buffer_full (guint seqnum, guint picid, guint buffer_type,
     gint picid_bits;
   } templates[] = {
     {
-    intra_nopicid_seqnum0, sizeof (intra_nopicid_seqnum0), 0}
+        intra_nopicid_seqnum0, sizeof (intra_nopicid_seqnum0), 0}
     , {
-    intra_picid24_seqnum0, sizeof (intra_picid24_seqnum0), 7}
+        intra_picid24_seqnum0, sizeof (intra_picid24_seqnum0), 7}
     , {
-    intra_picid6336_seqnum0, sizeof (intra_picid6336_seqnum0), 15}
+        intra_picid6336_seqnum0, sizeof (intra_picid6336_seqnum0), 15}
     ,
-        /*
-           { intra_nopicid_seqnum0_tl1_sync_tl0picidx12,
-           sizeof (intra_nopicid_seqnum0_tl1_sync_tl0picidx12),
-           0
-           },
-           { intra_picid24_seqnum0_tl1_sync_tl0picidx12,
-           sizeof (intra_picid24_seqnum0_tl1_sync_tl0picidx12),
-           7
-           },
-           { intra_picid6336_seqnum0_tl1_sync_tl0picidx12,
-           sizeof (intra_picid6336_seqnum0_tl1_sync_tl0picidx12),
-           15
-           },
-           { intra_picid24_seqnum0_tl1_sync_no_tl0picidx,
-           sizeof (intra_picid24_seqnum0_tl1_sync_no_tl0picidx),
-           7
-           },
-           { intra_picid24_seqnum0_notyk_tl0picidx12,
-           sizeof (intra_picid24_seqnum0_notyk_tl0picidx12),
-           7
-           }
-         */
+    /*
+       { intra_nopicid_seqnum0_tl1_sync_tl0picidx12,
+       sizeof (intra_nopicid_seqnum0_tl1_sync_tl0picidx12),
+       0
+       },
+       { intra_picid24_seqnum0_tl1_sync_tl0picidx12,
+       sizeof (intra_picid24_seqnum0_tl1_sync_tl0picidx12),
+       7
+       },
+       { intra_picid6336_seqnum0_tl1_sync_tl0picidx12,
+       sizeof (intra_picid6336_seqnum0_tl1_sync_tl0picidx12),
+       15
+       },
+       { intra_picid24_seqnum0_tl1_sync_no_tl0picidx,
+       sizeof (intra_picid24_seqnum0_tl1_sync_no_tl0picidx),
+       7
+       },
+       { intra_picid24_seqnum0_notyk_tl0picidx12,
+       sizeof (intra_picid24_seqnum0_notyk_tl0picidx12),
+       7
+       }
+     */
   };
   struct BufferTemplate *template = &templates[buffer_type];
   guint8 *packet = g_memdup2 (template->template, template->size);
@@ -229,18 +230,9 @@ typedef struct _DepayGapEventTestData
   guint buffer_type;
 } DepayGapEventTestData;
 
-typedef struct
-{
-  gint seq_num;
-  gint picid;
-  guint buffer_type;
-  gboolean s_bit;
-  gboolean marker_bit;
-} DepayGapEventTestDataFull;
-
 static void
 test_depay_gap_event_base (const DepayGapEventTestData * data,
-    gboolean send_lost_event, gboolean expect_gap_event, int iter)
+    gboolean send_lost_event, gboolean expect_gap_event)
 {
   GstEvent *event;
   GstClockTime pts = 0;
@@ -314,8 +306,7 @@ static const DepayGapEventTestData stop_gap_events_test_data[][2] = {
 
 GST_START_TEST (test_depay_stop_gap_events)
 {
-  test_depay_gap_event_base (&stop_gap_events_test_data[__i__][0], TRUE, FALSE,
-      __i__);
+  test_depay_gap_event_base (&stop_gap_events_test_data[__i__][0], TRUE, FALSE);
 }
 
 GST_END_TEST;
@@ -340,8 +331,7 @@ static const DepayGapEventTestData resend_gap_event_test_data[][2] = {
 
 GST_START_TEST (test_depay_resend_gap_event)
 {
-  test_depay_gap_event_base (&resend_gap_event_test_data[__i__][0], TRUE, TRUE,
-      __i__);
+  test_depay_gap_event_base (&resend_gap_event_test_data[__i__][0], TRUE, TRUE);
 }
 
 GST_END_TEST;
@@ -450,11 +440,181 @@ GST_START_TEST (test_depay_svc_forgive_invalid_sid)
 
 GST_END_TEST;
 
+GST_START_TEST (test_pay_delta_unit_flag)
+{
+  guint8 vp9_bitstream_payload[] = {
+    0xa2, 0x49, 0x83, 0x42, 0x20, 0x00, 0x1e, 0x00,
+    0x1e, 0xc0, 0x07, 0x04, 0x83, 0x83, 0x08, 0x40,
+    0x00, 0x06, 0x60, 0x00, 0x00, 0x10, 0xbf, 0xff,
+    0x5a, 0x0f, 0xff, 0xff, 0xff, 0xfb, 0xc9, 0x83,
+    0xff, 0xff, 0xff, 0xff, 0x34, 0xca, 0x00
+  };
+
+  /* set mtu so that the buffer is split into multiple packets */
+  GstHarness *h = gst_harness_new_parse ("rtpvp9pay mtu=48");
+  GstFlowReturn ret;
+  GstBuffer *buffer;
+
+  gst_harness_set_src_caps_str (h, "video/x-vp9");
+
+  buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
+      vp9_bitstream_payload, sizeof (vp9_bitstream_payload), 0,
+      sizeof (vp9_bitstream_payload), NULL, NULL);
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  /* the input buffer should be split into two buffers and pushed as a buffer
+   * list, only the first buffer of the first buffer list should be marked as a
+   * non-delta unit */
+  buffer = gst_harness_pull (h);
+  fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+  buffer = gst_harness_pull (h);
+  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+  gst_buffer_unref (buffer);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+static void
+fail_unless_vp9_ss (GstBuffer * buf, gint width, gint height)
+{
+  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+  guint8 *payload;
+
+  /* check the SS, on the payloaded buffer */
+  gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
+  payload = gst_rtp_buffer_get_payload (&rtp);
+
+  /* Assume: buffer should be the start of the frame as well as a keyframe */
+  fail_unless_equals_int (8, (gint) (payload[0] & 0x08));
+  fail_unless_equals_int (0, (gint) (payload[0] & 0x40));
+
+  /* it also should have set its V bit as we always hard code a SS */
+  fail_unless_equals_int (2, (gint) (payload[0] & 0x02));
+
+  /* similarly, as we don't set the picture ID mode on the payloader,
+   * it should not have its I set */
+  fail_unless_equals_int (0, (gint) (payload[0] & 0x80));
+
+  /* Now assuming no picture ID signaled, we should find the SS right after */
+  /* *INDENT-OFF* */
+  fail_unless_equals_int (0x18,           (gint) payload[1]); /* N_S=0 Y=1 G=1 */
+  fail_unless_equals_int (width >> 8,     (gint) payload[2]);
+  fail_unless_equals_int (width & 0xFF,   (gint) payload[3]);
+  fail_unless_equals_int (height >> 8,    (gint) payload[4]);
+  fail_unless_equals_int (height & 0xFF,  (gint) payload[5]);
+  /* *INDENT-ON* */
+
+  fail_unless_equals_int (0x01, (gint) payload[6]);     /* N_G=1 */
+  fail_unless_equals_int (0x04, (gint) payload[7]);     /* T=0 U=0 R=1 */
+  fail_unless_equals_int (0x01, (gint) payload[8]);     /* P_DIFF=1 */
+
+  gst_rtp_buffer_unmap (&rtp);
+}
+
+static void
+test_pay_ss_resolution_profiles (guint8 payload[], gint payload_len)
+{
+  GstHarness *h = gst_harness_new_parse ("rtpvp9pay");
+  GstFlowReturn ret;
+  GstBuffer *buffer, *out;
+
+  gst_harness_set_src_caps_str (h, "video/x-vp9");
+
+  buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
+      payload, payload_len, 0, payload_len, NULL, NULL);
+
+  ret = gst_harness_push (h, buffer);
+  fail_unless_equals_int (ret, GST_FLOW_OK);
+
+  out = gst_harness_pull (h);
+  fail_if (out == NULL);
+  fail_unless_vp9_ss (out, 16, 16);
+  gst_buffer_unref (out);
+
+  gst_harness_teardown (h);
+}
+
+GST_START_TEST (test_pay_ss_resolution_profile_0)
+{
+  // keyframe bitstream for profile 0, with 16x16 frame size
+  guint8 payload[] =
+      { 0x82, 0x49, 0x83, 0x42, 0x20, 0x0, 0xf0, 0x0, 0xf6, 0x0, 0x38, 0x24,
+    0x1c, 0x18, 0x42, 0x0, 0x0, 0x33, 0x0, 0x0, 0x0, 0x85, 0xff, 0xfa, 0xd0,
+    0x7f, 0xff, 0xff, 0xff, 0xde, 0x4c, 0x1f, 0xff, 0xff, 0xff, 0xf9, 0xa6,
+    0x50, 0x0
+  };
+  test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_pay_ss_resolution_profile_1)
+{
+  // keyframe bitstream for profile 1, with 16x16 frame size
+  guint8 payload[] = {
+    0xa2, 0x49, 0x83, 0x42, 0x20, 0x00, 0x1e, 0x00,
+    0x1e, 0xc0, 0x07, 0x04, 0x83, 0x83, 0x08, 0x40,
+    0x00, 0x06, 0x60, 0x00, 0x00, 0x10, 0xbf, 0xff,
+    0x5a, 0x0f, 0xff, 0xff, 0xff, 0xfb, 0xc9, 0x83,
+    0xff, 0xff, 0xff, 0xff, 0x34, 0xca, 0x00
+  };
+  test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_pay_ss_resolution_profile_2)
+{
+  // keyframe bitstream for profile 2, with 16x16 frame size
+  guint8 payload[] = {
+    0x92, 0x49, 0x83, 0x42, 0x10, 0x00, 0x78, 0x00,
+    0x7b, 0x00, 0x1c, 0x12, 0x0e, 0x0c, 0x21, 0x00,
+    0x00, 0x19, 0x80, 0x00, 0x00, 0x42, 0xff, 0xfd,
+    0x68, 0x3f, 0xff, 0xff, 0xff, 0xef, 0x26, 0x0f,
+    0xff, 0xff, 0xff, 0xfc, 0xd3, 0x28, 0x00
+  };
+  test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_pay_ss_resolution_profile_3)
+{
+  // keyframe bitstream for profile 3, with 16x16 frame size
+  guint8 payload[] =
+      { 0xb1, 0x24, 0xc1, 0xa1, 0x8, 0x0, 0x7, 0x80, 0x7, 0xb0, 0x1, 0xc1, 0x20,
+    0xe0, 0xc2, 0x10, 0x0, 0x1, 0x98, 0x0, 0x0, 0x4, 0x2f, 0xff, 0xd6, 0x83,
+    0xff, 0xff, 0xff, 0xfe, 0xf2, 0x60, 0xff, 0xff, 0xff, 0xff, 0xcd, 0x32,
+    0x80, 0x0
+  };
+  test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
+}
+
+GST_END_TEST;
+
+
 static Suite *
 rtpvp9_suite (void)
 {
   Suite *s = suite_create ("rtpvp9");
   TCase *tc_chain;
+
+  suite_add_tcase (s, (tc_chain = tcase_create ("vp9pay")));
+  tcase_add_test (tc_chain, test_pay_delta_unit_flag);
+
+  suite_add_tcase (s, (tc_chain =
+          tcase_create ("vp9pay-ss-resolution-profile")));
+  tcase_add_test (tc_chain, test_pay_ss_resolution_profile_0);
+  tcase_add_test (tc_chain, test_pay_ss_resolution_profile_1);
+  tcase_add_test (tc_chain, test_pay_ss_resolution_profile_2);
+  tcase_add_test (tc_chain, test_pay_ss_resolution_profile_3);
+
   suite_add_tcase (s, (tc_chain = tcase_create ("vp9depay")));
   tcase_add_test (tc_chain, test_depay_flexible_mode);
   tcase_add_test (tc_chain, test_depay_non_flexible_mode);
diff --git a/tests/check/elements/udpsrc.c b/tests/check/elements/udpsrc.c
index bdff756c4f5756e62c864f140d603fee339cb347..fe8aa15deb3a3ba6467528f2cf7c02c4c579e77f 100644
--- a/tests/check/elements/udpsrc.c
+++ b/tests/check/elements/udpsrc.c
@@ -247,6 +247,55 @@ send_failure:
 
 GST_END_TEST;
 
+static void
+on_multicast_source_updated (GObject * src, GParamSpec * pspec, guint * count)
+{
+  *count += 1;
+}
+
+GST_START_TEST (test_udpsrc_multicast_source)
+{
+  GstElement *src;
+  guint count = 0;
+  gchar *multicast_source = NULL;
+
+  src = gst_check_setup_element ("udpsrc");
+
+  g_signal_connect (G_OBJECT (src), "notify::multicast-source",
+      (GCallback) on_multicast_source_updated, &count);
+
+  /* Set uri without multicast-source */
+  g_object_set (src, "uri", "udp://127.0.0.1:5004", NULL);
+  fail_unless_equals_int (count, 0);
+  g_object_get (src, "multicast-source", &multicast_source, NULL);
+  fail_unless (multicast_source == NULL);
+
+  /* Sets source filter explicitly */
+  g_object_set (src, "multicast-source", "+127.0.0.2+127.0.0.3", NULL);
+  fail_unless_equals_int (count, 1);
+  g_object_get (src, "multicast-source", &multicast_source, NULL);
+  fail_unless_equals_string (multicast_source, "+127.0.0.2+127.0.0.3");
+  g_clear_pointer (&multicast_source, g_free);
+
+  /* Uri with source filters */
+  g_object_set (src, "uri", "udp://127.0.0.1:5004?multicast-source=+127.0.0.2",
+      NULL);
+  fail_unless_equals_int (count, 2);
+  g_object_get (src, "multicast-source", &multicast_source, NULL);
+  fail_unless_equals_string (multicast_source, "+127.0.0.2");
+  g_clear_pointer (&multicast_source, g_free);
+
+  /* New uri will reset source filters */
+  g_object_set (src, "uri", "udp://127.0.0.1:5004", NULL);
+  fail_unless_equals_int (count, 3);
+  g_object_get (src, "multicast-source", &multicast_source, NULL);
+  fail_unless (multicast_source == NULL);
+
+  gst_object_unref (src);
+}
+
+GST_END_TEST;
+
 static Suite *
 udpsrc_suite (void)
 {
@@ -256,6 +305,8 @@ udpsrc_suite (void)
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_udpsrc_empty_packet);
   tcase_add_test (tc_chain, test_udpsrc);
+  tcase_add_test (tc_chain, test_udpsrc_multicast_source);
+
   return s;
 }
 
diff --git a/tests/check/elements/videocrop.c b/tests/check/elements/videocrop.c
index b7378384bfbd57fead702d44d08d7c1fe4af8a37..2a0e73cb82db740e520171e19c3c6d252b2fcf26 100644
--- a/tests/check/elements/videocrop.c
+++ b/tests/check/elements/videocrop.c
@@ -99,14 +99,14 @@ GST_START_TEST (test_unit_sizes)
       gint width, height;
     } sizes_to_try[] = {
       {
-      160, 120}, {
-      161, 120}, {
-      160, 121}, {
-      161, 121}, {
-      159, 120}, {
-      160, 119}, {
-      159, 119}, {
-      159, 121}
+          160, 120}, {
+          161, 120}, {
+          160, 121}, {
+          161, 121}, {
+          159, 120}, {
+          160, 119}, {
+          159, 119}, {
+          159, 121}
     };
     GstStructure *s;
     GstCaps *caps;
@@ -406,14 +406,14 @@ GST_START_TEST (test_cropping)
     gint width, height;
   } sizes_to_try[] = {
     {
-    160, 160}, {
-    161, 160}, {
-    160, 161}, {
-    161, 161}, {
-    159, 160}, {
-    160, 159}, {
-    159, 159}, {
-    159, 161}
+        160, 160}, {
+        161, 160}, {
+        160, 161}, {
+        161, 161}, {
+        159, 160}, {
+        160, 159}, {
+        159, 159}, {
+        159, 161}
   };
   GList *caps_list, *node;
   gint i;
diff --git a/tests/check/elements/videofilter.c b/tests/check/elements/videofilter.c
index 2b59d15c4290ff6a56fde8a9299eeb8d70370f6e..851e9f22ebe29be4ed45f45ede3ad5cedde8fa7e 100644
--- a/tests/check/elements/videofilter.c
+++ b/tests/check/elements/videofilter.c
@@ -140,9 +140,10 @@ check_filter_varargs (const gchar * name, GstEvent * event, gint num_buffers,
   {
     const int width, height;
   } resolutions[] = { {
-  384, 288}, {
-  385, 289}, {
-  385, 385}};
+      384, 288}, {
+      385, 289}, {
+      385, 385}
+  };
   gint i, n, r;
   gint size;
   GstCaps *allcaps, *templ = gst_caps_from_string (VIDEO_CAPS_TEMPLATE_STRING);
diff --git a/tests/check/elements/videoflip.c b/tests/check/elements/videoflip.c
index 2887703a907002753be4464baec9905c80e94e00..a0bf9084b81b731ec8648b37d714efd67ca91c0d 100644
--- a/tests/check/elements/videoflip.c
+++ b/tests/check/elements/videoflip.c
@@ -268,6 +268,214 @@ GST_START_TEST (test_stress_change_method)
 
 GST_END_TEST;
 
+// push a buffer to retrieve the new caps from videoflip and check if the frame is rotated or not
+static void
+caps_update (GstHarness * flip, GstVideoInfo * in_info, gboolean rotate)
+{
+  GstEvent *e;
+  GstBuffer *buf;
+  GstCaps *out_caps;
+  GstVideoInfo out_info;
+
+  // push a buffer to get the new caps
+  buf = create_test_video_buffer_rgba8 (in_info);
+  fail_unless (gst_harness_push (flip, buf) == GST_FLOW_OK);
+
+  e = gst_harness_pull_event (flip);
+  gst_event_parse_caps (e, &out_caps);
+  gst_caps_ref (out_caps);
+  gst_event_unref (e);
+
+  buf = gst_harness_pull (flip);
+  fail_unless (buf != NULL);
+  gst_buffer_unref (buf);
+
+  fail_unless (gst_video_info_from_caps (&out_info, out_caps));
+
+  if (rotate) {
+    fail_unless_equals_int (GST_VIDEO_INFO_WIDTH (in_info),
+        GST_VIDEO_INFO_HEIGHT (&out_info));
+    fail_unless_equals_int (GST_VIDEO_INFO_HEIGHT (in_info),
+        GST_VIDEO_INFO_WIDTH (&out_info));
+  } else {
+    fail_unless_equals_int (GST_VIDEO_INFO_WIDTH (in_info),
+        GST_VIDEO_INFO_WIDTH (&out_info));
+    fail_unless_equals_int (GST_VIDEO_INFO_HEIGHT (in_info),
+        GST_VIDEO_INFO_HEIGHT (&out_info));
+  }
+
+  gst_caps_unref (out_caps);
+}
+
+static void
+send_orientation_tag (GstHarness * flip, const gchar * orientation,
+    GstTagScope scope)
+{
+  GstTagList *tags;
+  gchar *tmp;
+  GstEvent *e;
+
+  if (orientation) {
+    tmp = g_strdup_printf ("taglist,image-orientation=%s", orientation);
+  } else {
+    tmp = g_strdup ("taglist");
+  }
+
+  tags = gst_tag_list_new_from_string (tmp);
+  g_free (tmp);
+  fail_unless (tags != NULL);
+  gst_tag_list_set_scope (tags, scope);
+  gst_harness_push_event (flip, gst_event_new_tag (tags));
+
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_TAG);
+  gst_event_unref (e);
+}
+
+// set orientation from tags with videoflip in auto mode
+GST_START_TEST (test_orientation_tag)
+{
+  GstHarness *flip = gst_harness_new ("videoflip");
+  GstVideoInfo in_info, out_info;
+  GstCaps *in_caps, *out_caps;
+  GstEvent *e;
+
+  g_object_set (flip->element, "video-direction", 8 /* auto */ , NULL);
+
+  // downstream accept any resolution
+  gst_harness_set_sink_caps_str (flip, "video/x-raw");
+
+  gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_RGBA, 4, 9);
+  in_caps = gst_video_info_to_caps (&in_info);
+  gst_harness_set_src_caps (flip, in_caps);
+
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_STREAM_START);
+  gst_event_unref (e);
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_CAPS);
+  gst_event_parse_caps (e, &out_caps);
+  fail_unless (gst_video_info_from_caps (&out_info, out_caps));
+  fail_unless_equals_int (GST_VIDEO_INFO_WIDTH (&in_info),
+      GST_VIDEO_INFO_WIDTH (&out_info));
+  fail_unless_equals_int (GST_VIDEO_INFO_HEIGHT (&in_info),
+      GST_VIDEO_INFO_HEIGHT (&out_info));
+  gst_event_unref (e);
+
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_SEGMENT);
+  gst_event_unref (e);
+
+  send_orientation_tag (flip, "rotate-90", GST_TAG_SCOPE_STREAM);
+
+  // caps is updated as the frame is now rotated
+  caps_update (flip, &in_info, TRUE);
+
+  // orientation is reset on STREAM_START
+  gst_harness_push_event (flip, gst_event_new_stream_start ("2"));
+
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_STREAM_START);
+  gst_event_unref (e);
+
+  caps_update (flip, &in_info, FALSE);
+
+  gst_harness_teardown (flip);
+}
+
+GST_END_TEST;
+
+// send a buffer and ensure caps have not been updated
+static void
+caps_not_updated (GstHarness * flip, GstVideoInfo * in_info)
+{
+  GstBuffer *buf;
+  GstEvent *e;
+
+  buf = create_test_video_buffer_rgba8 (in_info);
+  buf = gst_harness_push_and_pull (flip, buf);
+  fail_unless (buf != NULL);
+  gst_buffer_unref (buf);
+
+  // caps is not updated
+  e = gst_harness_try_pull_event (flip);
+  fail_unless (e == NULL);
+}
+
+// receive orientation updates from tags with the global and stream scopes
+GST_START_TEST (test_orientation_tag_scopes)
+{
+  GstHarness *flip = gst_harness_new ("videoflip");
+  GstVideoInfo in_info, out_info;
+  GstCaps *in_caps, *out_caps;
+  GstEvent *e;
+
+  g_object_set (flip->element, "video-direction", 8 /* auto */ , NULL);
+
+  // downstream accept any resolution
+  gst_harness_set_sink_caps_str (flip, "video/x-raw");
+
+  gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_RGBA, 4, 9);
+  in_caps = gst_video_info_to_caps (&in_info);
+  gst_harness_set_src_caps (flip, in_caps);
+
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_STREAM_START);
+  gst_event_unref (e);
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_CAPS);
+  gst_event_parse_caps (e, &out_caps);
+  fail_unless (gst_video_info_from_caps (&out_info, out_caps));
+  fail_unless_equals_int (GST_VIDEO_INFO_WIDTH (&in_info),
+      GST_VIDEO_INFO_WIDTH (&out_info));
+  fail_unless_equals_int (GST_VIDEO_INFO_HEIGHT (&in_info),
+      GST_VIDEO_INFO_HEIGHT (&out_info));
+  gst_event_unref (e);
+
+  e = gst_harness_pull_event (flip);
+  fail_unless_equals_int (GST_EVENT_TYPE (e), GST_EVENT_SEGMENT);
+  gst_event_unref (e);
+
+  // send orientation global tag (global: 90, stream: /)
+  send_orientation_tag (flip, "rotate-90", GST_TAG_SCOPE_GLOBAL);
+  // caps is updated as the frame is now rotated
+  caps_update (flip, &in_info, TRUE);
+
+  // send orientation stream tag, overriding the global one (global: 90, stream: 0)
+  send_orientation_tag (flip, "rotate-0", GST_TAG_SCOPE_STREAM);
+  // caps is updated as the frame is no longer rotated
+  caps_update (flip, &in_info, FALSE);
+
+  // resend orientation global tag, which won't change the orientation as the stream tag takes precedence (global: 90, stream: 0)
+  send_orientation_tag (flip, "rotate-90", GST_TAG_SCOPE_GLOBAL);
+  caps_not_updated (flip, &in_info);
+
+  // actually update the orientation with the stream tag (global: 90, stream: 90)
+  send_orientation_tag (flip, "rotate-90", GST_TAG_SCOPE_STREAM);
+  // caps is updated as the frame is now rotated
+  caps_update (flip, &in_info, TRUE);
+
+  // sending a stream tag without orientation switch back to the global one, so no orientation change (global: 90, stream: /)
+  send_orientation_tag (flip, NULL, GST_TAG_SCOPE_STREAM);
+  caps_not_updated (flip, &in_info);
+
+  // remove orientation from global tag, restoring identity (global: /, stream: /)
+  send_orientation_tag (flip, NULL, GST_TAG_SCOPE_GLOBAL);
+  caps_update (flip, &in_info, FALSE);
+
+  // send rotation in stream tag (global: /, stream: 90)
+  send_orientation_tag (flip, "rotate-90", GST_TAG_SCOPE_STREAM);
+  caps_update (flip, &in_info, TRUE);
+
+  // sending a global tag without orientation does not change the rotation (global: /, stream: 90)
+  send_orientation_tag (flip, NULL, GST_TAG_SCOPE_GLOBAL);
+  caps_not_updated (flip, &in_info);
+
+  gst_harness_teardown (flip);
+}
+
+GST_END_TEST;
+
 static Suite *
 videoflip_suite (void)
 {
@@ -280,6 +488,8 @@ videoflip_suite (void)
   tcase_add_test (tc_chain,
       test_change_method_twice_same_caps_different_method);
   tcase_add_test (tc_chain, test_stress_change_method);
+  tcase_add_test (tc_chain, test_orientation_tag);
+  tcase_add_test (tc_chain, test_orientation_tag_scopes);
 
   return s;
 }
diff --git a/tests/check/elements/vp8enc.c b/tests/check/elements/vp8enc.c
index 3221d7885cc663c7670aa4ce93a963d15e479f5b..6ac36650f327f188f4e1a4452bda4f71b19ea5ef 100644
--- a/tests/check/elements/vp8enc.c
+++ b/tests/check/elements/vp8enc.c
@@ -219,10 +219,8 @@ GST_END_TEST;
     guint temporal_layer_id, tl0picidx;                                  \
     GstCustomMeta *meta = gst_buffer_get_custom_meta (buffer,            \
         "GstVP8Meta");                                                   \
-    GstStructure *s;                                                     \
     fail_unless (meta != NULL);                                          \
-    s = gst_custom_meta_get_structure (meta);                            \
-    fail_unless (gst_structure_get (s,                                   \
+    fail_unless (gst_structure_get (meta->structure,                     \
           "use-temporal-scaling", G_TYPE_BOOLEAN, &use_temporal_scaling, \
           "layer-sync", G_TYPE_BOOLEAN, &layer_sync,                     \
           "layer-id", G_TYPE_UINT, &temporal_layer_id,                   \
@@ -242,7 +240,8 @@ configure_vp8ts (GstHarness * h)
   GValueArray *layer_ids = g_value_array_new (4);
   GValueArray *bitrates = g_value_array_new (3);
   GValue ival = { 0, }, bval = {
-  0,};
+    0,
+  };
 
   gst_value_array_init (&layer_sync_flags, 8);
 
@@ -331,23 +330,24 @@ GST_START_TEST (test_encode_temporally_scaled)
     gboolean droppable;
   } expected[] = {
     {
-    TRUE, 0, 1, FALSE},         /* This is an intra */
+        TRUE, 0, 1, FALSE},     /* This is an intra */
     {
-    TRUE, 2, 1, TRUE}, {
-    TRUE, 1, 1, FALSE}, {
-    FALSE, 2, 1, TRUE}, {
-    FALSE, 0, 2, FALSE}, {
-    FALSE, 2, 2, TRUE}, {
-    FALSE, 1, 2, FALSE}, {
-    FALSE, 2, 2, TRUE}, {
-    FALSE, 0, 3, FALSE}, {
-    TRUE, 2, 3, TRUE}, {
-    TRUE, 1, 3, FALSE}, {
-    FALSE, 2, 3, TRUE}, {
-    FALSE, 0, 4, FALSE}, {
-    FALSE, 2, 4, TRUE}, {
-    FALSE, 1, 4, FALSE}, {
-  FALSE, 2, 4, TRUE},};
+        TRUE, 2, 1, TRUE}, {
+        TRUE, 1, 1, FALSE}, {
+        FALSE, 2, 1, TRUE}, {
+        FALSE, 0, 2, FALSE}, {
+        FALSE, 2, 2, TRUE}, {
+        FALSE, 1, 2, FALSE}, {
+        FALSE, 2, 2, TRUE}, {
+        FALSE, 0, 3, FALSE}, {
+        TRUE, 2, 3, TRUE}, {
+        TRUE, 1, 3, FALSE}, {
+        FALSE, 2, 3, TRUE}, {
+        FALSE, 0, 4, FALSE}, {
+        FALSE, 2, 4, TRUE}, {
+        FALSE, 1, 4, FALSE}, {
+        FALSE, 2, 4, TRUE},
+  };
   GstHarness *h = gst_harness_new ("vp8enc");
   gst_harness_set_src_caps (h, gst_caps_new_i420 (320, 240));
   configure_vp8ts (h);
@@ -384,7 +384,6 @@ GST_START_TEST (test_encode_fresh_meta)
   GstBuffer *buffer;
   GstHarness *h = gst_harness_new ("vp8enc");
   GstCustomMeta *meta;
-  GstStructure *s;
   gst_harness_set_src_caps (h, gst_caps_new_i420_full (320, 240, 25, 1, 1, 1));
 
   buffer = gst_harness_create_video_buffer_full (h, 0x0,
@@ -393,8 +392,7 @@ GST_START_TEST (test_encode_fresh_meta)
 
   /* Attach bogus meta to input buffer */
   meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
-  s = gst_custom_meta_get_structure (meta);
-  gst_structure_set (s,
+  gst_structure_set (meta->structure,
       "use-temporal-scaling", G_TYPE_BOOLEAN, FALSE,
       "layer-sync", G_TYPE_BOOLEAN, FALSE,
       "layer-id", G_TYPE_UINT, 0, "tl0picidx", G_TYPE_UINT, 0, NULL);
diff --git a/tests/check/meson.build b/tests/check/meson.build
index 0626837fa977df48bacc7727536b4b3b28b3a816..2fe0b00e23f4fba8679782ca525f9951ee3ebea1 100644
--- a/tests/check/meson.build
+++ b/tests/check/meson.build
@@ -92,6 +92,7 @@ if not get_option('rtp').disabled() and not get_option('rtpmanager').disabled()
     [ 'elements/rtph264' ],
     [ 'elements/rtph265' ],
     [ 'elements/rtpopus' ],
+    [ 'elements/rtppassthrough' ],
     [ 'elements/rtpvp8' ],
     [ 'elements/rtpvp9' ],
     [ 'elements/rtpbin' ],
@@ -127,15 +128,10 @@ endif
 # FIXME: valgrind elements/rtp-payloading - needs fixing
 # elements/videocrop should be disabled since it takes way too long in valgrind
 
-libsoup2_dep = dependency('libsoup-2.4', version : '>=2.48',
-                          required : false, fallback : ['libsoup', 'libsoup_dep'],
-                          default_options: ['sysprof=disabled'])
-libsoup3_dep = dependency('libsoup-3.0', required : false,
-                           fallback : ['libsoup3', 'libsoup_dep'])
-
 # FIXME: unistd dependency or not tested yet on windows
 if host_machine.system() != 'windows'
   good_tests += [
+    [ 'elements/amrnbenc', not amrnb_dep.found() ],
     [ 'elements/dash_mpd', not adaptivedemux2_dep.found(), [adaptivedemux2_dep] ],
     [ 'pipelines/flacdec', not flac_dep.found() ],
     [ 'elements/gdkpixbufsink', not gdkpixbuf_dep.found(), [gdkpixbuf_dep] ],
@@ -143,8 +139,6 @@ if host_machine.system() != 'windows'
     [ 'elements/jpegdec', not jpeglib.found() ],
     [ 'elements/jpegenc', not jpeglib.found() ],
     [ 'elements/mpg123audiodec', not mpg123_dep.found(), [gstfft_dep]],
-    [ 'elements/souphttpsrc', not libsoup2_dep.found(), [libsoup2_dep], [], 'elements/souphttpsrc2'],
-    [ 'elements/souphttpsrc', not libsoup3_dep.found(), [libsoup3_dep], [], 'elements/souphttpsrc3'],
     [ 'elements/id3v2mux', not taglib_dep.found() ],
     [ 'elements/apev2mux', not taglib_dep.found() ],
     [ 'elements/vp8enc', not vpx_dep.found() or not have_vp8_encoder ],
@@ -157,11 +151,20 @@ if host_machine.system() != 'windows'
   ]
 endif
 
+if get_option('soup').allowed()
+  if libsoup3_dep.found()
+    good_tests += [['elements/souphttpsrc', false, [libsoup3_dep], []]]
+  elif libsoup2_dep.found()
+    good_tests += [['elements/souphttpsrc', false, [libsoup2_dep], []]]
+  endif
+endif
+
+fsmod = import('fs')
 test_defines = [
   '-UG_DISABLE_ASSERT',
   '-UG_DISABLE_CAST_CHECKS',
   '-DGST_CHECK_TEST_ENVIRONMENT_BEACON="GST_PLUGIN_LOADING_WHITELIST"',
-  '-DGST_TEST_FILES_PATH="' + meson.current_source_dir() + '/../files"',
+  '-DGST_TEST_FILES_PATH="' + fsmod.as_posix(meson.current_source_dir()) + '/../files"',
   '-DGST_USE_UNSTABLE_API',
 ]
 
@@ -201,7 +204,7 @@ test_deps = [gst_dep, gstbase_dep, gstnet_dep, gstcheck_dep, gstaudio_dep,
 # FIXME: add valgrind suppression common/gst.supp gst-plugins-good.supp
 foreach t : good_tests
   fname = '@0@.c'.format(t.get(0))
-  test_name = t.get(4, t.get(0)).underscorify()
+  test_name = t.get(0).underscorify()
   extra_sources = t.get(3, [ ])
   extra_deps = t.get(2, [ ])
   skip_test = t.get(1, false)
diff --git a/tests/examples/gtk/gtkhttpsrc.c b/tests/examples/gtk/gtkhttpsrc.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2c05bff46802335fe79b3c3e265fd55107da75d
--- /dev/null
+++ b/tests/examples/gtk/gtkhttpsrc.c
@@ -0,0 +1,369 @@
+/*
+ * GStreamer
+ * Copyright (C) 2023 Arnaud Rebillout <elboulangero@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+// gcc gtkhttpsrc.c $(pkg-config --cflags --libs gstreamer-1.0 gtk+-3.0)
+
+#include <gtk/gtk.h>
+#include <gst/gst.h>
+
+//#define DEBUG_GST_BUS_MESSAGE_ELEMENT
+
+static GstElement *playbin;
+
+static gboolean accept_bad_certificates = FALSE;        // whether we accept bad certs
+static gchar *stream_uri;       // the stream we want to play
+static gchar *redirection_uri;  // set if there was a redirection
+static gchar *cert_errors;      // errors with the certificate
+
+/*
+ * Helpers
+ */
+
+static gchar *
+tls_errors_to_string (GTlsCertificateFlags errors)
+{
+  GPtrArray *a;
+  gchar *res;
+
+  a = g_ptr_array_new_full (2, NULL);
+
+  if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA)
+    g_ptr_array_add (a, (gchar *) "unknown-ca");
+  if (errors & G_TLS_CERTIFICATE_BAD_IDENTITY)
+    g_ptr_array_add (a, (gchar *) "bad-identity");
+  if (errors & G_TLS_CERTIFICATE_NOT_ACTIVATED)
+    g_ptr_array_add (a, (gchar *) "not-activated");
+  if (errors & G_TLS_CERTIFICATE_EXPIRED)
+    g_ptr_array_add (a, (gchar *) "expired");
+  if (errors & G_TLS_CERTIFICATE_REVOKED)
+    g_ptr_array_add (a, (gchar *) "revoked");
+  if (errors & G_TLS_CERTIFICATE_INSECURE)
+    g_ptr_array_add (a, (gchar *) "insecure");
+
+  if (a->len > 0) {
+    g_ptr_array_add (a, NULL);
+    res = g_strjoinv (", ", (gchar **) a->pdata);
+  } else
+    res = g_strdup ("unknown error");
+
+  g_ptr_array_free (a, TRUE);
+
+  return res;
+}
+
+static void
+start_playback (void)
+{
+  g_print ("Start playback: %s\n", stream_uri);
+  g_object_set (playbin, "uri", stream_uri, NULL);
+  gst_element_set_state (GST_ELEMENT (playbin), GST_STATE_PLAYING);
+}
+
+static void
+stop_playback (void)
+{
+  g_print ("Stop playback\n");
+  gst_element_set_state (GST_ELEMENT (playbin), GST_STATE_NULL);
+}
+
+/*
+ * Main window - Enter the URL of the stream and play it
+ */
+
+static GtkWidget *main_window;
+
+static void
+button_cb (GtkWidget * widget, GtkEntry * entry)
+{
+  const gchar *uri;
+
+  uri = gtk_entry_get_text (entry);
+
+  // quick check - the purpose of this example is to play web radios
+  if (g_str_has_prefix (uri, "http") == FALSE) {
+    g_print ("Invalid entry, must start with 'http'\n");
+    return;
+  }
+  // only one click allowed - this is just an example!
+  gtk_widget_set_sensitive (widget, FALSE);
+
+  // save in global variable
+  stream_uri = g_strdup (uri);
+
+  start_playback ();
+}
+
+static void
+show_main_window (void)
+{
+  GtkWidget *window;
+  GtkWidget *grid;
+  GtkWidget *entry;
+  GtkWidget *button;
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (window), 640, -1);
+
+  grid = gtk_grid_new ();
+  gtk_container_add (GTK_CONTAINER (window), grid);
+
+  entry = gtk_entry_new ();
+  gtk_widget_set_hexpand (entry, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), entry, 0, 0, 1, 1);
+
+  button = gtk_button_new_with_label ("> Play");
+  gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 1, 1);
+
+  g_signal_connect (button, "clicked", G_CALLBACK (button_cb), entry);
+  g_signal_connect (window, "delete-event", gtk_main_quit, NULL);
+
+  gtk_widget_show_all (window);
+
+  // save in global variable
+  main_window = window;
+}
+
+/*
+ * Dialog - Whether to play a stream when the certifiate is invalid
+ */
+
+static GtkWidget *dialog;
+
+static void
+button_yes_cb (GtkWidget * widget, gpointer data)
+{
+  accept_bad_certificates = TRUE;
+  start_playback ();
+  gtk_widget_destroy (dialog);
+  dialog = NULL;
+}
+
+static void
+show_dialog (void)
+{
+  GtkWidget *window;
+  GtkWidget *grid;
+  GtkWidget *box;
+  GtkWidget *label;
+  GtkWidget *button;
+  gchar *str;
+
+  // BEWARE! We can't gtk_dialog_run(), which would block the world, and
+  // prevent us from receiving other signals from GStreamer. In particular,
+  // if there was a redirection, we might not know it at this point.
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (main_window));
+
+  grid = gtk_grid_new ();
+  gtk_container_add (GTK_CONTAINER (window), grid);
+
+  str = g_strdup_printf ("Bad certificate: %s", cert_errors);
+  label = gtk_label_new (str);
+  g_free (str);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+
+  str = g_strdup_printf ("Stream URI: %s", stream_uri);
+  label = gtk_label_new (str);
+  g_free (str);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
+
+  str = g_strdup_printf ("Redirection URI: %s", redirection_uri ?
+      redirection_uri : "not redirected");
+  label = gtk_label_new (str);
+  g_free (str);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
+
+  label = gtk_label_new ("Play the stream anyway?");
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 2, 1);
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_grid_attach (GTK_GRID (grid), box, 0, 4, 1, 1);
+
+  button = gtk_button_new_with_label ("No");
+  g_signal_connect (button, "clicked", G_CALLBACK (gtk_main_quit), NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
+
+  button = gtk_button_new_with_label ("Yes");
+  g_signal_connect (button, "clicked", G_CALLBACK (button_yes_cb), NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
+
+  gtk_widget_show_all (window);
+
+  // save in global variable
+  dialog = window;
+}
+
+static void
+dialog_update (void)
+{
+  GtkWidget *grid;
+  GtkWidget *label;
+  gchar *str;
+
+  if (dialog == NULL)
+    return;
+
+  str = g_strdup_printf ("Redirection URI: %s", redirection_uri ?
+      redirection_uri : "not redirected");
+  grid = gtk_bin_get_child (GTK_BIN (dialog));
+  label = gtk_grid_get_child_at (GTK_GRID (grid), 0, 2);
+  gtk_label_set_text (GTK_LABEL (label), str);
+  g_free (str);
+}
+
+/*
+ * GStreamer things
+ */
+
+static gboolean
+idle_cb (gpointer user_data)
+{
+  show_dialog ();
+  return G_SOURCE_REMOVE;
+}
+
+static gboolean
+accept_certificate_cb (GstElement * source, GTlsCertificate * tls_certificate,
+    GTlsCertificateFlags tls_errors, gpointer user_data)
+{
+  gchar *errors;
+
+  errors = tls_errors_to_string (tls_errors);
+
+  // save in global variables
+  if (cert_errors)
+    g_free (cert_errors);
+  cert_errors = errors;
+
+  g_print ("Bad certificate: %s - %s\n", errors,
+      accept_bad_certificates ? "accepting" : "rejecting");
+
+  // Inform user that the certificate is invalid, and ask what to do.
+  // BEWARE! We're in the GStreamer streaming thread, we can't touch
+  // the GUI!
+  if (accept_bad_certificates == FALSE) {
+    //show_dialog(); // <- can't do that!
+    g_idle_add (idle_cb, NULL);
+  }
+
+  return accept_bad_certificates;
+}
+
+static void
+source_setup_cb (GstElement * playbin, GstElement * source, gpointer user_data)
+{
+  const gchar *name = G_OBJECT_TYPE_NAME (source);
+
+  if (g_signal_lookup ("accept-certificate", G_OBJECT_TYPE (source)) != 0) {
+    g_print ("Source %s has signal accept-certificate - connecting\n", name);
+    g_signal_connect (source, "accept-certificate",
+        G_CALLBACK (accept_certificate_cb), 0);
+  } else {
+    g_print ("Source %s does NOT have signal accept-certificate\n", name);
+  }
+}
+
+static void
+message_element_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+  const GstStructure *s;
+  const gchar *name;
+  const gchar *uri;
+
+  s = gst_message_get_structure (msg);
+  if (s == NULL)
+    return;
+
+  name = gst_structure_get_name (s);
+  if (g_strcmp0 (name, "http-headers") != 0)
+    return;
+
+#ifdef DEBUG_GST_BUS_MESSAGE_ELEMENT
+  gst_println ("%" GST_PTR_FORMAT, s);
+#endif
+
+  if (gst_structure_has_field (s, "redirection-uri") == FALSE)
+    return;
+
+  uri = gst_structure_get_string (s, "redirection-uri");
+  g_print ("Redirected to: %s\n", uri);
+
+  // save in global variable
+  if (redirection_uri)
+    g_free (redirection_uri);
+  redirection_uri = g_strdup (uri);
+
+  dialog_update ();
+}
+
+static void
+message_error_cb (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+  GError *err;
+  gchar *debug;
+
+  gst_message_parse_error (message, &err, &debug);
+
+  g_print ("Got error! ---------\n");
+  g_print ("  error code: %s: %d\n", g_quark_to_string (err->domain),
+      err->code);
+  g_print ("  message   : %s\n", err->message);
+  g_print ("  debug     : %s\n", debug);
+  g_print ("--------------------\n");
+
+  stop_playback ();
+
+  g_free (debug);
+  g_error_free (err);
+}
+
+static void
+setup_playback (void)
+{
+  GstBus *bus;
+
+  playbin = gst_element_factory_make ("playbin", "playbin");
+  g_signal_connect (playbin, "source-setup",
+      G_CALLBACK (source_setup_cb), NULL);
+
+  bus = gst_element_get_bus (playbin);
+  gst_bus_add_signal_watch (bus);
+  g_signal_connect (bus, "message::element",
+      G_CALLBACK (message_element_cb), NULL);
+  g_signal_connect (bus, "message::error", G_CALLBACK (message_error_cb), NULL);
+}
+
+int
+main (int argc, char *argv[])
+{
+  gst_init (&argc, &argv);
+  gtk_init (&argc, &argv);
+
+  setup_playback ();
+  show_main_window ();
+
+  gtk_main ();
+
+  stop_playback ();
+  gst_deinit ();
+
+  return 0;
+}
diff --git a/tests/examples/gtk/meson.build b/tests/examples/gtk/meson.build
index 76e9f4f8ea40cfc5d65b3d6339bbf9d5b2e6803e..31dfe0d01f7a05a4272230317bf5f69cea0aafe2 100644
--- a/tests/examples/gtk/meson.build
+++ b/tests/examples/gtk/meson.build
@@ -1,3 +1,9 @@
+executable('gtkhttpsrc', 'gtkhttpsrc.c',
+  dependencies: [gst_dep, gtk_dep, optional_deps],
+  c_args: gst_plugins_good_args,
+  include_directories: [configinc],
+  install: false)
+
 executable('gtksink', 'gtksink.c',
   dependencies: [gst_dep, gtk_dep, optional_deps],
   c_args: gst_plugins_good_args,
diff --git a/tests/examples/jack/meson.build b/tests/examples/jack/meson.build
index f9642168961d6527d1713bc079a6a2047d9ec443..fa87fb455dda46e9b4ef4415c62974de0fa06589 100644
--- a/tests/examples/jack/meson.build
+++ b/tests/examples/jack/meson.build
@@ -2,10 +2,90 @@ if get_option('jack').disabled()
   subdir_done()
 endif
 
-if libjack_dep.found()
-  executable('jack_client', 'jack_client.c',
-    dependencies: [gst_dep, gtk_dep, libjack_dep],
-    c_args: gst_plugins_good_args,
-    include_directories: [configinc],
-    install: false)
+jack_incdirs = [configinc, libsinc]
+
+# While we are dlopening jack for the gstjack plugins, or the example
+# we should link against jack and use its api directly as that's the
+# usage we expect for applications.
+libjack_dep = dependency('jack', version : '>= 1.9.7', required : false)
+
+if not libjack_dep.found()
+  fs = import('fs')
+  host_cpu = host_machine.cpu_family()
+  jack_maybe_installed = false
+  error_msg = '"jack" option is enabled but '
+  if (host_system == 'windows' and build_machine.system() == 'windows')
+    # Need to detect whether we're running on 64-bit Windows or not.
+    # If `C:/Program Files (x86)/` exists, we're running on 64-bit Windows, and
+    # C:/Program Files/ contains 64-bit programs. Else, we're on 32-bit Windows
+    # and C:/Program Files/ contains 32-bit programs.
+    #
+    # The user could either have a 32-bit JACK installation or a 64-bit one.
+    # When building for 32-bit x86, we need to check for both.
+    if fs.is_dir('C:/Program Files (x86)')
+      jack64_install_dir = 'C:/Program Files/JACK2'
+      jack32_install_dir = 'C:/Program Files (x86)/JACK2'
+    else
+      jack64_install_dir = ''
+      jack32_install_dir = 'C:/Program Files/JACK2'
+    endif
+
+    if host_cpu == 'x86'
+      jack_install_dir = jack32_install_dir
+      jack_maybe_installed = fs.is_dir(jack32_install_dir / 'include')
+      if not jack_maybe_installed and jack64_install_dir != ''
+        jack_maybe_installed = fs.is_dir(jack64_install_dir / 'include')
+        jack_install_dir = jack64_install_dir
+      endif
+    elif jack64_install_dir != ''
+      jack_maybe_installed = import('fs').is_dir(jack64_install_dir / 'include')
+      jack_install_dir = jack64_install_dir
+    endif
+
+    error_msg += 'JACK2 installation could not be found'
+  else
+    error_msg += 'JACK dependency could not be found'
+  endif
+
+  if not jack_maybe_installed
+    if jack_option.enabled()
+      error(error_msg)
+    endif
+    subdir_done()
+  endif
+
+  if not host_cpu.startswith('x86')
+    if jack_option.enabled()
+      error('On Windows, JACK only supports x86 32-bit and 64-bit')
+    endif
+    subdir_done()
+  endif
+
+  if host_cpu == 'x86'
+    jack_libname = 'libjack'
+    if jack_install_dir == jack64_install_dir
+      jack_libdir = jack_install_dir / 'lib32'
+    else
+      jack_libdir = jack_install_dir / 'lib'
+    endif
+  else
+    jack_libname = 'libjack64'
+    jack_libdir = jack_install_dir / 'lib'
+  endif
+
+  inc = include_directories(jack_install_dir / 'include')
+  libjack_dep = cc.find_library(jack_libname,
+      dirs: jack_libdir,
+      has_headers: 'jack/jack.h',
+      header_include_directories: inc,
+      required: jack_option)
+  # This won't be needed once we require a meson version that includes this:
+  # https://github.com/mesonbuild/meson/pull/10428
+  jack_incdirs += inc
 endif
+
+executable('jack_client', 'jack_client.c',
+  dependencies: [gst_dep, gtk_dep, libjack_dep],
+  c_args: gst_plugins_good_args,
+  include_directories: jack_incdirs,
+  install: false)
diff --git a/tests/examples/qt/qmloverlay/main.cpp b/tests/examples/qt/qmloverlay/main.cpp
index dbe362939801729785b746d05b172e338aa5985b..c74353170cc2251ae5bf39231d97d4828b5276ee 100644
--- a/tests/examples/qt/qmloverlay/main.cpp
+++ b/tests/examples/qt/qmloverlay/main.cpp
@@ -57,6 +57,10 @@ int main(int argc, char *argv[])
 
     GstElement *pipeline = gst_pipeline_new (NULL);
     GstElement *src = gst_element_factory_make ("videotestsrc", NULL);
+    GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
+    GstCaps *caps = gst_caps_from_string ("video/x-raw,format=RGBA");
+    g_object_set (capsfilter, "caps", caps, NULL);
+    gst_caps_unref (caps);
     GstElement *glupload = gst_element_factory_make ("glupload", NULL);
     /* the plugin must be loaded before loading the qml file to register the
      * GstGLVideoItem qml item */
@@ -66,8 +70,8 @@ int main(int argc, char *argv[])
 
     g_assert (src && glupload && overlay && sink);
 
-    gst_bin_add_many (GST_BIN (pipeline), src, glupload, overlay, overlay2, sink, NULL);
-    gst_element_link_many (src, glupload, overlay, overlay2, sink, NULL);
+    gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, glupload, overlay, overlay2, sink, NULL);
+    gst_element_link_many (src, capsfilter, glupload, overlay, overlay2, sink, NULL);
 
     /* load qmlglsink output */
     QQmlApplicationEngine engine;
diff --git a/tests/examples/qt/qmlsink-multisink/main.cpp b/tests/examples/qt/qmlsink-multisink/main.cpp
index 415b49b698150ce5dd61ea1c8851bd46d0c9c6f6..72856c2678a273e7e44a0e0949c23ecc0b1b6a1d 100644
--- a/tests/examples/qt/qmlsink-multisink/main.cpp
+++ b/tests/examples/qt/qmlsink-multisink/main.cpp
@@ -9,27 +9,33 @@ main (int argc, char *argv[])
 {
   gst_init (&argc, &argv);
 
-  QGuiApplication app (argc, argv);
-  QQmlApplicationEngine engine;
+  int ret;
+  {
+    QGuiApplication app (argc, argv);
+    QQmlApplicationEngine engine;
 
-  /* make sure that plugin was loaded */
-  GstElement *qmlglsink = gst_element_factory_make ("qmlglsink", NULL);
-  g_assert (qmlglsink);
+    /* make sure that plugin was loaded */
+    GstElement *qmlglsink = gst_element_factory_make ("qmlglsink", NULL);
+    g_assert (qmlglsink);
+    gst_clear_object (&qmlglsink);
 
-  /* anything supported by videotestsrc */
-  QStringList patterns (
-      {
-      "smpte", "ball", "spokes", "gamut"});
+    /* anything supported by videotestsrc */
+    QStringList patterns (
+        {
+        "smpte", "ball", "spokes", "gamut"});
 
-  engine.rootContext ()->setContextProperty ("patterns",
-      QVariant::fromValue (patterns));
+    engine.rootContext ()->setContextProperty ("patterns",
+        QVariant::fromValue (patterns));
 
-  QObject::connect (&engine, &QQmlEngine::quit, [&] {
-        gst_object_unref (qmlglsink);
-        qApp->quit ();
-      });
+    QObject::connect (&engine, &QQmlEngine::quit, [&] {
+          qApp->quit ();
+        });
 
-  engine.load (QUrl (QStringLiteral ("qrc:///main.qml")));
+    engine.load (QUrl (QStringLiteral ("qrc:///main.qml")));
 
-  return app.exec ();
+    ret = app.exec();
+  }
+  gst_deinit();
+
+  return ret;
 }
diff --git a/tests/examples/qt/qmlsink-multisink/videoitem/videoitem.cpp b/tests/examples/qt/qmlsink-multisink/videoitem/videoitem.cpp
index 9d2bb09af194e2efc9b6e2a5c075808351956bbb..3c8d22e6f43a1577a62f00d74dc0fed488cdc8d5 100644
--- a/tests/examples/qt/qmlsink-multisink/videoitem/videoitem.cpp
+++ b/tests/examples/qt/qmlsink-multisink/videoitem/videoitem.cpp
@@ -230,6 +230,7 @@ void VideoItem::componentComplete()
         if (window) {
             GstElement *glsink = gst_element_factory_make("qmlglsink", nullptr);
             Q_ASSERT(glsink);
+            gst_object_ref_sink (glsink);
 
             GstState current {GST_STATE_NULL}, pending {GST_STATE_NULL}, target {GST_STATE_NULL};
             auto status = gst_element_get_state(_priv->pipeline, &current, &pending, 0);
@@ -253,13 +254,12 @@ void VideoItem::componentComplete()
 
             gst_element_set_state(_priv->pipeline, GST_STATE_NULL);
 
-            glsink = GST_ELEMENT(gst_object_ref(glsink));
-
             window->scheduleRenderJob(new RenderJob([=] {
                 g_object_set(glsink, "widget", videoItem, nullptr);
                 _priv->renderPad = gst_element_get_static_pad(glsink, "sink");
                 g_object_set(_priv->sink, "sink", glsink, nullptr);
                 gst_element_set_state(_priv->pipeline, target);
+                gst_object_unref (glsink);
                 }),
                 QQuickWindow::BeforeSynchronizingStage);
         }
@@ -272,20 +272,16 @@ void VideoItem::componentComplete()
 void VideoItem::releaseResources()
 {
     GstElement *sink { nullptr };
-    QQuickWindow *win { window() };
 
     gst_element_set_state(_priv->pipeline, GST_STATE_NULL);
     g_object_get(_priv->sink, "sink", &sink, nullptr);
 
-    if (_priv->renderPad) {
+    if (sink && _priv->renderPad) {
         g_object_set(sink, "widget", nullptr, nullptr);
-        _priv->renderPad = nullptr;
     }
 
-    connect(this, &VideoItem::destroyed, this, [sink, win] {
-        auto job = new RenderJob(std::bind(&gst_object_unref, sink));
-        win->scheduleRenderJob(job, QQuickWindow::AfterSwapStage);
-    });
+    gst_clear_object (&_priv->renderPad);
+    gst_clear_object (&sink);
 }
 
 void VideoItem::updateRect()
@@ -318,6 +314,7 @@ void VideoItem::updateRect()
         setRect(QRect(span / 2, 0, winWidth - span, winHeight));
     }
     setResolution(QSize(picWidth, picHeight));
+    gst_clear_caps(&caps);
 }
 
 VideoItem::State VideoItem::state() const
diff --git a/tests/examples/qt/qmlsink/main.cpp b/tests/examples/qt/qmlsink/main.cpp
index a5cd5606b57d0ce7de901480b81bf20575292288..9aefe09bc1e97eef94c130fce9bf2e852f445f3c 100644
--- a/tests/examples/qt/qmlsink/main.cpp
+++ b/tests/examples/qt/qmlsink/main.cpp
@@ -46,6 +46,10 @@ int main(int argc, char *argv[])
 
     GstElement *pipeline = gst_pipeline_new (NULL);
     GstElement *src = gst_element_factory_make ("videotestsrc", NULL);
+    GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
+    GstCaps *caps = gst_caps_from_string ("video/x-raw,format=YV12");
+    g_object_set (capsfilter, "caps", caps, NULL);
+    gst_caps_unref (caps);
     GstElement *glupload = gst_element_factory_make ("glupload", NULL);
     /* the plugin must be loaded before loading the qml file to register the
      * GstGLVideoItem qml item */
@@ -53,8 +57,8 @@ int main(int argc, char *argv[])
 
     g_assert (src && glupload && sink);
 
-    gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL);
-    gst_element_link_many (src, glupload, sink, NULL);
+    gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, glupload, sink, NULL);
+    gst_element_link_many (src, capsfilter, glupload, sink, NULL);
 
     QQmlApplicationEngine engine;
     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
diff --git a/tests/examples/qt6/meson.build b/tests/examples/qt6/meson.build
index 23d3fb6c5d5f094f18363ef922cbd889e458675d..9e8f83574c5259ed042b4218b9260e1f41ad4cb2 100644
--- a/tests/examples/qt6/meson.build
+++ b/tests/examples/qt6/meson.build
@@ -15,3 +15,6 @@ if not qt6qml_example_deps.found()
 endif
 
 subdir('qmlsink')
+subdir('qmlsrc')
+subdir('qmloverlay')
+subdir('qmlmixer')
diff --git a/tests/examples/qt6/qmlmixer/main.cpp b/tests/examples/qt6/qmlmixer/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d9743932cd44479ac6095a71a6706bf15c06346
--- /dev/null
+++ b/tests/examples/qt6/qmlmixer/main.cpp
@@ -0,0 +1,131 @@
+#include <QApplication>
+#include <QQmlApplicationEngine>
+#include <QQuickWindow>
+#include <QQuickItem>
+#include <QRunnable>
+#include <QDirIterator>
+#include <gst/gst.h>
+
+class SetPlaying : public QRunnable
+{
+public:
+  SetPlaying(GstElement *);
+  ~SetPlaying();
+
+  void run ();
+
+private:
+  GstElement * pipeline_;
+};
+
+SetPlaying::SetPlaying (GstElement * pipeline)
+{
+  this->pipeline_ = pipeline ? static_cast<GstElement *> (gst_object_ref (pipeline)) : NULL;
+}
+
+SetPlaying::~SetPlaying ()
+{
+  if (this->pipeline_)
+    gst_object_unref (this->pipeline_);
+}
+
+void
+SetPlaying::run ()
+{
+  if (this->pipeline_)
+    gst_element_set_state (this->pipeline_, GST_STATE_PLAYING);
+}
+
+static void
+on_mixer_scene_initialized (GstElement * mixer, gpointer unused)
+{
+  QQuickItem *rootObject;
+  GST_INFO ("scene initialized");
+  g_object_get (mixer, "root-item", &rootObject, NULL);
+
+  QQuickItem *videoItem0 = rootObject->findChild<QQuickItem *> ("inputVideoItem0");
+  GstPad *sink0 = gst_element_get_static_pad (mixer, "sink_0");
+  g_object_set (sink0, "widget", videoItem0, NULL);
+  gst_clear_object (&sink0);
+
+  QQuickItem *videoItem1 = rootObject->findChild<QQuickItem *> ("inputVideoItem1");
+  GstPad *sink1 = gst_element_get_static_pad (mixer, "sink_1");
+  g_object_set (sink1, "widget", videoItem1, NULL);
+  gst_clear_object (&sink1);
+}
+
+int main(int argc, char *argv[])
+{
+  int ret;
+
+  gst_init (&argc, &argv);
+
+  {
+    QGuiApplication app(argc, argv);
+
+    QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
+
+    GstElement *pipeline = gst_pipeline_new (NULL);
+    GstElement *src0 = gst_element_factory_make ("videotestsrc", NULL);
+    GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
+    GstCaps *caps = gst_caps_from_string ("video/x-raw,format=YV12");
+    g_object_set (capsfilter, "caps", caps, NULL);
+    gst_clear_caps (&caps);
+    GstElement *glupload0 = gst_element_factory_make ("glupload", NULL);
+    GstElement *src1 = gst_element_factory_make ("videotestsrc", NULL);
+    gst_util_set_object_arg ((GObject *) src1, "pattern", "ball");
+    GstElement *glupload1 = gst_element_factory_make ("glupload", NULL);
+    /* the plugin must be loaded before loading the qml file to register the
+     * GstGLVideoItem qml item */
+    GstElement *mixer = gst_element_factory_make ("qml6glmixer", NULL);
+    GstElement *sink = gst_element_factory_make ("qml6glsink", NULL);
+
+    g_assert (src0 && glupload0 && mixer && sink);
+
+    gst_bin_add_many (GST_BIN (pipeline), src0, capsfilter, glupload0, src1, glupload1, mixer, sink, NULL);
+    gst_element_link_many (src0, capsfilter, glupload0, mixer, sink, NULL);
+    gst_element_link_many (src1, glupload1, mixer, NULL);
+
+    /* load qmlglsink output */
+    QQmlApplicationEngine engine;
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+    QQuickItem *videoItem;
+    QQuickWindow *rootObject;
+
+    /* find and set the videoItem on the sink */
+    rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
+    videoItem = rootObject->findChild<QQuickItem *> ("videoItem");
+    g_assert (videoItem);
+    g_object_set(sink, "widget", videoItem, NULL);
+
+    QDirIterator it(":", QDirIterator::Subdirectories);
+    while (it.hasNext()) {
+        qDebug() << it.next();
+    }
+
+    QFile f(":/mixer.qml");
+    if(!f.open(QIODevice::ReadOnly)) {
+        qWarning() << "error: " << f.errorString();
+        return 1;
+    }
+    QByteArray overlay_scene = f.readAll();
+    qDebug() << overlay_scene;
+
+    /* load qmlgloverlay contents */
+    g_signal_connect (mixer, "qml-scene-initialized", G_CALLBACK (on_mixer_scene_initialized), NULL);
+    g_object_set (mixer, "qml-scene", overlay_scene.data(), NULL);
+
+    rootObject->scheduleRenderJob (new SetPlaying (pipeline),
+        QQuickWindow::BeforeSynchronizingStage);
+
+    ret = app.exec();
+
+    gst_element_set_state (pipeline, GST_STATE_NULL);
+    gst_object_unref (pipeline);
+  }
+
+  gst_deinit ();
+
+  return ret;
+}
diff --git a/tests/examples/qt6/qmlmixer/main.qml b/tests/examples/qt6/qmlmixer/main.qml
new file mode 100644
index 0000000000000000000000000000000000000000..27a6b22807110e2d0805d0b4c5ca1afcc5d8e0f4
--- /dev/null
+++ b/tests/examples/qt6/qmlmixer/main.qml
@@ -0,0 +1,38 @@
+import QtQuick 6.0
+import QtQuick.Controls 6.0
+import QtQuick.Dialogs 6.0
+import QtQuick.Window 6.0
+
+import org.freedesktop.gstreamer.Qt6GLVideoItem 1.0
+
+ApplicationWindow {
+    id: window
+    visible: true
+    width: 640
+    height: 480
+    x: 30
+    y: 30
+    color: "black"
+
+    Item {
+        anchors.fill: parent
+
+        GstGLQt6VideoItem {
+            id: video
+            objectName: "videoItem"
+            anchors.centerIn: parent
+            width: parent.width
+            height: parent.height
+        }
+
+        Text {
+            anchors.bottom: parent.bottom
+            anchors.horizontalCenter: parent.horizontalCenter
+            text: "qmlglsink text"
+            font.pointSize: 20
+            color: "yellow"
+            style: Text.Outline
+            styleColor: "blue"
+        }
+    }
+}
diff --git a/tests/examples/qt6/qmlmixer/meson.build b/tests/examples/qt6/qmlmixer/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..466b245a6e45b6d13eacc6558671c6eabce5bf62
--- /dev/null
+++ b/tests/examples/qt6/qmlmixer/meson.build
@@ -0,0 +1,11 @@
+sources = [
+  'main.cpp',
+]
+
+qt_preprocessed = qt6_mod.preprocess(qresources : 'qmlmixer.qrc')
+executable('qml6glmixer', sources, qt_preprocessed,
+    dependencies : [gst_dep, qt6qml_example_deps],
+    override_options : ['cpp_std=c++17'],
+    c_args : gst_plugins_good_args,
+    include_directories : [configinc],
+    install: false)
diff --git a/tests/examples/qt6/qmlmixer/mixer.qml b/tests/examples/qt6/qmlmixer/mixer.qml
new file mode 100644
index 0000000000000000000000000000000000000000..8cb2d6460bdc5be8103b7da00fda213f92995a2a
--- /dev/null
+++ b/tests/examples/qt6/qmlmixer/mixer.qml
@@ -0,0 +1,67 @@
+import QtQuick 6.0
+
+import org.freedesktop.gstreamer.Qt6GLVideoItem 1.0
+
+Item {
+    /* render upside down for GStreamer */
+    transform: Scale { origin.x : 0; origin.y : height / 2.; yScale : -1 }
+
+    GstGLQt6VideoItem {
+        id: video0
+        objectName: "inputVideoItem0"
+        anchors.left: parent.left
+        anchors.right: parent.horizontalCenter
+        width: parent.width / 2
+        height: parent.height
+    }
+
+    GstGLQt6VideoItem {
+        id: video1
+        objectName: "inputVideoItem1"
+        anchors.left: parent.horizontalCenter
+        anchors.right: parent.right
+        width: parent.width / 2
+        height: parent.height
+    }
+
+    Text {
+        id: rotatingText
+        anchors.centerIn: parent
+        text: "Qt Quick\nrendered to\na texture"
+        font.pointSize: 20
+        color: "black"
+        style: Text.Outline
+        styleColor: "white"
+
+        RotationAnimator {
+            target: rotatingText;
+            from: 0;
+            to: 360;
+            duration: 5000
+            running: true
+            loops: Animation.Infinite
+        }
+    }
+
+    Text {
+        property int elapsedTime: 0
+
+        id: time
+        anchors.top: rotatingText.bottom
+        anchors.horizontalCenter: rotatingText.horizontalCenter
+        font.pointSize: 12
+        style: Text.Outline
+        styleColor: "black"
+        color: "white"
+
+        Timer {
+            interval: 1000
+            running: true
+            repeat: true
+            onTriggered: {
+                parent.elapsedTime += interval / 1000
+                parent.text = "overlay: " + parent.elapsedTime.toString() + " seconds"
+            }
+        }
+    }
+}
diff --git a/tests/examples/qt6/qmlmixer/qmlmixer.qrc b/tests/examples/qt6/qmlmixer/qmlmixer.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..c749352b18d33d4dcc020807c3b9e85f2f0b80bb
--- /dev/null
+++ b/tests/examples/qt6/qmlmixer/qmlmixer.qrc
@@ -0,0 +1,6 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+        <file>mixer.qml</file>
+    </qresource>
+</RCC>
diff --git a/tests/examples/qt6/qmloverlay/main.cpp b/tests/examples/qt6/qmloverlay/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..58b81506f90fe6001cb0552eb3e7facd244828b5
--- /dev/null
+++ b/tests/examples/qt6/qmloverlay/main.cpp
@@ -0,0 +1,131 @@
+#include <QApplication>
+#include <QQmlApplicationEngine>
+#include <QQuickWindow>
+#include <QQuickItem>
+#include <QRunnable>
+#include <QDirIterator>
+#include <gst/gst.h>
+
+class SetPlaying : public QRunnable
+{
+public:
+  SetPlaying(GstElement *);
+  ~SetPlaying();
+
+  void run ();
+
+private:
+  GstElement * pipeline_;
+};
+
+SetPlaying::SetPlaying (GstElement * pipeline)
+{
+  this->pipeline_ = pipeline ? static_cast<GstElement *> (gst_object_ref (pipeline)) : NULL;
+}
+
+SetPlaying::~SetPlaying ()
+{
+  if (this->pipeline_)
+    gst_object_unref (this->pipeline_);
+}
+
+void
+SetPlaying::run ()
+{
+  if (this->pipeline_)
+    gst_element_set_state (this->pipeline_, GST_STATE_PLAYING);
+}
+
+static void
+on_overlay_scene_initialized (GstElement * overlay, gpointer unused)
+{
+  QQuickItem *rootObject;
+  GST_INFO ("scene initialized");
+  g_object_get (overlay, "root-item", &rootObject, NULL);
+  QQuickItem *videoItem = rootObject->findChild<QQuickItem *> ("inputVideoItem");
+  g_object_set (overlay, "widget", videoItem, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+  int ret;
+
+  gst_init (&argc, &argv);
+
+  {
+    QGuiApplication app(argc, argv);
+
+    QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
+
+    GstElement *pipeline = gst_pipeline_new (NULL);
+    GstElement *src = gst_element_factory_make ("videotestsrc", NULL);
+    GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
+    GstCaps *caps = gst_caps_from_string ("video/x-raw,format=YV12");
+    g_object_set (capsfilter, "caps", caps, NULL);
+    gst_clear_caps (&caps);
+    GstElement *glupload = gst_element_factory_make ("glupload", NULL);
+    /* the plugin must be loaded before loading the qml file to register the
+     * GstGLVideoItem qml item */
+    GstElement *overlay = gst_element_factory_make ("qml6gloverlay", NULL);
+    GstElement *overlay2 = gst_element_factory_make ("qml6gloverlay", NULL);
+    GstElement *sink = gst_element_factory_make ("qml6glsink", NULL);
+
+    g_assert (src && glupload && overlay && sink);
+
+    gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, glupload, overlay, overlay2, sink, NULL);
+    gst_element_link_many (src, capsfilter, glupload, overlay, overlay2, sink, NULL);
+
+    /* load qmlglsink output */
+    QQmlApplicationEngine engine;
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+    QQuickItem *videoItem;
+    QQuickWindow *rootObject;
+
+    /* find and set the videoItem on the sink */
+    rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
+    videoItem = rootObject->findChild<QQuickItem *> ("videoItem");
+    g_assert (videoItem);
+    g_object_set(sink, "widget", videoItem, NULL);
+
+    QDirIterator it(":", QDirIterator::Subdirectories);
+    while (it.hasNext()) {
+        qDebug() << it.next();
+    }
+
+    QFile f(":/overlay.qml");
+    if(!f.open(QIODevice::ReadOnly)) {
+        qWarning() << "error: " << f.errorString();
+        return 1;
+    }
+    QByteArray overlay_scene = f.readAll();
+    qDebug() << overlay_scene;
+
+    QFile f2(":/overlay2.qml");
+    if(!f2.open(QIODevice::ReadOnly)) {
+        qWarning() << "error: " << f2.errorString();
+        return 1;
+    }
+    QByteArray overlay_scene2 = f2.readAll();
+    qDebug() << overlay_scene2;
+
+    /* load qmlgloverlay contents */
+    g_signal_connect (overlay, "qml-scene-initialized", G_CALLBACK (on_overlay_scene_initialized), NULL);
+    g_object_set (overlay, "qml-scene", overlay_scene.data(), NULL);
+
+    g_signal_connect (overlay2, "qml-scene-initialized", G_CALLBACK (on_overlay_scene_initialized), NULL);
+    g_object_set (overlay2, "qml-scene", overlay_scene2.data(), NULL);
+
+    rootObject->scheduleRenderJob (new SetPlaying (pipeline),
+        QQuickWindow::BeforeSynchronizingStage);
+
+    ret = app.exec();
+
+    gst_element_set_state (pipeline, GST_STATE_NULL);
+    gst_object_unref (pipeline);
+  }
+
+  gst_deinit ();
+
+  return ret;
+}
diff --git a/tests/examples/qt6/qmloverlay/main.qml b/tests/examples/qt6/qmloverlay/main.qml
new file mode 100644
index 0000000000000000000000000000000000000000..27a6b22807110e2d0805d0b4c5ca1afcc5d8e0f4
--- /dev/null
+++ b/tests/examples/qt6/qmloverlay/main.qml
@@ -0,0 +1,38 @@
+import QtQuick 6.0
+import QtQuick.Controls 6.0
+import QtQuick.Dialogs 6.0
+import QtQuick.Window 6.0
+
+import org.freedesktop.gstreamer.Qt6GLVideoItem 1.0
+
+ApplicationWindow {
+    id: window
+    visible: true
+    width: 640
+    height: 480
+    x: 30
+    y: 30
+    color: "black"
+
+    Item {
+        anchors.fill: parent
+
+        GstGLQt6VideoItem {
+            id: video
+            objectName: "videoItem"
+            anchors.centerIn: parent
+            width: parent.width
+            height: parent.height
+        }
+
+        Text {
+            anchors.bottom: parent.bottom
+            anchors.horizontalCenter: parent.horizontalCenter
+            text: "qmlglsink text"
+            font.pointSize: 20
+            color: "yellow"
+            style: Text.Outline
+            styleColor: "blue"
+        }
+    }
+}
diff --git a/tests/examples/qt6/qmloverlay/meson.build b/tests/examples/qt6/qmloverlay/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..81822f3d952337390e85a16247a285aad2befeb4
--- /dev/null
+++ b/tests/examples/qt6/qmloverlay/meson.build
@@ -0,0 +1,11 @@
+sources = [
+  'main.cpp',
+]
+
+qt_preprocessed = qt6_mod.preprocess(qresources : 'qmloverlay.qrc')
+executable('qml6gloverlay', sources, qt_preprocessed,
+    dependencies : [gst_dep, qt6qml_example_deps],
+    override_options : ['cpp_std=c++17'],
+    c_args : gst_plugins_good_args,
+    include_directories : [configinc],
+    install: false)
diff --git a/tests/examples/qt6/qmloverlay/overlay.qml b/tests/examples/qt6/qmloverlay/overlay.qml
new file mode 100644
index 0000000000000000000000000000000000000000..96a733c2e32f0cdfa6a4af86b2665bdf1cd19a87
--- /dev/null
+++ b/tests/examples/qt6/qmloverlay/overlay.qml
@@ -0,0 +1,57 @@
+import QtQuick 6.0
+
+import org.freedesktop.gstreamer.Qt6GLVideoItem 1.0
+
+Item {
+    /* render upside down for GStreamer */
+    transform: Scale { origin.x : 0; origin.y : height / 2.; yScale : -1 }
+
+    GstGLQt6VideoItem {
+        id: video
+        objectName: "inputVideoItem"
+        anchors.centerIn: parent
+        width: parent.width
+        height: parent.height
+    }
+
+    Text {
+        id: rotatingText
+        anchors.centerIn: parent
+        text: "Qt Quick\nrendered to\na texture"
+        font.pointSize: 20
+        color: "black"
+        style: Text.Outline
+        styleColor: "white"
+
+        RotationAnimator {
+            target: rotatingText;
+            from: 0;
+            to: 360;
+            duration: 5000
+            running: true
+            loops: Animation.Infinite
+        }
+    }
+
+    Text {
+        property int elapsedTime: 0
+
+        id: time
+        anchors.top: rotatingText.bottom
+        anchors.horizontalCenter: rotatingText.horizontalCenter
+        font.pointSize: 12
+        style: Text.Outline
+        styleColor: "black"
+        color: "white"
+
+        Timer {
+            interval: 1000
+            running: true
+            repeat: true
+            onTriggered: {
+                parent.elapsedTime += interval / 1000
+                parent.text = "overlay: " + parent.elapsedTime.toString() + " seconds"
+            }
+        }
+    }
+}
diff --git a/tests/examples/qt6/qmloverlay/overlay2.qml b/tests/examples/qt6/qmloverlay/overlay2.qml
new file mode 100644
index 0000000000000000000000000000000000000000..17fb34ad63942e09f141803c9f8669cdc560408a
--- /dev/null
+++ b/tests/examples/qt6/qmloverlay/overlay2.qml
@@ -0,0 +1,57 @@
+import QtQuick 6.0
+
+import org.freedesktop.gstreamer.Qt6GLVideoItem 1.0
+
+Item {
+    /* render upside down for GStreamer */
+    transform: Scale { origin.x : 0; origin.y : height / 2.; yScale : -1 }
+
+    GstGLQt6VideoItem {
+        id: video
+        objectName: "inputVideoItem"
+        anchors.centerIn: parent
+        width: parent.width
+        height: parent.height
+    }
+
+    Text {
+        id: rotatingText
+        anchors.centerIn: parent
+        text: "Qt Quick\nrendered to\na texture"
+        font.pointSize: 20
+        color: "black"
+        style: Text.Outline
+        styleColor: "white"
+
+        RotationAnimator {
+            target: rotatingText;
+            from: 0;
+            to: 360;
+            duration: 10000
+            running: true
+            loops: Animation.Infinite
+        }
+    }
+
+    Text {
+        property int elapsedTime: 0
+
+        id: time
+        anchors.bottom: rotatingText.top
+        anchors.horizontalCenter: rotatingText.horizontalCenter
+        font.pointSize: 12
+        style: Text.Outline
+        styleColor: "red"
+        color: "black"
+
+        Timer {
+            interval: 1000
+            running: true
+            repeat: true
+            onTriggered: {
+                parent.elapsedTime += interval / 1000
+                parent.text = "overlay: " + parent.elapsedTime.toString() + " seconds"
+            }
+        }
+    }
+}
diff --git a/tests/examples/qt6/qmloverlay/qmloverlay.qrc b/tests/examples/qt6/qmloverlay/qmloverlay.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..42fb250d63a84f0cf5200a1b512c230c53fe7195
--- /dev/null
+++ b/tests/examples/qt6/qmloverlay/qmloverlay.qrc
@@ -0,0 +1,7 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+        <file>overlay.qml</file>
+        <file>overlay2.qml</file>
+    </qresource>
+</RCC>
diff --git a/tests/examples/qt6/qmlsink/main.cpp b/tests/examples/qt6/qmlsink/main.cpp
index 81f305252b896ecb9384af8b7d9aba8c5ead3aa1..38c681dd06b9192f2dc6b71fe38f5ae476ece9a0 100644
--- a/tests/examples/qt6/qmlsink/main.cpp
+++ b/tests/examples/qt6/qmlsink/main.cpp
@@ -48,6 +48,10 @@ int main(int argc, char *argv[])
 
     GstElement *pipeline = gst_pipeline_new (NULL);
     GstElement *src = gst_element_factory_make ("videotestsrc", NULL);
+    GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
+    GstCaps *caps = gst_caps_from_string ("video/x-raw,format=YV12");
+    g_object_set (capsfilter, "caps", caps, NULL);
+    gst_clear_caps (&caps);
     GstElement *glupload = gst_element_factory_make ("glupload", NULL);
     /* the plugin must be loaded before loading the qml file to register the
      * GstGLVideoItem qml item */
@@ -55,8 +59,8 @@ int main(int argc, char *argv[])
 
     g_assert (src && glupload && sink);
 
-    gst_bin_add_many (GST_BIN (pipeline), src, glupload, sink, NULL);
-    gst_element_link_many (src, glupload, sink, NULL);
+    gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, glupload, sink, NULL);
+    gst_element_link_many (src, capsfilter, glupload, sink, NULL);
 
     QQmlApplicationEngine engine;
     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
diff --git a/tests/examples/qt6/qmlsrc/grabqml.pro b/tests/examples/qt6/qmlsrc/grabqml.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9c97c670a42bf237f88fa645183017bdf622b34d
--- /dev/null
+++ b/tests/examples/qt6/qmlsrc/grabqml.pro
@@ -0,0 +1,20 @@
+TEMPLATE = app
+
+QT += qml quick widgets
+
+QT_CONFIG -= no-pkg-config
+CONFIG += link_pkgconfig debug
+PKGCONFIG = \
+    gstreamer-1.0 \
+    gstreamer-video-1.0
+
+DEFINES += GST_USE_UNSTABLE_API
+
+INCLUDEPATH += ../lib
+
+SOURCES += main.cpp
+
+RESOURCES += qmlsrc.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
diff --git a/tests/examples/qt6/qmlsrc/main.cpp b/tests/examples/qt6/qmlsrc/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9ceb648627e899ed265b63bc9dd35cbff024fb2b
--- /dev/null
+++ b/tests/examples/qt6/qmlsrc/main.cpp
@@ -0,0 +1,82 @@
+#include <QApplication>
+#include <QQmlApplicationEngine>
+#include <QQuickWindow>
+#include <QQuickItem>
+#include <QQuickView>
+#include <QRunnable>
+#include <QDebug>
+#include <gst/gst.h>
+
+class SetPlaying : public QRunnable
+{
+public:
+  SetPlaying(GstElement *);
+  ~SetPlaying();
+
+  void run ();
+
+private:
+  GstElement * pipeline_;
+};
+
+SetPlaying::SetPlaying (GstElement * pipeline)
+{
+  this->pipeline_ = pipeline ? static_cast<GstElement *> (gst_object_ref (pipeline)) : NULL;
+}
+
+SetPlaying::~SetPlaying ()
+{
+  if (this->pipeline_)
+    gst_object_unref (this->pipeline_);
+  
+}
+
+void
+SetPlaying::run ()
+{
+  if (this->pipeline_)
+    gst_element_set_state (this->pipeline_, GST_STATE_PLAYING);
+}
+
+int main(int argc, char *argv[])
+{
+  int ret;
+
+  QGuiApplication app(argc, argv);
+  gst_init (&argc, &argv);
+
+  QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
+
+  GstElement *pipeline = gst_pipeline_new (NULL);
+  GstElement *src = gst_element_factory_make ("qml6glsrc", NULL);
+  GstElement *sink = gst_element_factory_make ("glimagesink", NULL); 
+
+  g_assert (src && sink);
+
+  gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
+  gst_element_link_many (src, sink, NULL);
+
+  QQmlApplicationEngine engine;
+  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+  QQuickWindow *rootObject;
+
+  /* find and set the QQuickWindow on the src */
+  rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
+  g_object_set(src, "window", rootObject, NULL);
+  g_object_set(src, "use-default-fbo", TRUE, NULL);
+  /* output buffer of qmlglsrc is vertical flip, get the image orientation tag */
+  g_object_set(sink, "rotate-method", 8, NULL);
+
+  rootObject->scheduleRenderJob (new SetPlaying (pipeline),
+      QQuickWindow::BeforeSynchronizingStage);
+
+  ret = app.exec();
+
+  gst_element_set_state (pipeline, GST_STATE_NULL);
+  gst_object_unref (pipeline);
+
+  gst_deinit ();
+
+  return ret;
+}
diff --git a/tests/examples/qt6/qmlsrc/main.qml b/tests/examples/qt6/qmlsrc/main.qml
new file mode 100644
index 0000000000000000000000000000000000000000..11b7047faf4015d72051e6b19f2b542a6fe69615
--- /dev/null
+++ b/tests/examples/qt6/qmlsrc/main.qml
@@ -0,0 +1,64 @@
+import QtQuick 6.0
+import QtQuick.Controls 6.0
+import QtQuick.Dialogs 6.0
+import QtQuick.Window 6.0
+
+ApplicationWindow {
+    id: window
+    visible: true
+    width: 640
+    height: 480
+    x: 30
+    y: 30
+    color: "dodgerblue"
+
+    Item {
+        anchors.fill: parent
+
+        Rectangle {
+            color: Qt.rgba(1, 1, 1, 0.7)
+            border.width: 1
+            border.color: "white"
+            anchors.bottomMargin: 15
+            anchors.horizontalCenter: parent.horizontalCenter
+            width : parent.width - 30
+            height: parent.height - 30
+            radius: 8
+
+            Text {
+                id: text1
+                anchors.centerIn: parent
+                text: "Hello World!"
+                font.pointSize: 24
+                visible: timer.tex1_visible
+            }
+
+            Text {
+                id: text2
+                anchors.centerIn: parent
+                text: "This is qmlglsrc demo!"
+                font.pointSize: 24
+                visible: timer.tex2_visible
+            }
+
+            Timer {
+                id: timer
+                property int count: 0
+                property int tex1_visible: 1
+                property int tex2_visible: 0
+                interval: 30; running: true; repeat: true
+                onTriggered: {
+                  count++;
+                  if (count%2 == 0) {
+                    tex1_visible = 1;
+                    tex2_visible = 0;
+                  }
+                  else {
+                    tex1_visible = 0;
+                    tex2_visible = 1;
+                  }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/examples/qt6/qmlsrc/meson.build b/tests/examples/qt6/qmlsrc/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..11ba53372cdfa4206cb472f8ed84f69272bdeed6
--- /dev/null
+++ b/tests/examples/qt6/qmlsrc/meson.build
@@ -0,0 +1,11 @@
+sources = [
+  'main.cpp',
+]
+
+qt_preprocessed = qt6_mod.preprocess(qresources : 'qmlsrc.qrc')
+executable('qml6src', sources, qt_preprocessed,
+    dependencies : [gst_dep, qt6qml_example_deps],
+    override_options : ['cpp_std=c++17'],
+    c_args : gst_plugins_good_args,
+    include_directories : [configinc],
+    install: false)
diff --git a/tests/examples/qt6/qmlsrc/qmlsrc.qrc b/tests/examples/qt6/qmlsrc/qmlsrc.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..5f6483ac33f1881cc59e080e69bb033ebe2a7829
--- /dev/null
+++ b/tests/examples/qt6/qmlsrc/qmlsrc.qrc
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>
diff --git a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c
index 6944ece079c98ee4327d271a7fbda649bb08b75b..03a836e574f939953c531a427c350cf31efd768f 100644
--- a/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c
+++ b/tests/examples/rpicamsrc/webrtc-unidirectional-h264.c
@@ -175,7 +175,7 @@ create_receiver_entry (SoupWebsocketConnection * connection)
   GError *error;
   ReceiverEntry *receiver_entry;
 
-  receiver_entry = g_slice_alloc0 (sizeof (ReceiverEntry));
+  receiver_entry = g_new0 (ReceiverEntry, 1);
   receiver_entry->connection = connection;
 
   g_object_ref (G_OBJECT (connection));
@@ -234,7 +234,7 @@ destroy_receiver_entry (gpointer receiver_entry_ptr)
   if (receiver_entry->connection != NULL)
     g_object_unref (G_OBJECT (receiver_entry->connection));
 
-  g_slice_free1 (sizeof (ReceiverEntry), receiver_entry);
+  g_free (receiver_entry);
 }
 
 
diff --git a/tests/files/editlists.mp4.gz.gz b/tests/files/editlists.mp4.gz.gz
new file mode 100644
index 0000000000000000000000000000000000000000..93cafdca132a0ac95fc1b65a9217a6649c201c8a
Binary files /dev/null and b/tests/files/editlists.mp4.gz.gz differ
diff --git a/tests/files/mss-fragment.m4f b/tests/files/mss-fragment.m4f
new file mode 100644
index 0000000000000000000000000000000000000000..6b0811ebba645850bafe4f0906e1a21d7d399dd3
Binary files /dev/null and b/tests/files/mss-fragment.m4f differ
diff --git a/tests/files/qtdemux-test-BBB_32k_1.mp4 b/tests/files/qtdemux-test-BBB_32k_1.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..fe6f928625b7430e7631a93ac5a2aeef26540eb6
Binary files /dev/null and b/tests/files/qtdemux-test-BBB_32k_1.mp4 differ
diff --git a/tests/files/qtdemux-test-BBB_32k_init.mp4 b/tests/files/qtdemux-test-BBB_32k_init.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..b18045d15eef2819b3596d24beed9b0c25ea4173
Binary files /dev/null and b/tests/files/qtdemux-test-BBB_32k_init.mp4 differ
diff --git a/tests/files/qtdemux-test-audio-init.mp4 b/tests/files/qtdemux-test-audio-init.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..4588fccd3d44567d10f9fd4636f5ef312750964c
Binary files /dev/null and b/tests/files/qtdemux-test-audio-init.mp4 differ
diff --git a/tests/files/qtdemux-test-audio-seg1.m4f b/tests/files/qtdemux-test-audio-seg1.m4f
new file mode 100644
index 0000000000000000000000000000000000000000..c30671398a85ecb8ff7eb29556674133579c825a
Binary files /dev/null and b/tests/files/qtdemux-test-audio-seg1.m4f differ
diff --git a/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-itunes.m4a b/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-itunes.m4a
new file mode 100644
index 0000000000000000000000000000000000000000..ddea727aa5b02733e5c83f6649967ad21e01b9bb
Binary files /dev/null and b/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-itunes.m4a differ
diff --git a/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-with-itunsmpb.m4a b/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-with-itunsmpb.m4a
new file mode 100644
index 0000000000000000000000000000000000000000..963e9cf2d3bbc282c02cde7ff3fd71010f3f9659
Binary files /dev/null and b/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-with-itunsmpb.m4a differ
diff --git a/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-without-itunsmpb.m4a b/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-without-itunsmpb.m4a
new file mode 100644
index 0000000000000000000000000000000000000000..da9a618667a9888dc385064ccc43ee803c2e9263
Binary files /dev/null and b/tests/files/sine-1kHztone-48kHzrate-mono-s32le-200000samples-nero-without-itunsmpb.m4a differ
diff --git a/tests/meson.build b/tests/meson.build
index 86c90d3f397f15c6eb375eb80bf73e9955f1e8b2..7299575125fe14c44ed93e5f53d2da117bf2ee63 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,4 +1,8 @@
-if not get_option('tests').disabled() and gstcheck_dep.found()
+if get_option('tests').disabled() or static_build
+  subdir_done()
+endif
+
+if gstcheck_dep.found()
   subdir('check')
   subdir('interactive')
 endif