<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Centricular Devlog</title>
  <subtitle>A blog about recent works of people at Centricular</subtitle>
  <link href="https://centricular.com/devlog/feed.xml" rel="self" />
  <link href="https://centricular.com/devlog/" />
  <updated>2026-02-19T00:00:00Z</updated>
  <id>https://centricular.com/devlog/</id>
  <author>
    <name>Centricular</name>
    <email>contact@centricular.com</email>
  </author>
  <entry>
    <title>Python Wheels for GStreamer</title>
    <author>
      <name>Nirbheek Chauhan</name>
      <email>nirbheek+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2026-02/Python-Wheels/" />
    <updated>2026-02-19T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2026-02/Python-Wheels/</id>
    <content type="html">&lt;p&gt;GStreamer has shipped binaries for all the major platforms for many years now:
Windows, Android, macOS, iOS. Linux packages are, of course, handled by all the
various distros.&lt;/p&gt;
&lt;p&gt;However, if you wanted to use the Python bindings on macOS or Windows, you had
to jump through hoops. Till now. GStreamer &lt;strong&gt;1.28.0&lt;/strong&gt; ships Python wheels
supporting Python 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 on macOS (GIL) and Windows
(GIL and free-threading). All you need to do is to run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m pip install gstreamer-bundle==1.28.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&#39;s it! You will have a complete GStreamer install, with all the plugins
you expect on macOS and Windows, and all utilities including &lt;code&gt;gst-launch-1.0&lt;/code&gt;
&lt;code&gt;gst-inspect-1.0&lt;/code&gt; &lt;code&gt;gst-device-monitor-1.0&lt;/code&gt; &lt;code&gt;ges-launch-1.0&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;gstreamer-bundle&lt;/code&gt; package is a complete distribution, so it will pull in all the
plugins, libraries, cmd-line tools, etc. If you want to depend on a more
minimal GStreamer installation or you want to avoid pulling in GPL or
known-patent-encumbered (&amp;quot;restricted&amp;quot;) plugins, you can use the
&lt;code&gt;gstreamer-meta&lt;/code&gt; package. That puts plugins behind &amp;quot;extras&amp;quot; like &lt;code&gt;gpl&lt;/code&gt; &lt;code&gt;cli&lt;/code&gt;
&lt;code&gt;restricted&lt;/code&gt; &lt;code&gt;gtk4&lt;/code&gt; etc.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a href=&quot;https://www.pollen-robotics.com/&quot;&gt;Pollen Robotics&lt;/a&gt; for sponsoring
this work. The &lt;a href=&quot;https://huggingface.co/spaces/pollen-robotics/Reachy_Mini&quot;&gt;Reachy Mini&lt;/a&gt;
companion robot by Pollen Robotics/Hugging Face &lt;a href=&quot;https://huggingface.co/docs/reachy_mini/SDK/media-architecture&quot;&gt;uses GStreamer&lt;/a&gt;
via the Python bindings and is the first production user of these wheels!&lt;/p&gt;
&lt;p&gt;We&#39;re very excited to see more people make use of these wheels.&lt;/p&gt;
&lt;p&gt;Read on for technical details on how all this was accomplished.&lt;/p&gt;
&lt;h2&gt;Step 1: Ship Python bindings via introspection on macOS and Windows&lt;/h2&gt;
&lt;p&gt;After many years, Python bindings support was re-introduced in GStreamer
&lt;strong&gt;1.26&lt;/strong&gt; and was shipped with the installers on macOS and Windows. This required
significant work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1587&quot;&gt;Re-introduce gobject-introspection support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1674&quot;&gt;Re-introduce Python bindings support&lt;/a&gt; and ship it on macOS + Windows&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1714&quot;&gt;Load typelibs relocatably&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://centricular.com/about/#amy&quot;&gt;Amy&lt;/a&gt; for doing the bulk of the
work here, and to everyone else who contributed towards this over the years:
&lt;a href=&quot;https://gitlab.freedesktop.org/ylatuya&quot;&gt;Andoni&lt;/a&gt;, &lt;a href=&quot;https://gitlab.freedesktop.org/nacho.garglez&quot;&gt;Nacho&lt;/a&gt;,
&lt;a href=&quot;https://gitlab.freedesktop.org/thiblahute&quot;&gt;Thibault&lt;/a&gt;, &lt;a href=&quot;https://gitlab.freedesktop.org/Tjitte/&quot;&gt;Tjitte&lt;/a&gt;,
and more that I&#39;m sure I&#39;ve missed.&lt;/p&gt;
&lt;h2&gt;Step 2: Build wheels for all supported Python versions&lt;/h2&gt;
&lt;p&gt;When shipping Python bindings for C libraries, it is necessary to also ship the
accompanying libraries and plugins, lest ABI mismatches and incompatibilities
arise. That&#39;s why the wheels we ship constitute a complete GStreamer
distribution, including all plugin dependencies such as GTK4. This means you
also have &lt;strong&gt;Python bindings for GTK4&lt;/strong&gt; available on macOS and Windows.&lt;/p&gt;
&lt;p&gt;This wasn&#39;t easy to accomplish, especially because PyGObject doesn&#39;t use the
limited Python C API. That means we can&#39;t just build for Python 3.9 and call it
a day. We need separate wheels for each Python version × target.&lt;/p&gt;
&lt;p&gt;The count goes something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We split the gstreamer libraries, plugins, and dependencies across 11
wheels&lt;/li&gt;
&lt;li&gt;We support 16 Python versions: 3.9 3.10 3.11 3.12 3.13 3.13t 3.14 3.14t&lt;/li&gt;
&lt;li&gt;And 3 platforms: macOS universal, Windows MSVC x86_64, Windows MSVC x86&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s 11 × 16 × 3 = &lt;span title=&quot;macOS Python doesn&#39;t support free threading
so the actual number is lower&quot;&gt;&lt;strong&gt;528&lt;/strong&gt; wheels&lt;/span&gt;. That is absolutely
untenable!&lt;/p&gt;
&lt;p&gt;So we have to do &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/blob/16d4d43caa4b51ecb6fcc4705ea530af4ec2a1c6/data/wheel/setup.py#L16-59&quot;&gt;some chicanery&lt;/a&gt;
to trim that down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Put everything that links to or loads Python in one wheel called
&lt;code&gt;gstreamer_python&lt;/code&gt;, so that everything else is agnostic to the Python
version being used&lt;/li&gt;
&lt;li&gt;Override &lt;code&gt;py_limited_api&lt;/code&gt; to be &lt;code&gt;cp39&lt;/code&gt; for all agnostic wheels and mark them
as not containing ext modules&lt;/li&gt;
&lt;li&gt;Rebuild the recipes responsible for generating libraries or plugins that go
into &lt;code&gt;gstreamer_python&lt;/code&gt; with each Python version we need to support&lt;/li&gt;
&lt;li&gt;On macOS, override &lt;code&gt;plat_name&lt;/code&gt; to be &lt;code&gt;macosx_10_13_universal2&lt;/code&gt; for all
agnostic wheels even if the Python version we&#39;re using doesn&#39;t support macOS
10.13, so that they can be reused across all Python versions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That brings us down to &lt;strong&gt;92&lt;/strong&gt; wheels. Still quite a lot, but now it&#39;s
a manageable number!&lt;/p&gt;
&lt;p&gt;The long-term solution is to &lt;a href=&quot;https://gitlab.gnome.org/GNOME/pygobject/-/issues/730&quot;&gt;port PyGObject over to the Limited Python
C API&lt;/a&gt;—which is quite
a big undertaking—but should allow us to skip most of this for Python &amp;gt;=3.12.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://centricular.com/about/#amy&quot;&gt;Amy&lt;/a&gt; once again for doing most
of the work to make this possible, and to Pollen Robotics for sponsoring us to
do it. Here are the relevant merge requests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2004&quot;&gt;Support for Python wheel packaging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2044&quot;&gt;Build separate wheels for each Python version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2041&quot;&gt;and&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2036&quot;&gt;there&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2038&quot;&gt;were&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2039&quot;&gt;a few&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2043&quot;&gt;more&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2063&quot;&gt;fixes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 3: Linux support&lt;/h2&gt;
&lt;p&gt;You may have noticed that there was no mention of wheels targeting Linux.
That&#39;s a much harder problem to solve than shipping on macOS or Windows, so we
had to punt it for a later release, likely one of the 1.28.x stable releases.&lt;/p&gt;
&lt;p&gt;We&#39;re planning to target &lt;a href=&quot;https://github.com/pypa/manylinux/&quot;&gt;manylinux_2_28&lt;/a&gt;
and support Python 3.9+, but there are still unknowns that could throw
a spanner in our plans. For instance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GStreamer often utilizes subtle characteristics of the Linux graphics stack
for good performance, which may break by targeting such an old base.&lt;/li&gt;
&lt;li&gt;The difference in library versions shipped with the wheels vs on the system
may cause subtle or catastrophic breakage in apps that &lt;em&gt;also&lt;/em&gt; load system
libraries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&#39;re hoping that we can overcome all this and ship something that allows users
on any Linux distro to get a functional GStreamer just by doing &lt;code&gt;pip install gstreamer-bundle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the meantime, please continue to use the distro-provided GStreamer packages
and Python bindings, and if they&#39;re missing plugins or are too old, please
contact your distro maintainer(s).&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>GStreamer Whisper Speech-to-Text Element</title>
    <author>
      <name>Mathieu Duponchelle</name>
      <email>mathieu+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2026-02/whisper/" />
    <updated>2026-02-11T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2026-02/whisper/</id>
    <content type="html">&lt;p&gt;At the &#39;25 GStreamer conference I gave a talk titled &lt;a href=&quot;https://gstconf.ubicast.tv/videos/costly-speech-an-introduction/&quot;&gt;Costly Speech: an introduction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This was in reference to the fact that all the speech-related elements used in the pipeline
I presented were wrappers around for-pay cloud services or for-pay on-site servers.&lt;/p&gt;
&lt;p&gt;At the end of the talk, I mentioned that plans for future development included new, &amp;quot;free&amp;quot;
backends. The first piece of the puzzle was a &lt;a href=&quot;https://openai.com/index/whisper/&quot;&gt;Whisper&lt;/a&gt;-based transcriber.&lt;/p&gt;
&lt;p&gt;I have the pleasure to announce that it is now implemented and &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/whisper/index.html&quot;&gt;published&lt;/a&gt;,
thank you to Ray Tiley from &lt;a href=&quot;https://www.trms.com/&quot;&gt;Tightrope Media Systems&lt;/a&gt; for sponsoring this work!&lt;/p&gt;
&lt;h2&gt;Design / Implementation&lt;/h2&gt;
&lt;p&gt;The main design goal was for the new transcriber to behave identically to the existing
transcribers, in particular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It needed to output timestamped words one at a time&lt;/li&gt;
&lt;li&gt;It needed to handle live streams with a configurable latency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to fulfill that second requirement, the implementation has to feed the model
with chunks of a configurable duration.&lt;/p&gt;
&lt;p&gt;This approach works well for constraining the latency, but didn&#39;t give the best results
accuracy-wise, as words close to the chunk boundaries would often go misssing, poorly
transcribed or duplicated.&lt;/p&gt;
&lt;p&gt;To address this, the implementation uses two mechanisms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It always feeds the previous chunk when running inference for a given chunk&lt;/li&gt;
&lt;li&gt;It extracts tokens from a sliding window at a configurable distance from the &amp;quot;live edge&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s an example with a 4-second chunk duration and a 1 second live edge offset:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0     1     2     3     4     5     6     7     8
| 4-second chunk        | 4-second chunk        |
                  | 4-second token window |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach greatly mitigates the boundary issues, as the tokens are always extracted
from a &amp;quot;stable&amp;quot; region of the model&#39;s output.&lt;/p&gt;
&lt;p&gt;With the above settings, the element reports a 5-second latency, to which a configurable
processing latency is added. That processing latency is dependent on the hardware, on
my machine using CUDA and a NVIDIA RTX 5080 GPU processing time is around 10x real time,
which means 1 second processing latency is sufficient.&lt;/p&gt;
&lt;p&gt;The obvious drawback of this approach is a doubling of the resource usage as each chunk
is fed twice through the inference model, it could be further refined to only feed part
of the previous chunk and thus increase performance without sacrificing accuracy.&lt;/p&gt;
&lt;p&gt;As the interface of the element follows that of other transcribers, it can be used as
an alternative transcriber within &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rsclosedcaption/transcriberbin.html&quot;&gt;transcriberbin&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Future prospects&lt;/h2&gt;
&lt;p&gt;The biggest missing piece to bring the transcriber to feature parity with other transcribers
such as the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/speechmatics/index.html&quot;&gt;speechmatics-based one&lt;/a&gt; is speaker diarization (~ identification).&lt;/p&gt;
&lt;p&gt;Whisper itself does not support diarization. The &lt;a href=&quot;https://github.com/akashmjn/tinydiarize&quot;&gt;tinydiarize&lt;/a&gt; project aimed to finetune
models to address this, but it has unfortunately been put on hold for now, and only
supported detecting speaker changes, not identifying individual speakers.&lt;/p&gt;
&lt;p&gt;It is not clear at the moment what would be the best open source option to integrate
for this task. Models such as &lt;a href=&quot;https://huggingface.co/nvidia/diar_streaming_sortformer_4spk-v2.1&quot;&gt;NVidia&#39;s streaming sortformer&lt;/a&gt; are promising, but
limited to four speakers for example.&lt;/p&gt;
&lt;p&gt;We are very interested in suggestions on this front. Don&#39;t hesitate to hit us up if
you have any or are interested in sponsoring further improvements to our growing
stack of speech-related elements!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>New GStreamer icecastsink with AAC support</title>
    <author>
      <name>Tim-Philipp Müller</name>
      <email>tim+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2026-02/icecastsink/" />
    <updated>2026-02-09T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2026-02/icecastsink/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://icecast.org&quot;&gt;Icecast&lt;/a&gt; is a Free and Open Source multimedia streaming
server, primarily used for audio and radio streaming over HTTP(S).&lt;/p&gt;
&lt;p&gt;In GStreamer you can send an audio stream to such a server with the
&lt;code&gt;shout2send&lt;/code&gt; sink element based on libshout2.&lt;/p&gt;
&lt;p&gt;This works perfectly fine, but has one limitation: it does not support
the AAC audio codec, which for some use cases and target systems is the
preferred audio codec. This is because libshout2 does not support it and
will not support it, at least not officially upstream.&lt;/p&gt;
&lt;p&gt;Some streaming servers such as the &lt;a href=&quot;https://www.rocketbroadcaster.com/streaming-audio-server/&quot;&gt;Rocket Streaming Audio Server (RSAS)&lt;/a&gt;
do support this though, and as such it would be nice to be able to send streams
to them in AAC format as well.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/icecast/#icecastsink-page&quot;&gt;&lt;code&gt;icecastsink&lt;/code&gt;&lt;/a&gt;, which is a new sink element written in Rust
to send audio to an Icecast server.&lt;/p&gt;
&lt;p&gt;It supports sending AAC audio in addition to Ogg/Vorbis, Ogg/Opus, FLAC and MP3,
and also has support for automatic re-connect in case the server kicks off the
client, which might happen if the client doesn&#39;t send data for a while.&lt;/p&gt;
&lt;p&gt;Give it a spin and let us know how it goes!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>GStreamer 1.28 Natively Supports Windows ARM64</title>
    <author>
      <name>Nirbheek Chauhan</name>
      <email>nirbheek+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2026-02/Windows-ARM64/" />
    <updated>2026-02-03T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2026-02/Windows-ARM64/</id>
    <content type="html">&lt;p&gt;One of the many items on my &amp;quot;nice-to-have&amp;quot; TODO list has been shipping
a GStreamer installer that natively targets Windows ARM64.
&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/&quot;&gt;Cerbero&lt;/a&gt; has had support
for cross-compiling to Windows ARM64 since GStreamer 1.16 in the form of
targeting UWP. However, once that was laid to rest with GStreamer 1.22, we
didn&#39;t start shipping Windows ARM64 installers instead because it was looking
like Microsoft&#39;s ARM64 experiment had also failed.&lt;/p&gt;
&lt;p&gt;Lately, however, there&#39;s been a significant resurgence of ARM64 laptops that run
Windows, and they seem to actually have compelling features for some types of
users.  So I spent a day or two and &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/2030&quot;&gt;reinstated support for Windows ARM64&lt;/a&gt;
built with MSVC in Cerbero.&lt;/p&gt;
&lt;p&gt;My purpose was just to find the shortest path to getting that to
a usable state, so &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/issues/548&quot;&gt;a bunch of plugins are missing&lt;/a&gt;.
In particular &lt;strong&gt;all&lt;/strong&gt; Rust plugins had to be disabled due to an issue building
the &lt;code&gt;ring&lt;/code&gt; crate. I am optimistic that someone will come along and help fix
these issues 😉&lt;/p&gt;
&lt;p&gt;You can find the installer at the usual location:
&lt;a href=&quot;https://gstreamer.freedesktop.org/download/#windows&quot;&gt;https://gstreamer.freedesktop.org/download/#windows&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note that these binaries are cross-compiled from x86_64, so the installer
itself is x86, and the contents are missing gobject-introspection and Python
bindings. We are also unable to generate Python wheels for Windows ARM64
because of this. If someone would like to help with any of this, please get in
touch on &lt;a href=&quot;https://matrix.to/#/#windows:gstreamer.org&quot;&gt;the Windows channel&lt;/a&gt; in
GStreamer&#39;s Matrix community.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using GstAnalytics from Rust with burn and YOLOX object detection</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-12/burn-yolox/" />
    <updated>2025-12-31T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-12/burn-yolox/</id>
    <content type="html">&lt;p&gt;Currently most code using the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/analytics/&quot;&gt;GStreamer Analytics library&lt;/a&gt; library is written in C or Python. To check how well the API works from Rust, and to have an excuse to play with the Rust &lt;a href=&quot;https://burn.dev&quot;&gt;burn deep-learning framework&lt;/a&gt;, I&#39;ve implemented an object detection inference element based on the &lt;a href=&quot;https://huggingface.co/papers/2107.08430&quot;&gt;YOLOX&lt;/a&gt; model and a corresponding tensor decoder that allows usage with other elements based on the GstAnalytics API. I started this work at the last GStreamer hackfest, but this has now finally been merged and will be part of the GStreamer 1.28.0 release.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://burn.dev&quot;&gt;burn&lt;/a&gt; is a deep-learning framework in Rust that is approximately on the same level of abstraction as &lt;a href=&quot;https://pytorch.org&quot;&gt;PyTorch&lt;/a&gt;. It features lots of computation backends (CPU-based, Vulkan, CUDA, ROCm, Metal, libtorch, ...), has loaders (or better: code generation) for e.g. ONNX or PyTorch models, and compiles and optimizes the model for a specific backend. It also comes with &lt;a href=&quot;https://github.com/tracel-ai/models&quot;&gt;a repository&lt;/a&gt; containing various example models and links to other community models.&lt;/p&gt;
&lt;p&gt;The first element is &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/burn/&quot;&gt;burn-yoloxinference&lt;/a&gt;. It takes raw RGB video frames and passes them through burn; as of the time of this writing either through a CPU-based or a Vulkan-based computation backend. The output then is the very same video frames with the raw object detection results attached as a &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/analytics/gsttensormeta.html&quot;&gt;&lt;code&gt;GstTensorMeta&lt;/code&gt;&lt;/a&gt;. This is essentially a 85x8400 float matrix, which contains 8400 rows of candidate object detection boxes (4 floats) together with confidence values for the classes (80 floats for the pre-trained models on the &lt;a href=&quot;https://cocodataset.org&quot;&gt;COCO&lt;/a&gt; classes) and one confidence value for the overall box. The &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/analytics/burn/src/yoloxinference&quot;&gt;element&lt;/a&gt; itself is mostly boilerplate, caps negotiation code and glue code between GStreamer and burn.&lt;/p&gt;
&lt;p&gt;The second element is &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rsanalytics/yoloxtensordec.html&quot;&gt;yoloxtensordec&lt;/a&gt;. This takes the output of the first element and decodes the &lt;code&gt;GstTensorMeta&lt;/code&gt; into a &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/analytics/gstanalyticsmeta.html&quot;&gt;&lt;code&gt;GstAnalyticsRelationMeta&lt;/code&gt;&lt;/a&gt;, which describes the detected objects with their bounding boxes in an abstract way. As part of this it also implements a non-maximum suppression (NMS) filter using intersection over unions (IoU) of bounding boxes to reduce the 8400 candidate boxes to a much lower number of actual likely object detections. The &lt;code&gt;GstAnalyticsRelationMeta&lt;/code&gt; can then be used e.g. by the generic &lt;code&gt;objectdetectionoverlay&lt;/code&gt; to render rectangles on top of the video, or the &lt;code&gt;ioutracker&lt;/code&gt; elements to track objects over a sequence of frames. Again, &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/analytics/analytics/src/yoloxtensordec&quot;&gt;this element&lt;/a&gt; is mostly boilerplate and caps negotiation code, plus around 100 SLOC of algorithm. In comparison the C YOLOv9 tensor decoder element is about 3x as much code, mostly thanks to the overhead of C memory book-keeping, lack of useful data structures and lack of abstraction language tools.&lt;/p&gt;
&lt;p&gt;The reason why the tensor decoder is a separate element is mostly to have one such element per model and to have it implemented independently of the actual implementation and runtime of the model. The same tensor decoder should, for example, also work fine on the output of the &lt;code&gt;onnxinference&lt;/code&gt; element with a YOLOX model. From GStreamer 1.28 onwards it will also be possible to autoplug suitable tensor decoders via the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/tensordecoders/tensordecodebin.html&quot;&gt;tensordecodebin&lt;/a&gt; element.&lt;/p&gt;
&lt;p&gt;That the tensor decoders are independent of the actual implementation of the model also has the advantage that it can be implemented in a different language, preferably in a safer and less verbose language than C.&lt;/p&gt;
&lt;p&gt;For using both elements together and using &lt;code&gt;objectdetectionoverlay&lt;/code&gt; to render rectangles around the object detections, the following pipeline can be used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch-1.0 souphttpsrc location=https://raw.githubusercontent.com/tracel-ai/models/f4444a90955c1c6fda90597aac95039a393beb5a/squeezenet-burn/samples/cat.jpg &#92;
    ! jpegdec ! videoconvertscale ! &amp;quot;video/x-raw,width=640,height=640&amp;quot; &#92;
    ! burn-yoloxinference model-type=large backend-type=vulkan ! yoloxtensordec label-file=COCO_classes.txt &#92;
    ! videoconvertscale ! objectdetectionoverlay &#92;
    ! videoconvertscale ! imagefreeze ! autovideosink -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output should look similar to this &lt;img class=&quot;devlog-image rounded&quot; decoding=&quot;async&quot; alt=&quot;image&quot; src=&quot;https://centricular.com/devlog/img/tcXY-K2ULT-640.jpeg&quot; width=&quot;640&quot; height=&quot;640&quot;&gt;.&lt;/p&gt;
&lt;p&gt;I also did a &lt;a href=&quot;https://gstconf.ubicast.tv/videos/burn-a-little-case-study-on-using-gstanalytics-from-rust/&quot;&gt;lightning talk&lt;/a&gt; about this at the GStreamer conference this year.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Single media file support with hlssink3 and hlscmafsink</title>
    <author>
      <name>Sanchayan Maity</name>
      <email>sanchayan+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-12/hls-single-media-file-support/" />
    <updated>2025-12-30T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-12/hls-single-media-file-support/</id>
    <content type="html">&lt;p&gt;When using HTTP Live Streaming (HLS), a common use case is to use MPEG-TS segments or fragmented MP4 fragments. This is done so that the overall stream is available as a sequence of small HTTP-based file downloads, each being one short chunk of an overall bounded or unbounded media stream.&lt;/p&gt;
&lt;p&gt;The playlist file (.m3u8) contains a list of these small segments or fragments. This is the standard and most common approach for HLS. For the HLS CMAF case, a multi-segment playlist would look like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-TARGETDURATION:5
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI=&amp;quot;init00000.mp4&amp;quot;
#EXTINF:5,
segment00000.m4s
#EXTINF:5,
segment00001.m4s
#EXTINF:5,
segment00002.m4s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An alternative approach is to use a single media file with the &lt;code&gt;EXT-X-BYTERANGE&lt;/code&gt; tag. This method is primarily used for on-demand (VOD) streaming where the complete media file already exists and can reduce the number of files that needs to be managed on the server. Single file with byte-ranges requires the server and client to support HTTP byte range requests and 206 Partial Content responses.&lt;/p&gt;
&lt;p&gt;The single media file use case wasn&#39;t supported so far with either of &lt;code&gt;hlssink3&lt;/code&gt; or &lt;code&gt;hlscmafsink&lt;/code&gt;. A new property &lt;code&gt;single-media-file&lt;/code&gt; has been added, which lets users specify the use of a single media file.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;hlscmafsink&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;single-media-file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;main.mp4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
hlssink3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;single-media-file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;main.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the HLS CMAF case, this would generate a playlist like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-TARGETDURATION:5
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI=&amp;quot;main.mp4&amp;quot;,BYTERANGE=&amp;quot;768@0&amp;quot;
#EXT-X-BYTERANGE:100292@768
#EXTINF:5,
main.mp4
#EXT-X-BYTERANGE:98990@101060
#EXTINF:5,
main.mp4
#EXT-X-BYTERANGE:99329@200050
#EXTINF:5,
main.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be useful if one has storage requirements where the use of a single media file for HLS might be favourable.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Audio source separation in GStreamer with demucs</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-12/demucs/" />
    <updated>2025-12-29T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-12/demucs/</id>
    <content type="html">&lt;p&gt;Audio source separation describes the process of splitting an already mixed audio stream into its individual, logical sources. For example, splitting a song into separate streams for its individual instruments and vocals. This can be used for example for karaoke, music practice, or isolating the speaker from background noise for easier understanding by humans or improving results of speech-to-text processing.&lt;/p&gt;
&lt;p&gt;Starting with GStreamer 1.28.0 &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/demucs/&quot;&gt;an element&lt;/a&gt; for this purpose will be included. It is based on the Python/pytorch implementation of &lt;a href=&quot;https://github.com/adefossez/demucs&quot;&gt;demucs&lt;/a&gt; and comes with various pre-trained models with different performance and accuracy characteristics, as well as which different sets of sources they can separate. CPU-based processing is generally multiple times real-time on modern CPUs (around 8x on mine) but GPU-based processing via pytorch is also possible.&lt;/p&gt;
&lt;p&gt;The element itself is part of the &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/audio/demucs&quot;&gt;GStreamer Rust plugins&lt;/a&gt; and can either run demucs locally in-process using an embedded Python interpreter via &lt;a href=&quot;https://pyo3.rs&quot;&gt;pyo3&lt;/a&gt;, or via a small Python service over WebSockets that can run either locally or remotely (e.g. for thin clients). The used model, and chunk size and overlap between chunks can be configured. Chunk size and overlap provide control over the introduced latency (lower values give lower latency) and quality (higher values give better quality).&lt;/p&gt;
&lt;p&gt;The separate sources are provided on individual source pads of the element and it effectively behaves like a demuxer. A pipeline for karaoke would for example look as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch-1.0 uridecodebin uri=file:///path/to/music/file ! audioconvert ! tee name=t ! &#92;
  queue max-size-time=0 max-size-bytes=0 max-size-buffers=2 ! demucs name=demucs model-name=htdemucs &#92;
  demucs.src_vocals ! queue ! audioamplify amplification=-1 ! mixer.sink_0 &#92;
  t. ! queue max-size-time=9000000000 max-size-bytes=0 max-size-buffers=0 ! mixer.sink_1 &#92;
  audiomixer name=mixer ! audioconvert ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This takes an URI to a music file, passes that through the &lt;code&gt;demucs&lt;/code&gt; element for extracting the vocals, then takes the original input via a &lt;code&gt;tee&lt;/code&gt; and subtracts the vocals from it by first inverting all samples of the vocals stream with the &lt;code&gt;audioamplify&lt;/code&gt; element and then mixing it with the original input with an &lt;code&gt;audiomixer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also did a &lt;a href=&quot;https://gstconf.ubicast.tv/videos/audio-source-separation-using-snakes-crabs-and-torches/&quot;&gt;lightning talk&lt;/a&gt; about this at the GStreamer conference this year.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>New GStreamer ElevenLabs speech synthesis plugin</title>
    <author>
      <name>Mathieu Duponchelle</name>
      <email>mathieu+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-11/elevenlabs-speech-synthesis/" />
    <updated>2025-11-25T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-11/elevenlabs-speech-synthesis/</id>
    <content type="html">&lt;p&gt;Back in June &#39;25, I implemented a new speech synthesis element using the &lt;a href=&quot;https://elevenlabs.io/docs/api-reference/text-to-speech/convert&quot;&gt;ElevenLabs API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this post I will briefly explain some of the design choices I made, and provide one
or two usage examples.&lt;/p&gt;
&lt;h2&gt;POST vs. WSS&lt;/h2&gt;
&lt;p&gt;ElevenLabs offers two interfaces for speech synthesis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Either open a websocket and feed the service small chunks of text (eg words) to receive
a continuous audio stream&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Or POST longer segments of text to receive independent audio fragments&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The websocket API is well-adapted to conversational use cases, and can offer the lowest
latency, but isn&#39;t the most well-suited to the use cases I was targeting: my goal was to
use it to synthesize audio from text that was first transcribed, then translated from an
original input audio stream.&lt;/p&gt;
&lt;p&gt;In this situation we have two constraints we need to be mindful of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For translation purposes we need to construct large enough text segments prior to translating,
in order for the translation service to operate with enough context to do a good job.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once audio has been synthesized, we might also need to resample it in order to have it fit
within the original duration of the speech.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The latency benefits from using the websocket API are largely negated by the larger text
segments we would use as the input&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resampling the continuous stream we would receive to make sure individual words are time-shifted
back to the &amp;quot;correct&amp;quot; position, while possible thanks to the &lt;code&gt;sync_alignment&lt;/code&gt; option,
would have increased the complexity of the resulting element&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I chose to use the POST API for this element. We might still choose to implement a websocket-based
version if there is a good story for using GStreamer in a conversational pipeline, but that is not
on my radar for now.&lt;/p&gt;
&lt;p&gt;Additionally, we already have a speech synthesis element around the AWS Polly API which
is also POST-based, so both elements can share a similar design.&lt;/p&gt;
&lt;h2&gt;Audio resampling&lt;/h2&gt;
&lt;p&gt;As mentioned previously, the ElevenLabs API does not offer direct control over the duration
of the output audio.&lt;/p&gt;
&lt;p&gt;For instance, you might be dubbing speech from a fast speaker with a slow voice, potentially
causing the output audio to drift out of sync.&lt;/p&gt;
&lt;p&gt;To address this, the element can optionally make use of &lt;a href=&quot;https://github.com/colinmarc/signalsmith-stretch-rs&quot;&gt;signalsmith_stretch&lt;/a&gt; to resample
the audio in a pitch-preserving manner.&lt;/p&gt;
&lt;p&gt;When the feature is enabled it can be used through the &lt;code&gt;overflow=compress&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;The effect can sometimes be pretty jarring for very short input, so an extra property is
also exposed to allow some tolerance for drift: &lt;code&gt;max-overflow&lt;/code&gt;. It represents the maximum
duration by which the audio output is allow to drift out of sync, and does a good job
using up intervals of silence between utterances.&lt;/p&gt;
&lt;h2&gt;Voice cloning&lt;/h2&gt;
&lt;p&gt;The ElevenLabs API exposes a pretty powerful feature, &lt;a href=&quot;https://elevenlabs.io/docs/api-reference/voices/ivc/create&quot;&gt;Instant Voice Cloning&lt;/a&gt;. It can be
used to create a custom voice that will sound very much like a reference voice, requiring
only a handful of seconds to a few minutes of reference audio data to produce useful
results.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;multilingual&lt;/code&gt; model, that newly-cloned voice can even be used to generate convincing
speech in a different language.&lt;/p&gt;
&lt;p&gt;A typical pipeline for my target use case can be represented as (pseudo gst-launch):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;input_audio_src ! transcriber ! translator ! synthesizer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using a transcriber element such as &lt;code&gt;speechmaticstranscriber&lt;/code&gt;, speaker &amp;quot;diarization&amp;quot;
(fancy word for detection) can be used to determine when a given speaker was speaking, thus
making it possible to clone voices even in a multi-speaker situation.&lt;/p&gt;
&lt;p&gt;The challenge in this situation however is that the synthesizer element doesn&#39;t have
access to the original audio samples, as it only deals with text as the input.&lt;/p&gt;
&lt;p&gt;I thus decided on the following solution:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;input_audio_src ! voicecloner ! transcriber ! .. ! synthesizer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The voice cloner element will accumulate audio samples, then upon receiving custom upstream
events from the transcriber element with information about speaker timings it will start
cloning voices and trim its internal sample queue.&lt;/p&gt;
&lt;p&gt;To be compatible, a transcriber simply needs to send the appropriate events upstream.
The &lt;code&gt;speechmaticstranscriber&lt;/code&gt; element can be used as a reference.&lt;/p&gt;
&lt;p&gt;Finally, once a voice clone is ready, the cloner element sends another event downstream
with a mapping of speaker id to voice id. The synthesizer element can then intercept the
event and start using the newly-created voice clone.&lt;/p&gt;
&lt;p&gt;The cloner element can also be used in single-speaker voice by just setting the &lt;code&gt;speaker&lt;/code&gt;
property to some identifier and watching for messages on the bus:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch-1.0 -m -e alsasrc ! audioconvert ! audioresample ! queue ! elevenlabsvoicecloner api-key=$SPEECHMATICS_API_KEY speaker=&amp;quot;Mathieu&amp;quot; ! fakesink
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;At this year&#39;s GStreamer conference I gave &lt;a href=&quot;https://gstconf.ubicast.tv/videos/costly-speech-an-introduction/&quot;&gt;a talk&lt;/a&gt; where I demo&#39;d these new elements.&lt;/p&gt;
&lt;p&gt;This is the pipeline I used then:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;XXX&quot;&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;XXX&quot;&lt;/span&gt; gst-launch-1.0 uridecodebin &lt;span class=&quot;token assign-left variable&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;file:///home/meh/Videos/spanish-convo-trimmed.webm &lt;span class=&quot;token assign-left variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ud &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  ud. &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; queue max-size-time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15000000000&lt;/span&gt; max-size-bytes&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; max-size-buffers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; clocksync &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; autovideosink &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  ud. &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; audioconvert &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; audioresample &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; clocksync &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; elevenlabsvoicecloner api-key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;XXX &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    speechmaticstranscriber &lt;span class=&quot;token assign-left variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;wss://eu2.rt.speechmatics.com/v2 enable-late-punctuation-hack&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false join-punctuation&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false api-key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;XXX&quot;&lt;/span&gt; max-delay&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2500&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;latency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4000&lt;/span&gt; language-code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;es &lt;span class=&quot;token assign-left variable&quot;&gt;diarization&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;speaker &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    queue max-size-time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15000000000&lt;/span&gt; max-size-bytes&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; max-size-buffers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; textaccumulate &lt;span class=&quot;token assign-left variable&quot;&gt;latency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt; drain-on-final-transcripts&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false extend-duration&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    awstranslate &lt;span class=&quot;token assign-left variable&quot;&gt;latency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; input-language-code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;es-ES&quot;&lt;/span&gt; output-language-code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;en-EN&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    elevenlabssynthesizer api-key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;XXX retry-with-speed&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false &lt;span class=&quot;token assign-left variable&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;compress &lt;span class=&quot;token assign-left variable&quot;&gt;latency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt; language-code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;en&quot;&lt;/span&gt; voice-id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;iCKVfVbyCo5AAswzTkkX&quot;&lt;/span&gt; model-id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;eleven_multilingual_v2&quot;&lt;/span&gt; max-overflow&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    queue max-size-time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15000000000&lt;/span&gt; max-size-bytes&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; max-size-buffers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; audiomixer &lt;span class=&quot;token assign-left variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; autoaudiosink audiotestsrc &lt;span class=&quot;token assign-left variable&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.03&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;wave&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;violet-noise &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; clocksync &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; m.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Watch my talk for the result, or try it yourself (you will need API keys for speechmatics / AWS / elevenlabs)!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Support for non-closed caption VANC with MXF in GStreamer</title>
    <author>
      <name>Sanchayan Maity</name>
      <email>sanchayan+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-11/mxf-non-closed-caption-vanc/" />
    <updated>2025-11-24T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-11/mxf-non-closed-caption-vanc/</id>
    <content type="html">&lt;p&gt;The GStreamer Material Exchange Format (MXF) muxer and demuxer elements so far only supported extracting Vertical Ancillary Data (VANC) as closed captions. Any other VANC data was silently dropped. This was primarily reflected by the sink pad template of &lt;code&gt;mxfmux&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  SINK template: &#39;vanc_sink_%u&#39;
    Availability: On request
    Capabilities:
      closedcaption/x-cea-708
                 format: cdp
              framerate: [ 0/1, 2147483647/1 ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mxfmux&lt;/code&gt; and &lt;code&gt;mxfdemux&lt;/code&gt; have now been extended to support arbitrary VANC data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pub.smpte.org/latest/st436-1/st0436-1-2013.pdf&quot;&gt;SMPTE 436 (pdf)&lt;/a&gt; specification defines how the ancillary data is stored in MXF. &lt;a href=&quot;https://pub.smpte.org/pub/st2038/st2038-2021.pdf&quot;&gt;SMPTE 2038 (pdf)&lt;/a&gt; defines the carriage of Ancillary Data Packets in an MPEG-2 Transport Stream acting as a more structured format (ST2038) in comparison to the line-based format (ST436M). &lt;code&gt;mxfdemux&lt;/code&gt; converts from ST436M to ST2038 while &lt;code&gt;mxfmux&lt;/code&gt; converts from ST2038 to ST436M. So &lt;code&gt;mxfdemux&lt;/code&gt; now outputs VANC (ST436M) essence tracks as ST2038 streams and &lt;code&gt;mxfmux&lt;/code&gt; consumes ST2038 streams to output VANC (ST436M) essence tracks.&lt;/p&gt;
&lt;p&gt;A breaking change was introduced to support this in the muxer, by updating the acceptable caps on the pad. The sink pad template of &lt;code&gt;mxfmux&lt;/code&gt; has now changed to &lt;code&gt;meta/x-st-2038&lt;/code&gt; instead of the earlier &lt;code&gt;closedcaption/x-cea-708&lt;/code&gt;. Applications can use &lt;code&gt;cctost2038anc&lt;/code&gt; for converting closed captions to ST2038.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  SINK template: &#39;vanc_sink_%u&#39;
    Availability: On request
    Capabilities:
      meta/x-st-2038
              alignment: frame (gchararray)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the pad templates of &lt;code&gt;mxfdemux&lt;/code&gt; haven&#39;t changed as shown below, the caps on the source pad are going to be &lt;code&gt;meta/x-st-2038&lt;/code&gt; for VANC data, so applications have to handle different caps now. Closed captions can be extracted via &lt;code&gt;st2038anctocc&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  SRC template: &#39;track_%u&#39;
    Availability: Sometimes
    Capabilities:
      ANY
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The older behaviour is still available via an environment variable &lt;code&gt;GST_VANC_AS_CEA708&lt;/code&gt;. In addition, &lt;code&gt;mxfdemux&lt;/code&gt; can now read both, 8-bit and 10-bit VANC data from MXF files.&lt;/p&gt;
&lt;p&gt;The ST2038 elements available in Rust plugins and described in an earlier post &lt;a href=&quot;https://centricular.com/devlog/2024-10/st-2038-ancillary-data-and-captions/&quot;&gt;here&lt;/a&gt;, have also seen some fixes for correctly handling &lt;code&gt;alignment&lt;/code&gt; and &lt;code&gt;framerate&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Blackmagic DeckLink support for handling arbitrary VANC data</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-11/decklink-vanc/" />
    <updated>2025-11-21T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-11/decklink-vanc/</id>
    <content type="html">&lt;p&gt;As part of our ongoing efforts to extend GStreamer&#39;s support for ancillary data, I&#39;ve recently improved the ancillary data handling in the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/decklink/&quot;&gt;Blackmagic DeckLink&lt;/a&gt; plugin. This plugin can be used to capture or output SDI/HDMI/ST2110 streams with Blackmagic DeckLink capture/output cards.&lt;/p&gt;
&lt;p&gt;Previously only CEA 608/708 closed captions and AFD/Bar ancillary data was handled in that plugin. Now it can also additionally handle any other kind of ancillary data via &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/video/gstvideoanc.html#GstAncillaryMeta&quot;&gt;&lt;code&gt;GstAncillaryMeta&lt;/code&gt;&lt;/a&gt; and leave interpretation or handling of the concrete payload to the application or other elements.&lt;/p&gt;
&lt;p&gt;This new behaviour was added in &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/10111&quot;&gt;this MR&lt;/a&gt;, which is part of git main now, and can be enabled via the &lt;code&gt;output-vanc&lt;/code&gt; properties on the video source / sink elements.&lt;/p&gt;
&lt;p&gt;The same was already supported before by the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/aja/&quot;&gt;plugin for AJA capture/output cards&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example the following pipeline can be used to forward an SDI stream from an one DeckLink card to an AJA card&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gst-launch-1.0 decklinkvideosrc output-vanc&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; queue &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; combiner.video &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  decklinkaudiosrc &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; queue &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; combiner.audio &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  ajasinkcombiner &lt;span class=&quot;token assign-left variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;combiner &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; ajasink handle-ancillary-meta&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With both the AJA and DeckLink sink elements, special care is needed to not e.g. output closed captions twice. Both sinks can retrieve them from &lt;code&gt;GstVideoClosedCaptionMeta&lt;/code&gt; and &lt;code&gt;GstAncillaryMeta&lt;/code&gt;, and outputting from both will likely lead to problems at the consumer of the output.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Support for SMPTE ST 291-1 ancillary data over RTP</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-11/st291-anc-rtp/" />
    <updated>2025-11-18T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-11/st291-anc-rtp/</id>
    <content type="html">&lt;p&gt;While working on other ancillary data related features in GStreamer (more on that some other day), I noticed that we didn&#39;t have support for sending or receiving ancillary data via RTP in GStreamer despite it being a quite simple RTP mapping defined in &lt;a href=&quot;https://datatracker.ietf.org/doc/rfc8331/&quot;&gt;RFC 8331&lt;/a&gt; and it being used as part of ST 2110.&lt;/p&gt;
&lt;p&gt;The new RTP &lt;code&gt;rtpsmpte291pay&lt;/code&gt; payloader and &lt;code&gt;rtpsmpte291depay&lt;/code&gt; depayloader can be found in &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2691&quot;&gt;this MR&lt;/a&gt; for &lt;code&gt;gst-plugins-rs&lt;/code&gt;, which should be merged in the next days.&lt;/p&gt;
&lt;p&gt;The new elements pass the SMPTE ST 291-1 ancillary data as ST 2038 streams through the pipeline. ST 2038 streams can be directly extracted from or stored in MXF or MPEG-TS containers, can be extracted or inserted into SDI streams with the AJA or Blackmagic Decklink sources/sinks, or can be handled generically by the ST 2038 elements from the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rsclosedcaption/&quot;&gt;&lt;code&gt;rsclosedcaption&lt;/code&gt;&lt;/a&gt; plugin.&lt;/p&gt;
&lt;p&gt;For example the following pipeline can be used to convert an SRT subtitle file to CEA-708 closed captions, which are then converted to an ST 2038 stream and sent over RTP:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ gst-launch-1.0 filesrc &lt;span class=&quot;token assign-left variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;file.srt &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; subparse &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    tttocea708 &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; closedcaption/x-cea-708,framerate&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;/1 &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; ccconverter &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    cctost2038anc &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; rtpsmpte291pay &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    udpsink &lt;span class=&quot;token assign-left variable&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123.123&lt;/span&gt;.123.123 &lt;span class=&quot;token assign-left variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;45678&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you might be wondering how ST 291-1 and ST 2038 are related to each other and what ST 2038 has to do with RTP.&lt;/p&gt;
&lt;p&gt;ST 291-1 is the basic standard that defines the packet format for ancillary packets as e.g. transmitted over SDI. ST 2038 on the other hand defines a mechanism for packaging ST 291-1 into MPEG-TS, and in addition to the plain ST 291-1 packets provides some additional information like the line number on which the ST 291-1 packet is to be stored. RFC 8331 defines a similar mapping just for RTP, and apart from one field it provides exactly the same information and conversion between the two formats is relatively simple.&lt;/p&gt;
&lt;p&gt;Using ST 2038 as generic ancillary data stream format in GStreamer seemed like the pragmatic choice here. GStreamer already had support for handling ST 2038 streams in various elements, a set of helper elements to handle ST 2038 streams, and e.g. GStreamer&#39;s MXF ANC support (ST 436) also uses ST 2038 as stream format.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Multitrack Audio Capability in GStreamer FLV Plugin</title>
    <author>
      <name>Taruntej Kanakamalla</name>
      <email>tarun+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-11/Multitrack-Audio-Capability-in-FLV/" />
    <updated>2025-11-10T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-11/Multitrack-Audio-Capability-in-FLV/</id>
    <content type="html">&lt;p&gt;For one of our recent projects, we worked on adding multitrack audio capabilities to the GStreamer FLV plugin following the &lt;a href=&quot;https://veovera.org/docs/enhanced/enhanced-rtmp-v2.html&quot;&gt;Enhanced RTMP (v2)&lt;/a&gt; specification.
All changes are now merged upstream (see &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9682&quot;&gt;MR 9682&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Enhanced RTMP&lt;/h3&gt;
&lt;p&gt;As the name suggests, this is an enhancement to the RTMP (and FLV) specifications. The latest version was released earlier this year and is aimed at meeting the technical standards of current and future online media broadcasting requirements, which include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Contemporary audio/video codecs (HEVC, AV1, Opus, FLAC, etc.)&lt;/li&gt;
&lt;li&gt;Multitrack capabilities (for concurrent management and processing)&lt;/li&gt;
&lt;li&gt;Connection stability and resilience&lt;/li&gt;
&lt;li&gt;and &lt;a href=&quot;https://veovera.org/docs/enhanced/enhanced-rtmp-v2.html#abstract&quot;&gt;more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;FLV and RTMP in GStreamer&lt;/h3&gt;
&lt;p&gt;The existing &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/flv/index.html?gi-language=c&quot;&gt;FLV&lt;/a&gt; and &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rtmp2/index.html?gi-language=c&quot;&gt;RTMP2&lt;/a&gt; plugins followed the previous versions of the RTMP/FLV specifications, so they could handle at most one video and one audio track at a time. This is where most of the work was needed, to add the ability to handle multiple tracks.&lt;/p&gt;
&lt;h3&gt;Multitrack Audio&lt;/h3&gt;
&lt;p&gt;We considered a couple of options for adding multitrack audio and enhanced FLV capabilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write completely new element(s), preferably in Rust (or)&lt;/li&gt;
&lt;li&gt;Extend the current FLV muxer and demuxer elements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Writing a fresh set of elements from scratch, perhaps even in Rust, would have potentially made it easier to accommodate newer versions of the specification. But the second option, extending the existing FLV muxer/demuxer elements turned out to be simpler.&lt;/p&gt;
&lt;h3&gt;Problems to Solve&lt;/h3&gt;
&lt;p&gt;So, at a high level, we had two problems to solve:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Handle multiple tracks&lt;/p&gt;
&lt;p&gt;As mentioned above, the FLV and RTMP plugins were equipped to handle only one audio and one video track. So we needed to add support for handling multiple audio and video tracks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Maintain backwards compatibility&lt;/p&gt;
&lt;p&gt;There should be no breakage in any existing applications that stream using the legacy FLV format. So, the muxer needs a mechanism to decide whether a given audio input needs to be written into the FLV container in the enhanced format or the legacy format.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;A two-step solution&lt;/h3&gt;
&lt;p&gt;We arrived at a two-step solution for the implementation of multiple track handling:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Use the &lt;code&gt;audio&lt;/code&gt; template pad only for the legacy format and define a new &lt;code&gt;audio_%u&lt;/code&gt; template for the enhanced format. That makes it clear which stream needs to be written as a legacy FLV track or an enhanced FLV track. The index of the &lt;code&gt;audio_%u&lt;/code&gt; pads is also used as the track ID when writing enhanced FLV.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Derive a new element from the existing FLV muxer called &lt;code&gt;eflvmux&lt;/code&gt;, which defines the new &lt;code&gt;audio_%u&lt;/code&gt; pad templates. The old &lt;code&gt;flvmux&lt;/code&gt; will continue to support only the legacy codec/format. That way, the existing applications that use &lt;code&gt;flvmux&lt;/code&gt; for legacy FLV streaming will not face any conflicts while requesting the pads.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Minor Caveat&lt;/h3&gt;
&lt;p&gt;Note that applications that use &lt;code&gt;eflvmux&lt;/code&gt; need to specify the correct pad template name (&lt;code&gt;audio&lt;/code&gt; or &lt;code&gt;audio_%u&lt;/code&gt;) when requesting sink pads to ensure that the input audio data is written to the correct FLV track (legacy or enhanced).&lt;/p&gt;
&lt;p&gt;Some formats such as MP3 and AAC are supported in both legacy and enhanced tracks, so we can&#39;t just auto-detect the right thing to do.&lt;/p&gt;
&lt;h3&gt;Interoperability issues&lt;/h3&gt;
&lt;p&gt;An interesting thing we noticed while testing streaming of multitrack audio with &lt;a href=&quot;https://twitch.tv&quot;&gt;Twitch.tv&lt;/a&gt; is that when we tried to stream multiple enhanced FLV tracks or a mix of single legacy track and one or more enhanced FLV tracks, none of the combinations worked.&lt;/p&gt;
&lt;p&gt;On the other hand, OBS was able to stream multitrack audio just fine to the same endpoint. Dissecting the RTMP packets sent out by OBS revealed that Twitch can accept at most two tracks, one legacy and one enhanced, and the enhanced FLV track&#39;s ID needs to be a non-zero value. To our knowledge, this is not documented anywhere.&lt;/p&gt;
&lt;p&gt;It was a simple matter of track ID semantics which could be easily missed without referring to the &lt;a href=&quot;https://github.com/obsproject/obs-studio/&quot;&gt;OBS Studio&lt;/a&gt; code. This is also the case with &lt;a href=&quot;https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/HEAD:/libavformat/flvenc.c&quot;&gt;FFmpeg&lt;/a&gt; which we recently noticed.&lt;/p&gt;
&lt;p&gt;So we have requested a &lt;a href=&quot;https://github.com/veovera/enhanced-rtmp/issues/48&quot;&gt;clarification&lt;/a&gt; on the track ID semantics from the enhanced RTMP specification maintainers and got a confirmation that 0 remains a valid value for track ID. As mentioned in the specification, it can be used to represent the highest priority track or the default track.&lt;/p&gt;
&lt;p&gt;However, when streaming to servers like Twitch you may need to take care to request only pads with index greater than 0 from &lt;code&gt;eflvmux&lt;/code&gt; because it may not accept tracks with ID 0.&lt;/p&gt;
&lt;h3&gt;Sample Pipelines to test&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://notes.gstreamer.org/NSoRjOTyS1Ko8be3bI78ow#&quot;&gt;Here&lt;/a&gt; are some sample pipelines I used for testing the muxer and demuxer during the implementation.&lt;/p&gt;
&lt;h3&gt;Scope for other features&lt;/h3&gt;
&lt;p&gt;The FLV muxer and demuxer have undergone significant structural changes in order to support multiple audio tracks. This should make it easy to update the existing multitrack video capability &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8398&quot;&gt;merge request&lt;/a&gt; as well as add support for advanced codecs listed in the specification, some of which (like &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9873&quot;&gt;H265&lt;/a&gt; and &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5087&quot;&gt;AV1&lt;/a&gt;) are already in progress.&lt;/p&gt;
&lt;p&gt;There is also a work-in-progress &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9806&quot;&gt;merge request&lt;/a&gt; to add the eRTMP related support to the &lt;code&gt;rtmp2&lt;/code&gt; plugin.&lt;/p&gt;
&lt;p&gt;P.S.: You can also refer to my &lt;a href=&quot;https://indico.freedesktop.org/event/11/contributions/500/&quot;&gt;talk&lt;/a&gt; on this topic at the &lt;a href=&quot;https://gstreamer.freedesktop.org/conference/2025/&quot;&gt;GStreamer Conference&lt;/a&gt; that took place in London last month. The recording will be soon published on &lt;a href=&quot;https://gstconf.ubicast.tv/channels/gstreamer-conference-2025&quot;&gt;Ubicast&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Linking and shrinking Rust static libraries: a tale of fire</title>
    <author>
      <name>Amy</name>
      <email>amy+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-11/dragonfire/" />
    <updated>2025-11-03T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-11/dragonfire/</id>
    <content type="html">&lt;p&gt;At the GStreamer project, we produce SDKs for lots of platforms: Linux, Android,
macOS, iOS, and Windows. However, as we port more and more plugins to Rust
🦀, we are finding ourselves backed into a corner.&lt;/p&gt;
&lt;p&gt;Rust static libraries are simply too big.&lt;/p&gt;
&lt;p&gt;To give you an example, the AWS folks changed their SDK back in March to
switch their cryptographic toolkit over to their &lt;code&gt;aws-lc-rs&lt;/code&gt; crate &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fn-dragonfire-1&quot; id=&quot;fnref-dragonfire-1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.
However, that causes a 2-10x increase in code size (bug reports
&lt;a href=&quot;https://github.com/aws/aws-lc-rs/issues/745&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://github.com/smithy-lang/smithy-rs/issues/4049&quot;&gt;here&lt;/a&gt;),
which gets duplicated on every plugin that makes use of their ecosystem!&lt;/p&gt;
&lt;h2&gt;What are Rust staticlibs made of?&lt;/h2&gt;
&lt;p&gt;To summarise, each Rust plugin packs a copy of its dependencies, plus a copy of
the Rust standard library. This is not a problem on shared libraries and
executables by their very nature, but on static libraries it causes several
issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/rust/issues/104707&quot;&gt;Rust leaks unexported symbols from native staticlibs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On some platform, &lt;a href=&quot;https://github.com/rust-lang/rust/issues/44322&quot;&gt;linking against multiple Rust staticlibs is impossible&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;First approach: Single-Object Prelinking&lt;/h2&gt;
&lt;p&gt;I won&#39;t bore you with the details as I&#39;ve written
&lt;a href=&quot;https://www.amyspark.me/blog/posts/2024/01/10/stripping-rust-libraries.html&quot;&gt;another blog post&lt;/a&gt;
on the subject; the gist is that you can unpack the library, and then
ask the linker to perform
&lt;a href=&quot;https://maskray.me/blog/2022-11-21-relocatable-linking&quot;&gt;&amp;quot;partial linking&amp;quot; or &amp;quot;relocatable linking&amp;quot;&lt;/a&gt; (Linux term)
or &lt;a href=&quot;https://developer.apple.com/documentation/xcode/build-settings-reference#Perform-Single-Object-Prelink&quot;&gt;&amp;quot;Single-Object Prelinking&amp;quot;&lt;/a&gt; (the Apple term, which I&#39;ll use throughout the post)
over the object files. Setting which symbols you want to be visible
for downstream consumers lets dead-code elimination take place at the plugin
level, ensuring your libraries are now back to a reasonable size.&lt;/p&gt;
&lt;h2&gt;Why is it not enough?&lt;/h2&gt;
&lt;p&gt;Single-Object Prelinking has two drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unoptimized code: the linker won&#39;t be able to deduplicate functions between melded objects, as they&#39;ve been hidden by the prelinking process.&lt;/li&gt;
&lt;li&gt;Windows: there are no officially supported tools (read: Visual Studio, LLVM, GCC) to perform this at the compiler level. It is possible to do this with binutils, but the PE-COFF format doesn&#39;t allow to change the visibility of unexported functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Melt all the object files with the power of dragons&#39; fire!&lt;/h2&gt;
&lt;p&gt;As said earlier, &lt;a href=&quot;https://github.com/rust-lang/rust/issues/44322#issuecomment-3253730349&quot;&gt;no tools on Windows support prelinking officially yet&lt;/a&gt;,
but there&#39;s another thing we can do: library deduplication.&lt;/p&gt;
&lt;p&gt;Thanks to Rust&#39;s comprehensive crate ecosystem, I wrote a new CLI tool which I
called &lt;code&gt;dragonfire&lt;/code&gt;. Given a complete Rust workspace or list of static
libraries, &lt;code&gt;dragonfire&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;reads all the static libraries in one pass&lt;/li&gt;
&lt;li&gt;deduplicates the object files inside them based on their size and naming (Rust has its own, unique naming convention for object files -- pretty useful!)&lt;/li&gt;
&lt;li&gt;copies the duplicate objects into a new static library (usually called &lt;code&gt;gstrsworkspace&lt;/code&gt; as its primary use is for the GStreamer ecosystem)&lt;/li&gt;
&lt;li&gt;removes the duplicates from the rest of the libraries&lt;/li&gt;
&lt;li&gt;updates the symbol table in each of the libraries with the bundled LLVM tools&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks to the &lt;a href=&quot;https://docs.rs/ar/latest/ar/&quot;&gt;ar&lt;/a&gt; crate, the unpacking and
writing only happens at stage 3, ensuring no wasteful I/O slowdowns takes place.
The &lt;a href=&quot;https://docs.rs/llvm-tools/0.1.1/llvm_tools/&quot;&gt;llvm-tools-preview&lt;/a&gt; component
in turn takes care of locating and calling up &lt;code&gt;llvm-ar&lt;/code&gt; for updating the
workspace&#39;s symbol tables.&lt;/p&gt;
&lt;p&gt;A special mention is deserved to the object files&#39; naming convention. Assume a
Rust staticlib named &lt;code&gt;libfoo&lt;/code&gt;, its object files will be named as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;crate_name-hash1.crate_name.hash2-cgu.nnn.rcgu.o&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;On Windows only: &lt;code&gt;foo.crate_name-hash1.crate_name.hash2-cgu.nnn.rcgu.o&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;On non-Windows platforms: same as above, but replacing &lt;code&gt;foo&lt;/code&gt; with &lt;code&gt;libfoo-hash&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In all cases, &lt;code&gt;crate_name&lt;/code&gt; means a dependency present somewhere in the
workspace tree, and&lt;code&gt; nnn&lt;/code&gt; is a number that will be bigger than zero whenever
&lt;code&gt;-C codegen-units&lt;/code&gt; was set to higher than 1.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;dragonfire&lt;/code&gt; purposes, dropping the library prefix is enough to be able to
deduplicate object files; however, on Windows we can also find import
library stubs, which LLVM can generate on its own by the use of the &lt;code&gt;#[raw-dylib]&lt;/code&gt; annotation &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fn-dragonfire-2&quot; id=&quot;fnref-dragonfire-2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Import stubs can have any extension, e.g. &lt;code&gt;.dll&lt;/code&gt;,
&lt;code&gt;.exe&lt;/code&gt; and &lt;code&gt;.sys&lt;/code&gt; (the latter two coming from private Win32 APIs).
These stubs cannot be deduplicated as they are generated
individually per imported function, so &lt;code&gt;dragonfire&lt;/code&gt; must preserve them where
they are.&lt;/p&gt;
&lt;h2&gt;Drawbacks of object file deduplication&lt;/h2&gt;
&lt;p&gt;Again there are several disadvantages of this approach. On Apple platforms,
deduplicating libraries triggers a &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1935/diffs#d28b7f11a60bdebf2288eb9a6402c284c5434904_390_390&quot;&gt;strange linker error&lt;/a&gt;, which I&#39;ve not seen
before:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;text-wrap&quot;&gt;ld: multiple errors: compact unwind must have at least 1 fixup in &#39;&amp;lt;framework&amp;gt;/GStreamer[arm64][1021](libgstrsworkspace_a-3f2b47962471807d-lse_ldset4_acq.o)&#39;; r_symbolnum=-19 out of range in &#39;&amp;lt;framework&amp;gt;/GStreamer[arm64][1022](libgstrsworkspace_a-compiler_builtins-350c23344d78cfbc.compiler_builtins.5e126dca1f5284a9-cgu.162.rcgu.o)&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also led me to find that &lt;a href=&quot;https://github.com/rust-lang/rust/issues/146145&quot;&gt;Rust libraries were packing bitcode, which is forbidden by Apple&lt;/a&gt;. (This was thankfully &lt;a href=&quot;https://github.com/rust-lang/rust/pull/146133&quot;&gt;already fixed&lt;/a&gt; before shipping time, but we&#39;ve not yet updated our Rust minimum version to take advantage of it.)&lt;/p&gt;
&lt;p&gt;Another drawback is that Rust&#39;s implementation of LTO causes dead-code
elimination at the crate level, as opposed to the workspace level. This makes
object file deduplication impossible, as each copy is different.&lt;/p&gt;
&lt;p&gt;For the Windows platform, there is an extra drawback which affects specifically
object files produced by LLVM: &lt;a href=&quot;https://github.com/rust-lang/rust/issues/143931&quot;&gt;the COMDAT sections are set to &lt;code&gt;IMAGE_COMDAT_SELECT_NODUPLICATES&lt;/code&gt;&lt;/a&gt;.
This means that the linker will outright reject functions with multiple
definitions, rather than realise they&#39;re all duplicates and discarding all but
one of the copies. MSVC in particular &lt;a href=&quot;https://developercommunity.visualstudio.com/t/unresolved-external-symbol-referenced-in-function/1016851#TPIN-N1078418&quot;&gt;performs symbol resolution
&lt;em&gt;before&lt;/em&gt; dead-code elimination&lt;/a&gt;.
This means that linking will fail because of unresolved symbols &lt;em&gt;before&lt;/em&gt; dead code elimination kicks in; to use deduplicated libraries, one must set the linker flags
&lt;code&gt;/OPT:REF /FORCE:UNRESOLVED&lt;/code&gt; to ensure the dead code can be successfully
eliminated.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;With library deduplication, we can make static libraries up to 44x smaller
when building under MSVC &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fn-dragonfire-3&quot; id=&quot;fnref-dragonfire-3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; (you can expand the tables below for the full
comparison):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gstaws.lib: from 173M to 71M (~2.5x)&lt;/li&gt;
&lt;li&gt;gstrswebrtc.lib: from 193M to 66M (~2.9x)&lt;/li&gt;
&lt;li&gt;gstwebrtchttp.lib: from 66M to 1,5M (~ 44x)&lt;/li&gt;
&lt;/ul&gt;
&lt;details markdown=&quot;1&quot; class=&quot;mb-3&quot;&gt;&lt;summary&gt;Table: before and after melding under MSVC&lt;/summary&gt;
&lt;table class=&quot;table table-striped table-sm table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;file&lt;/th&gt;
&lt;th&gt;no prelinking&lt;/th&gt;
&lt;th&gt;melded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gstaws.lib&lt;/td&gt;
&lt;td&gt;173M&lt;/td&gt;
&lt;td&gt;71M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstcdg.lib&lt;/td&gt;
&lt;td&gt;36M&lt;/td&gt;
&lt;td&gt;572K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstclaxon.lib&lt;/td&gt;
&lt;td&gt;32M&lt;/td&gt;
&lt;td&gt;568K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstdav1d.lib&lt;/td&gt;
&lt;td&gt;34M&lt;/td&gt;
&lt;td&gt;936K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstelevenlabs.lib&lt;/td&gt;
&lt;td&gt;59M&lt;/td&gt;
&lt;td&gt;1008K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstfallbackswitch.lib&lt;/td&gt;
&lt;td&gt;37M&lt;/td&gt;
&lt;td&gt;2,3M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstffv1.lib&lt;/td&gt;
&lt;td&gt;34M&lt;/td&gt;
&lt;td&gt;744K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstfmp4.lib&lt;/td&gt;
&lt;td&gt;39M&lt;/td&gt;
&lt;td&gt;3,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstgif.lib&lt;/td&gt;
&lt;td&gt;34M&lt;/td&gt;
&lt;td&gt;1,1M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstgopbuffer.lib&lt;/td&gt;
&lt;td&gt;30M&lt;/td&gt;
&lt;td&gt;456K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsthlsmultivariantsink.lib&lt;/td&gt;
&lt;td&gt;46M&lt;/td&gt;
&lt;td&gt;1,6M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsthlssink3.lib&lt;/td&gt;
&lt;td&gt;41M&lt;/td&gt;
&lt;td&gt;1,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsthsv.lib&lt;/td&gt;
&lt;td&gt;34M&lt;/td&gt;
&lt;td&gt;796K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstjson.lib&lt;/td&gt;
&lt;td&gt;31M&lt;/td&gt;
&lt;td&gt;704K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstlewton.lib&lt;/td&gt;
&lt;td&gt;33M&lt;/td&gt;
&lt;td&gt;1,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstlivesync.lib&lt;/td&gt;
&lt;td&gt;33M&lt;/td&gt;
&lt;td&gt;728K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstmp4.lib&lt;/td&gt;
&lt;td&gt;38M&lt;/td&gt;
&lt;td&gt;2,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstmpegtslive.lib&lt;/td&gt;
&lt;td&gt;31M&lt;/td&gt;
&lt;td&gt;704K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstndi.lib&lt;/td&gt;
&lt;td&gt;38M&lt;/td&gt;
&lt;td&gt;2,8M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstoriginalbuffer.lib&lt;/td&gt;
&lt;td&gt;34M&lt;/td&gt;
&lt;td&gt;376K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstquinn.lib&lt;/td&gt;
&lt;td&gt;75M&lt;/td&gt;
&lt;td&gt;23M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstraptorq.lib&lt;/td&gt;
&lt;td&gt;33M&lt;/td&gt;
&lt;td&gt;2,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrav1e.lib&lt;/td&gt;
&lt;td&gt;46M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstregex.lib&lt;/td&gt;
&lt;td&gt;38M&lt;/td&gt;
&lt;td&gt;404K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstreqwest.lib&lt;/td&gt;
&lt;td&gt;58M&lt;/td&gt;
&lt;td&gt;1,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsanalytics.lib&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;1000K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsaudiofx.lib&lt;/td&gt;
&lt;td&gt;54M&lt;/td&gt;
&lt;td&gt;22M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsclosedcaption.lib&lt;/td&gt;
&lt;td&gt;52M&lt;/td&gt;
&lt;td&gt;8,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsinter.lib&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;604K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsonvif.lib&lt;/td&gt;
&lt;td&gt;46M&lt;/td&gt;
&lt;td&gt;2,0M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrspng.lib&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;1,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsrtp.lib&lt;/td&gt;
&lt;td&gt;59M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsrtsp.lib&lt;/td&gt;
&lt;td&gt;57M&lt;/td&gt;
&lt;td&gt;4,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrstracers.lib&lt;/td&gt;
&lt;td&gt;40M&lt;/td&gt;
&lt;td&gt;2,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsvideofx.lib&lt;/td&gt;
&lt;td&gt;48M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrswebrtc.lib&lt;/td&gt;
&lt;td&gt;193M&lt;/td&gt;
&lt;td&gt;66M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstrsworkspace.lib&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;137M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gststreamgrouper.lib&lt;/td&gt;
&lt;td&gt;30M&lt;/td&gt;
&lt;td&gt;376K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsttextahead.lib&lt;/td&gt;
&lt;td&gt;30M&lt;/td&gt;
&lt;td&gt;332K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsttextwrap.lib&lt;/td&gt;
&lt;td&gt;32M&lt;/td&gt;
&lt;td&gt;2,1M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstthreadshare.lib&lt;/td&gt;
&lt;td&gt;52M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsttogglerecord.lib&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;808K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gsturiplaylistbin.lib&lt;/td&gt;
&lt;td&gt;31M&lt;/td&gt;
&lt;td&gt;648K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstvvdec.lib&lt;/td&gt;
&lt;td&gt;34M&lt;/td&gt;
&lt;td&gt;564K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gstwebrtchttp.lib&lt;/td&gt;
&lt;td&gt;66M&lt;/td&gt;
&lt;td&gt;1,5M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/details&gt;
&lt;p&gt;The results from the melding above can be compared with the file sizes obtained
using LTO on Windows &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fn-dragonfire-4&quot; id=&quot;fnref-dragonfire-4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; (remember it doesn&#39;t actually fix
linking against plugins):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gstaws.lib: from 71M (LTO) to 67M (melded) (-5.6%)&lt;/li&gt;
&lt;li&gt;gstrswebrtc.lib: from 105M to 66M (-37.1%)&lt;/li&gt;
&lt;li&gt;gstwebrtchttp.lib: from 28M to 1,5M (-94.6%)&lt;/li&gt;
&lt;/ul&gt;
&lt;details markdown=&quot;1&quot; class=&quot;mb-3&quot;&gt;&lt;summary&gt;Table: before and after LTO under MSVC (no melding involved)&lt;/summary&gt;
&lt;table class=&quot;table table-striped table-sm table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;file (codegen-units=1 in all cases)&lt;/th&gt;
&lt;th&gt;no prelinking&lt;/th&gt;
&lt;th&gt;lto=thin&lt;/th&gt;
&lt;th&gt;opt-level=s + lto=thin&lt;/th&gt;
&lt;th&gt;debug=1 + opt-level=s&lt;/th&gt;
&lt;th&gt;debug=1  + lto=thin + opt-level=s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;old/gstaws.lib&lt;/td&gt;
&lt;td&gt;199M&lt;/td&gt;
&lt;td&gt;199M&lt;/td&gt;
&lt;td&gt;171M&lt;/td&gt;
&lt;td&gt;78M&lt;/td&gt;
&lt;td&gt;67M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstcdg.lib&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;7,5M&lt;/td&gt;
&lt;td&gt;7,5M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstclaxon.lib&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;7,7M&lt;/td&gt;
&lt;td&gt;7,7M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstdav1d.lib&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;7,9M&lt;/td&gt;
&lt;td&gt;7,8M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstelevenlabs.lib&lt;/td&gt;
&lt;td&gt;52M&lt;/td&gt;
&lt;td&gt;52M&lt;/td&gt;
&lt;td&gt;49M&lt;/td&gt;
&lt;td&gt;24M&lt;/td&gt;
&lt;td&gt;22M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstfallbackswitch.lib&lt;/td&gt;
&lt;td&gt;18M&lt;/td&gt;
&lt;td&gt;18M&lt;/td&gt;
&lt;td&gt;17M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstffv1.lib&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;7,6M&lt;/td&gt;
&lt;td&gt;7,6M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstfmp4.lib&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;td&gt;19M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstgif.lib&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;7,9M&lt;/td&gt;
&lt;td&gt;7,9M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstgopbuffer.lib&lt;/td&gt;
&lt;td&gt;9,7M&lt;/td&gt;
&lt;td&gt;9,7M&lt;/td&gt;
&lt;td&gt;9,7M&lt;/td&gt;
&lt;td&gt;7,5M&lt;/td&gt;
&lt;td&gt;7,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsthlsmultivariantsink.lib&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsthlssink3.lib&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;8,9M&lt;/td&gt;
&lt;td&gt;8,8M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsthsv.lib&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;7,8M&lt;/td&gt;
&lt;td&gt;7,7M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstjson.lib&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;8,4M&lt;/td&gt;
&lt;td&gt;8,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstlewton.lib&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;8,1M&lt;/td&gt;
&lt;td&gt;8,1M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstlivesync.lib&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;8,3M&lt;/td&gt;
&lt;td&gt;8,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstmp4.lib&lt;/td&gt;
&lt;td&gt;17M&lt;/td&gt;
&lt;td&gt;17M&lt;/td&gt;
&lt;td&gt;17M&lt;/td&gt;
&lt;td&gt;9,9M&lt;/td&gt;
&lt;td&gt;9,7M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstmpegtslive.lib&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;8,0M&lt;/td&gt;
&lt;td&gt;7,9M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstndi.lib&lt;/td&gt;
&lt;td&gt;21M&lt;/td&gt;
&lt;td&gt;21M&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;td&gt;12M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstoriginalbuffer.lib&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,7M&lt;/td&gt;
&lt;td&gt;7,4M&lt;/td&gt;
&lt;td&gt;7,3M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstquinn.lib&lt;/td&gt;
&lt;td&gt;94M&lt;/td&gt;
&lt;td&gt;94M&lt;/td&gt;
&lt;td&gt;86M&lt;/td&gt;
&lt;td&gt;39M&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstraptorq.lib&lt;/td&gt;
&lt;td&gt;18M&lt;/td&gt;
&lt;td&gt;18M&lt;/td&gt;
&lt;td&gt;17M&lt;/td&gt;
&lt;td&gt;9,8M&lt;/td&gt;
&lt;td&gt;9,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrav1e.lib&lt;/td&gt;
&lt;td&gt;39M&lt;/td&gt;
&lt;td&gt;39M&lt;/td&gt;
&lt;td&gt;37M&lt;/td&gt;
&lt;td&gt;19M&lt;/td&gt;
&lt;td&gt;18M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstregex.lib&lt;/td&gt;
&lt;td&gt;26M&lt;/td&gt;
&lt;td&gt;26M&lt;/td&gt;
&lt;td&gt;25M&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstreqwest.lib&lt;/td&gt;
&lt;td&gt;53M&lt;/td&gt;
&lt;td&gt;53M&lt;/td&gt;
&lt;td&gt;49M&lt;/td&gt;
&lt;td&gt;24M&lt;/td&gt;
&lt;td&gt;22M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsanalytics.lib&lt;/td&gt;
&lt;td&gt;15M&lt;/td&gt;
&lt;td&gt;15M&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;9,2M&lt;/td&gt;
&lt;td&gt;8,9M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsaudiofx.lib&lt;/td&gt;
&lt;td&gt;57M&lt;/td&gt;
&lt;td&gt;57M&lt;/td&gt;
&lt;td&gt;56M&lt;/td&gt;
&lt;td&gt;23M&lt;/td&gt;
&lt;td&gt;22M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsclosedcaption.lib&lt;/td&gt;
&lt;td&gt;40M&lt;/td&gt;
&lt;td&gt;40M&lt;/td&gt;
&lt;td&gt;36M&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;td&gt;18M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsinter.lib&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;14M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;8,5M&lt;/td&gt;
&lt;td&gt;8,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsonvif.lib&lt;/td&gt;
&lt;td&gt;21M&lt;/td&gt;
&lt;td&gt;21M&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrspng.lib&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;8,2M&lt;/td&gt;
&lt;td&gt;8,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsrtp.lib&lt;/td&gt;
&lt;td&gt;47M&lt;/td&gt;
&lt;td&gt;47M&lt;/td&gt;
&lt;td&gt;44M&lt;/td&gt;
&lt;td&gt;22M&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsrtsp.lib&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;33M&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;15M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrstracers.lib&lt;/td&gt;
&lt;td&gt;28M&lt;/td&gt;
&lt;td&gt;28M&lt;/td&gt;
&lt;td&gt;27M&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;15M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrsvideofx.lib&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;16M&lt;/td&gt;
&lt;td&gt;35M&lt;/td&gt;
&lt;td&gt;9,2M&lt;/td&gt;
&lt;td&gt;15M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstrswebrtc.lib&lt;/td&gt;
&lt;td&gt;329M&lt;/td&gt;
&lt;td&gt;329M&lt;/td&gt;
&lt;td&gt;284M&lt;/td&gt;
&lt;td&gt;124M&lt;/td&gt;
&lt;td&gt;105M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gststreamgrouper.lib&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,7M&lt;/td&gt;
&lt;td&gt;7,2M&lt;/td&gt;
&lt;td&gt;7,2M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsttextahead.lib&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,6M&lt;/td&gt;
&lt;td&gt;9,5M&lt;/td&gt;
&lt;td&gt;7,4M&lt;/td&gt;
&lt;td&gt;7,3M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsttextwrap.lib&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;8,4M&lt;/td&gt;
&lt;td&gt;8,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstthreadshare.lib&lt;/td&gt;
&lt;td&gt;49M&lt;/td&gt;
&lt;td&gt;49M&lt;/td&gt;
&lt;td&gt;45M&lt;/td&gt;
&lt;td&gt;23M&lt;/td&gt;
&lt;td&gt;20M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsttogglerecord.lib&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;13M&lt;/td&gt;
&lt;td&gt;8,5M&lt;/td&gt;
&lt;td&gt;8,4M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gsturiplaylistbin.lib&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;7,9M&lt;/td&gt;
&lt;td&gt;7,9M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstvvdec.lib&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;11M&lt;/td&gt;
&lt;td&gt;7,5M&lt;/td&gt;
&lt;td&gt;7,5M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old/gstwebrtchttp.lib&lt;/td&gt;
&lt;td&gt;69M&lt;/td&gt;
&lt;td&gt;69M&lt;/td&gt;
&lt;td&gt;63M&lt;/td&gt;
&lt;td&gt;30M&lt;/td&gt;
&lt;td&gt;28M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/details&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This article presents several longstanding pain points in Rust, namely staticlib
binary sizes, symbol leaking, and incompatibilities between Rust and MSVC. I
demonstrate the tool &lt;code&gt;dragonfire&lt;/code&gt; that aims to address and work around, where
possible, these issues, along with remaining issues to be addressed.&lt;/p&gt;
&lt;p&gt;As explained earlier, &lt;code&gt;dragonfire&lt;/code&gt; treated libraries are live on all platforms
except Apple&#39;s, if you use the development packages from mainline;
it&#39;s on track hopefully for
the 1.28 release of GStreamer. There&#39;s already a &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1935&quot;&gt;merge request pending to enable it for Apple platforms&lt;/a&gt;,
we&#39;re only waiting to update the Rust mininum version.&lt;/p&gt;
&lt;p&gt;If you want to have a look, &lt;code&gt;dragonfire&lt;/code&gt;&#39;s source code is available at
&lt;a href=&quot;https://gitlab.freedesktop.org/amyspark/dragonfire&quot;&gt;Freedesktop&#39;s GitLab instance&lt;/a&gt;.
Please note that at the moment I have no plans to submit this to &lt;a href=&quot;http://crates.io&quot;&gt;crates.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Feel free to contact me with any feedback, and thanks for reading!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-dragonfire-1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;See its &lt;code&gt;default-https-client&lt;/code&gt; &lt;a href=&quot;https://lib.rs/crates/aws-smithy-runtime/versions&quot;&gt;feature&lt;/a&gt; at &lt;a href=&quot;http://lib.rs&quot;&gt;lib.rs&lt;/a&gt;, you will find it throughout the AWS SDK ecosystem. &lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fnref-dragonfire-1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-dragonfire-2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/reference/items/external-blocks.html#dylib-versus-raw-dylib&quot;&gt;https://doc.rust-lang.org/reference/items/external-blocks.html#dylib-versus-raw-dylib&lt;/a&gt; &lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fnref-dragonfire-2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-dragonfire-3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;In all cases the &lt;code&gt;-C&lt;/code&gt; flags are &lt;code&gt;debug=1&lt;/code&gt; + &lt;code&gt;codegen-units=1&lt;/code&gt; + &lt;code&gt;opt-level=s&lt;/code&gt;; see &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1916#note_3036669&quot;&gt;this comment&lt;/a&gt; for the complete results across all platforms. &lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fnref-dragonfire-3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-dragonfire-4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Source: &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1895&quot;&gt;https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1895&lt;/a&gt; &lt;a href=&quot;https://centricular.com/devlog/2025-11/dragonfire/#fnref-dragonfire-4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <title>Perfect Audio Device Switching on macOS and Windows</title>
    <author>
      <name>Nirbheek Chauhan</name>
      <email>nirbheek+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-08/Perfect-Audio-Device-Switching/" />
    <updated>2025-08-12T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-08/Perfect-Audio-Device-Switching/</id>
    <content type="html">&lt;p&gt;Over the past few years, we&#39;ve been slowly working on improving the
platform-specific plugins for Windows, macOS, iOS, and Android, and making them
work as well as the equivalent plugins on Linux. In this episode, we will look
at audio device switching in the source and sink elements on macOS and Windows.&lt;/p&gt;
&lt;p&gt;On Linux, if you&#39;re using the PulseAudio elements (both with the PulseAudio
daemon and PipeWire), you get perfect device switching: quick, seamless, easy,
and reliable. Simply set the &lt;code&gt;device&lt;/code&gt; property whenever you want and you&#39;re off
to the races. If the device gets unplugged, the pipeline will continue, and you
will get notified of the unplug via the &lt;code&gt;GST_MESSAGE_DEVICE_REMOVED&lt;/code&gt; bus
message from &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/gstreamer/gstdevicemonitor.html&quot;&gt;&lt;code&gt;GstDeviceMonitor&lt;/code&gt;&lt;/a&gt;
so you can change the device.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9326&quot;&gt;As of a few weeks ago&lt;/a&gt;,
the Windows Audio plugin &lt;code&gt;wasapi2&lt;/code&gt; implements the same behaviour. All you have
to do is set the &lt;code&gt;device&lt;/code&gt; property to whatever device you want (fetched using
the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/gstreamer/gstdevicemonitor.html?gi-language=c&quot;&gt;&lt;code&gt;GstDeviceMonitor&lt;/code&gt;&lt;/a&gt;
API), at any time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9264&quot;&gt;A merge request is open&lt;/a&gt;
for adding the same feature to the macOS audio plugin, and is expected to be
merged soon.&lt;/p&gt;
&lt;p&gt;For graceful error handling, such as accidental device unplug or other
unexpected errors, there&#39;s a new &lt;code&gt;continue-on-error&lt;/code&gt; property. Setting that
will cause the source to output silence after unplug, whereas the sink will
simply discard the buffers. An element warning will be emitted to notify the
app (alongside the &lt;code&gt;GST_MESSAGE_DEVICE_REMOVED&lt;/code&gt; bus message if there was
a hardware unplug), and the app can switch the device by setting the &lt;code&gt;device&lt;/code&gt;
property.&lt;/p&gt;
&lt;p&gt;Thanks to Seungha and Piotr for working on this!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>GstHip: A Cross-Vendor HIP Backend for GStreamer with Runtime Flexibility</title>
    <author>
      <name>Seungha Yang</name>
      <email>seungha+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-07/amd-hip-integration/" />
    <updated>2025-07-10T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-07/amd-hip-integration/</id>
    <content type="html">&lt;p&gt;HIP (formerly known as Heterogeneous-computing Interface for Portability)
is AMD’s GPU programming API that enables portable,
CUDA-like development across both AMD and NVIDIA platforms.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On AMD GPUs, HIP runs natively via the ROCm stack.&lt;/li&gt;
&lt;li&gt;On NVIDIA GPUs, HIP operates as a thin translation layer over the CUDA runtime and driver APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows developers to maintain a single codebase that can target multiple
GPU vendors with minimal effort.&lt;/p&gt;
&lt;h2&gt;Where HIP Is Used&lt;/h2&gt;
&lt;p&gt;HIP has seen adoption in AMD-focused GPU computing workflows, particularly
in environments that require CUDA-like programmability. Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PyTorch ROCm backend for deep learning workloads&lt;/li&gt;
&lt;li&gt;Select scientific applications like LAMMPS and GROMACS have experimented with
HIP backends for AMD GPU support&lt;/li&gt;
&lt;li&gt;GPU-accelerated media processing on systems that leverage AMD hardware&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While HIP adoption has been more limited compared to CUDA,
its reach continues to expand as support for AMD GPUs grows across a broader range of use cases.&lt;/p&gt;
&lt;h2&gt;The Challenge: Compile-Time Platform Lock-in&lt;/h2&gt;
&lt;p&gt;Despite its cross-vendor goal, HIP still has a fundamental constraint at the build level.
As of HIP 6.3, HIP requires developers to statically define their target
platform at compile time via macros like:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;__HIP_PLATFORM_AMD__&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// for AMD ROCm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;__HIP_PLATFORM_NVIDIA__&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// for CUDA backend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This leads to two key limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You must compile separate binaries for AMD and NVIDIA&lt;/li&gt;
&lt;li&gt;A single binary cannot support both platforms simultaneously&lt;/li&gt;
&lt;li&gt;HIP does not support runtime backend switching natively&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;GstHip’s Solution&lt;/h2&gt;
&lt;p&gt;To overcome this limitation, GstHip uses runtime backend dispatch through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dlopen()&lt;/code&gt; on Linux&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LoadLibrary()&lt;/code&gt; on Windows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of statically linking against a single HIP backend, GstHip loads both
the ROCm HIP runtime and the CUDA driver/runtime API at runtime.
This makes it possible to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Detect available GPUs dynamically&lt;/li&gt;
&lt;li&gt;Choose the appropriate backend per device&lt;/li&gt;
&lt;li&gt;Even support simultaneous use of AMD and NVIDIA GPUs in the same process&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Unified Wrapper API&lt;/h2&gt;
&lt;p&gt;GstHip provides a clean wrapper layer that abstracts backend-specific APIs via
a consistent naming scheme:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;hipError_t&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;HipFooBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;GstHipVendor vendor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Hip&lt;/code&gt; prefix (capital &lt;code&gt;H&lt;/code&gt;) clearly distinguishes the wrapper from native &lt;code&gt;hipFooBar(...)&lt;/code&gt; functions.
The &lt;code&gt;GstHipVendor&lt;/code&gt; enum indicates which backend to target:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GST_HIP_VENDOR_AMD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GST_HIP_VENDOR_NVIDIA&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Internally, each &lt;code&gt;HipFooBar(...)&lt;/code&gt; function dispatches to the correct backend by calling either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hipFooBar(...)&lt;/code&gt; for AMD ROCm&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cudaFooBar(...)&lt;/code&gt; for NVIDIA CUDA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These symbols are dynamically resolved via &lt;code&gt;dlopen()&lt;/code&gt; / &lt;code&gt;LoadLibrary()&lt;/code&gt;,
enabling runtime backend selection without GPU vendor-specific builds.&lt;/p&gt;
&lt;h2&gt;Memory Interop&lt;/h2&gt;
&lt;p&gt;All memory interop in GstHip is handled through the &lt;code&gt;hipupload&lt;/code&gt; and &lt;code&gt;hipdownload&lt;/code&gt; elements.
While zero-copy is not supported due to backend-specific resource management and
ownership ambiguity, GstHip provides optimized memory transfers between systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;System Memory ↔ HIP Memory&lt;/strong&gt;: Utilizes HIP pinned memory to achieve fast upload/download operations between host and device memory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GstGL ↔ GstHip&lt;/strong&gt;: Uses HIP resource interop APIs to perform GPU-to-GPU memory copies between OpenGL and HIP memory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GstCUDA ↔ GstHip (on NVIDIA platforms)&lt;/strong&gt;: Since both sides use CUDA memory, direct GPU-to-GPU memory copies are performed using CUDA APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;GPU-Accelerated Filter Elements&lt;/h2&gt;
&lt;p&gt;GstHip includes GPU-accelerated filters optimized for real-time media processing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hipconvertscale&lt;/code&gt;/&lt;code&gt;hipconvert&lt;/code&gt;/&lt;code&gt;hipscale&lt;/code&gt;: Image format conversion and image scaling&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hipcompositor&lt;/code&gt;: composing multiple video streams into single video stream&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These filters use the same unified dispatch system and are compatible with both AMD and NVIDIA platforms.&lt;/p&gt;
&lt;h2&gt;Application Integration Support&lt;/h2&gt;
&lt;p&gt;As of &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9340&quot;&gt;Merge Request!9340&lt;/a&gt;, GstHip exposes public APIs that allow
applications to access HIP resources managed by GStreamer.
This also enables applications to implement custom GstHIP-based plugins using
the same underlying infrastructure without duplicating resource management.&lt;/p&gt;
&lt;h2&gt;Summary of GstHip Advantages&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Single plugin/library binary supports both AMD and NVIDIA GPUs&lt;/li&gt;
&lt;li&gt;Compatible with Linux and Windows&lt;/li&gt;
&lt;li&gt;Supports multi-GPU systems, including hybrid AMD + NVIDIA configurations&lt;/li&gt;
&lt;li&gt;Seamless memory interop with System Memory, GstGL, and GstCUDA&lt;/li&gt;
&lt;li&gt;Provides high-performance GPU filters for video processing&lt;/li&gt;
&lt;li&gt;Maintains a clean API layer via HipFooBar(...) wrappers, enabling backend-neutral development&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>New generic multi-stream analytics batcher element</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-07/analytics-combiner-splitter/" />
    <updated>2025-07-01T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-07/analytics-combiner-splitter/</id>
    <content type="html">&lt;p&gt;For a project recently it was necessary to collect video frames of multiple
streams during a specific interval, and in the future also audio, to pass it
through an inference framework for extracting additional metadata from the
media and attaching it to the frames.&lt;/p&gt;
&lt;p&gt;While GStreamer has gained quite a bit of &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/additional/design/machine-learning-analytics.html&quot;&gt;infrastructure&lt;/a&gt; in the past years for machine learning use-cases in the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/analytics/&quot;&gt;analytics&lt;/a&gt; library, there was nothing for this specific use-case yet.&lt;/p&gt;
&lt;p&gt;As part of solving this, I &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9282&quot;&gt;proposed&lt;/a&gt; as design for a generic interface that allows combining and batching multiple streams into a single one by using empty buffers with a &lt;code&gt;GstMeta&lt;/code&gt; that contains the buffers of the original streams, and caps that include the caps of the original streams and allow format negotiation in the pipeline to work as usual.&lt;/p&gt;
&lt;p&gt;While this covers my specific use case of combining multiple streams, it
should be generic enough to also handle other cases that came up during the
discussions.&lt;/p&gt;
&lt;p&gt;In addition I wrote &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2317&quot;&gt;two new elements&lt;/a&gt;, &lt;code&gt;analyticscombiner&lt;/code&gt; and
&lt;code&gt;analyticssplitter&lt;/code&gt;, that make use of this new API for combining and batching
multiple streams in a generic, media-agnostic way over specific time
intervals, and later splitting it out again into the original streams. The combiner
can be configured to collect all media in the time interval, or only the first or last.&lt;/p&gt;
&lt;p&gt;Conceptually the combiner element is similar to NVIDIA&#39;s DeepStream &lt;code&gt;nvstreammux&lt;/code&gt; element, and
in the future it should be possible to write a translation layer between the
GStreamer analytics library and DeepStream.&lt;/p&gt;
&lt;p&gt;The basic idea for the usage of these elements is to have a pipeline like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- stream 1 --&#92;                                                                  / -- stream 1 with metadata --
               -- analyticscombiner -- inference elements -- analyticssplitter --
-- stream 2 --/                                                                  &#92; -- stream 2 with metadata --
   ........                                                                           ......................
-- stream N -/                                                                     &#92;- stream N with metadata --

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The inference elements would only add additional metadata to each of the buffers, which
can then be made use of further downstream in the pipeline for operations like overlays or
blurring specific areas of the frames.&lt;/p&gt;
&lt;p&gt;In the future there are likely going to be more batching elements for specific stream types,
operating on multiple or a single stream, or making use of completely different batching
strategies.&lt;/p&gt;
&lt;p&gt;Special thanks also to Olivier and Daniel who provided very useful feedback
during the review of the two merge requests.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>GStreamer Direct3D12 Rust bindings</title>
    <author>
      <name>Seungha Yang</name>
      <email>seungha+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-06/d3d12-binding-support/" />
    <updated>2025-06-23T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-06/d3d12-binding-support/</id>
    <content type="html">&lt;p&gt;With GStreamer 1.26, a new D3D12 backend &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6494&quot;&gt;GstD3D12 public library&lt;/a&gt;
was introduced in gst-plugins-bad.&lt;/p&gt;
&lt;p&gt;Now, with the new &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1732&quot;&gt;gstreamer-d3d12 rust crate&lt;/a&gt;,
Rust can finally access the Windows-native GPU feature written in GStreamer
in a safe and idiomatic way.&lt;/p&gt;
&lt;h2&gt;What You Get with GStreamer D3D12 Support in Rust&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Pass &lt;strong&gt;D3D12 textures created by your Rust application&lt;/strong&gt; directly
into GStreamer pipelines without data copying&lt;/li&gt;
&lt;li&gt;Likewise, &lt;strong&gt;GStreamer-generated GPU resources&lt;/strong&gt;
(such as frames decoded by D3D12 decoders) can be accessed directly in your Rust app&lt;/li&gt;
&lt;li&gt;GstD3D12 base GStreamer element can be written in Rust&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Beyond Pipelines: General D3D12 Utility Layer&lt;/h2&gt;
&lt;p&gt;GstD3D12 is &lt;strong&gt;not limited to multimedia pipelines&lt;/strong&gt;. It also acts as a convenient D3D12 runtime utility, providing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPU resource pooling such as command allocator and descriptor heap, to reduce overhead and improve reuse&lt;/li&gt;
&lt;li&gt;Abstractions for creating and recycling GPU textures with consistent lifetime tracking&lt;/li&gt;
&lt;li&gt;Command queue and fence management helpers, greatly simplifying GPU/CPU sync&lt;/li&gt;
&lt;li&gt;A foundation for building custom GPU workflows in Rust, with or without the full GStreamer pipeline&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>GStreamer OpenGL surfaces on Wayland</title>
    <author>
      <name>Matthew Waters</name>
      <email>matthew+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-05/wayland-multiple-windows/" />
    <updated>2025-05-21T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-05/wayland-multiple-windows/</id>
    <content type="html">&lt;p&gt;As part of the &lt;a href=&quot;https://discourse.gstreamer.org/t/gstreamer-spring-hackfest-2025-on-16-18-may-2025-in-nice-france/4366&quot;&gt;GStreamer Hackfest in Nice, France&lt;/a&gt;
I had some time to go through some outstanding GStreamer issues. One such issue
that has been on my mind was &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2997&quot;&gt;this GStreamer OpenGL Wayland issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, the issue is that OpenGL is an old API and did not have some of the
platform extensions it does today. As a result, most windowing system APIs allow
creating an output surface (or a window) but never showing it. This also works
just fine when you are creating an OpenGL context but not actually rendering
anything to the screen and this approach is what is used by all of the other
major OpenGL platforms (Windows, macOS, X11, etc) supported by GStreamer.&lt;/p&gt;
&lt;p&gt;When wayland initially arrived, this was not the case. A wayland surface could
be the back buffer (an OpenGL term for rendering to a surface) but could not be
hidden. This is very different from how other windowing APIs worked at the time.
As a result, the initial implementation using Wayland within GStreamer OpenGL
used some heuristics for determining when a wayland surface would be created and
used that basically boiled down to, if there is no shared OpenGL context, then
create a window.&lt;/p&gt;
&lt;p&gt;This heuristic obviously breaks in multiple different ways, the two most obvious
being:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;gltestsrc ! gldownload ! some-non-gl-sink&lt;/code&gt; - there should be no surface used
here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gltestsrc ! glimagesink gltestsrc ! glimagesink&lt;/code&gt; - there should be two
output surfaces used here.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The good news is that issue is now fixed by adding some API that &lt;code&gt;glimagesink&lt;/code&gt;
can use to notify that it would like an output surface. This has been
implemented in &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9007&quot;&gt;this merge request&lt;/a&gt;
and will be part of GStreamer 1.28.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JPEG XS support for interlaced video</title>
    <author>
      <name>Tim-Philipp Müller</name>
      <email>tim+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2025-01/jpeg-xs-interlaced-video-support/" />
    <updated>2025-01-02T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2025-01/jpeg-xs-interlaced-video-support/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/JPEG_XS&quot;&gt;JPEG XS&lt;/a&gt; is a visually lossless, low-latency, intra-only video codec
for video production workflows, standardised in ISO/IEC 21122.&lt;/p&gt;
&lt;p&gt;A few months ago &lt;a href=&quot;https://centricular.com/devlog/2024-09/jpeg-xs-mpegts/&quot;&gt;we added support&lt;/a&gt; for JPEG XS encoding
and decoding in GStreamer, alongside MPEG-TS container support.&lt;/p&gt;
&lt;p&gt;This initially covered progressive scan only though.&lt;/p&gt;
&lt;p&gt;Unfortunately &lt;a href=&quot;https://en.wikipedia.org/wiki/Interlaced_video&quot;&gt;interlaced scan&lt;/a&gt;, which harks back to the
days when TVs had cathode ray tube displays, is still quite common, especially
in the broadcasting industry, so it was only a matter of time until support
for that would be needed as well.&lt;/p&gt;
&lt;p&gt;Long story short, GStreamer can now (with &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8219&quot;&gt;this pending Merge Request&lt;/a&gt;)
also encode and decode interlaced video into/from JPEG XS.&lt;/p&gt;
&lt;p&gt;When putting JPEG XS into MPEG-TS, the individual fields are actually coded
separately, so there are two JPEG XS code streams per frame. Inside GStreamer
pipelines interlaced raw video can be carried in multiple ways, but the most
common one is an &amp;quot;interleaved&amp;quot; image, where the two fields are interleaved
row by row, and this is also what capture cards such as AJA or Decklink Blackmagic
produce in GStreamer.&lt;/p&gt;
&lt;p&gt;When encoding interlaced video in this representation, we need to go twice
over each frame and feed every second row of pixels to the underlying
&lt;a href=&quot;https://github.com/OpenVisualCloud/SVT-JPEG-XS/&quot;&gt;SVT JPEG XS&lt;/a&gt; encoder which itself is not aware of the interlaced
nature of the video content. We do this by specifying double the usual stride
as rowstride. This works fine, but unearthed some minor issues with the size
checks on the codec side, for which we filed a &lt;a href=&quot;https://github.com/OpenVisualCloud/SVT-JPEG-XS/pull/5&quot;&gt;pull request&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please give it a spin, and let us know if you have any questions or are
interested in additional container mappings such as MP4 or MXF, or
RTP payloaders / depayloaders.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Clocking a GStreamer pipeline from an MPEG-TS stream</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-12/mpegtslivesrc/" />
    <updated>2024-12-06T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-12/mpegtslivesrc/</id>
    <content type="html">&lt;p&gt;Some time ago, Edward and I wrote a new element that allows clocking a
GStreamer pipeline from an MPEG-TS stream, for example received via SRT.&lt;/p&gt;
&lt;p&gt;This new element, &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/mpegtslive/&quot;&gt;&lt;code&gt;mpegtslivesrc&lt;/code&gt;&lt;/a&gt;, wraps around any existing live source element, e.g. &lt;code&gt;udpsrc&lt;/code&gt; or &lt;code&gt;srtsrc&lt;/code&gt;, and provides a GStreamer clock that approximates the sender&#39;s clock. By making use of this clock as pipeline clock, it is possible to run the whole pipeline at the same speed as the sender is producing the stream and without having to implement any kind of clock drift mechanism like skewing or resampling. Without this it is necessary currently to adjust the timestamps of media coming out of GStreamer&#39;s &lt;code&gt;tsdemux&lt;/code&gt; element, which is problematic if accurate timestamps are necessary or the stream should be stored to a file, e.g. a 25fps stream wouldn&#39;t have exactly 40ms inter-frame timestamp differences anymore.&lt;/p&gt;
&lt;p&gt;The clock is approximated by making use of the in-stream MPEG-TS PCR, which basically gives the sender&#39;s clock time at specific points inside the stream, and correlating that together with the local receive times via a linear regression to calculate the relative rate between the sender&#39;s clock and the local system clock.&lt;/p&gt;
&lt;p&gt;Usage of the element is as simple as&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ gst-launch-1.0 mpegtslivesrc &lt;span class=&quot;token assign-left variable&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;srtsrc location=srt://1.2.3.4:5678?latency=150&amp;amp;mode=caller&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; tsdemux skew-corrections&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
$ gst-launch-1.0 mpegtslivesrc &lt;span class=&quot;token assign-left variable&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;udpsrc address=1.2.3.4 port=5678&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; tsdemux skew-corrections&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Addition 2025-06-28&lt;/strong&gt;: If you&#39;re using an older (&amp;lt; 1.28) version of GStreamer, you&#39;ll have to use the
&lt;code&gt;ignore-pcr=true&lt;/code&gt; property on &lt;code&gt;tsdemux&lt;/code&gt; instead. &lt;code&gt;skew-corrections=false&lt;/code&gt; was
only added &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9301&quot;&gt;recently&lt;/a&gt;
and allows for more reliable handling of MPEG-TS timestamp discontinuities.&lt;/p&gt;
&lt;p&gt;A similar approach for clocking is implemented in the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/aja/ajasrc.html&quot;&gt;AJA source&lt;/a&gt;
element and the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/ndi/ndisrc.html&quot;&gt;NDI source&lt;/a&gt; element when the clocked timestamp mode is configured.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Apple AAC encoder in GStreamer</title>
    <author>
      <name>Piotr Brzeziński</name>
      <email>piotr+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-11/audiotoolbox-encoder/" />
    <updated>2024-11-05T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-11/audiotoolbox-encoder/</id>
    <content type="html">&lt;p&gt;Thanks to the newly added &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/osxaudio/atenc.html&quot;&gt;&lt;code&gt;atenc&lt;/code&gt;&lt;/a&gt; element, you can now use Apple&#39;s well-known AAC encoder directly in GStreamer!&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gst-launch-1.0 &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; audiotestsrc &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; audio/x-raw,channels&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;,rate&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;48000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; atenc &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; mp4mux &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; filesink &lt;span class=&quot;token assign-left variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;output.m4a&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It supports all the usual rate control modes (CBR/LTA/VBR/CVBR), as well as settings relevant for each of them (target bitrate for CBR, perceived quality for VBR).&lt;/p&gt;
&lt;p&gt;For now you can encode AAC-LC with up to 7.1 channels. Support for more AAC profiles and different output formats will be added in the future.&lt;/p&gt;
&lt;p&gt;If you need decoding too, &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/osxaudio/atdec.html&quot;&gt;&lt;code&gt;atdec&lt;/code&gt;&lt;/a&gt; is there to help and has supported AAC alongside a few other formats for a long time now.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>GStreamer Support for SMPTE 2038 Ancillary Data and Closed Captions in MPEG-TS</title>
    <author>
      <name>Tim-Philipp Müller</name>
      <email>tim+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-10/st-2038-ancillary-data-and-captions/" />
    <updated>2024-10-25T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-10/st-2038-ancillary-data-and-captions/</id>
    <content type="html">&lt;p&gt;If you&#39;ve ever seen a news or sports channel playing without sound in
the background of a hotel lobby, bar, or airport, you&#39;ve probably seen
&lt;a href=&quot;https://en.wikipedia.org/wiki/Closed_captioning&quot;&gt;closed captions&lt;/a&gt; in action.&lt;/p&gt;
&lt;p&gt;These TV-style captions are alphabet/character-based, with some very basic
commands to control the positioning and layout of the text on the screen.&lt;/p&gt;
&lt;p&gt;They are very low bitrate and were transmitted in the invisible part of TV
images during the vertical blanking interval (VBI) back in those good old
analogue days (&amp;quot;line 21 captions&amp;quot;).&lt;/p&gt;
&lt;p&gt;Nowadays they are usually carried as part of the MPEG-2 or H.264/H.265 video
bitstream, unlike say text subtitles in a Matroska file which will be its
own separate stream in the container.&lt;/p&gt;
&lt;p&gt;In GStreamer closed captions can be carried in different ways: Either
implicitly as part of a video bitstream, or explicitly as part of a video
bitstream with &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/video/gstvideoanc.html?gi-language=c#GstVideoCaptionMeta&quot;&gt;video caption metas&lt;/a&gt; on the buffers passing
through the pipeline. Captions can also travel through a pipeline stand-alone
in form of one of multiple raw caption bitstream formats.&lt;/p&gt;
&lt;p&gt;To make handling these different options easier for applications there are
&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/closedcaption/index.html?gi-language=c#plugin-closedcaption&quot;&gt;elements&lt;/a&gt; that can extract captions from the video
bitstream into metas, and split off captions from metas into their own
stand-alone stream, and to do the reverse and combine and reinject them again.&lt;/p&gt;
&lt;h2&gt;SMPTE 2038 Ancillary Data&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pub.smpte.org/pub/st2038/st2038-2021.pdf&quot;&gt;SMPTE 2038 (pdf)&lt;/a&gt; is a generic system to put VBI-style
ancillary data into an MPEG-TS container. This could include all kinds of
metadata such as scoreboard data or game clocks, and of course also closed
captions, in this case in form of a distinct stream completely separate
from any video bitstream.&lt;/p&gt;
&lt;p&gt;We&#39;ve recently added &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1777&quot;&gt;support for SMPTE 2038 ancillary data in GStreamer&lt;/a&gt;.
This comes in form of a number of new elements in the &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rsclosedcaption/index.html?gi-language=c#plugin-rsclosedcaption&quot;&gt;GStreamer Rust closedcaption plugin&lt;/a&gt;
and &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7461&quot;&gt;mappings for it in the MPEG-TS muxer and demuxer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new elements are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;st2038ancdemux&lt;/strong&gt;: splits SMPTE ST-2038 ancillary metadata (as received
from &lt;code&gt;tsdemux&lt;/code&gt;) into separate streams per DID/SDID and line/horizontal_offset.
Will add a sometimes pad with details for each ancillary stream. Also has an
always source pad that just outputs all ancillary streams for easy forwarding
or remuxing, in case none of the ancillary streams need to be modified or
dropped.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;st2038ancmux&lt;/strong&gt;: muxes SMPTE ST-2038 ancillary metadata streams into a
single stream for muxing into MPEG-TS with &lt;code&gt;mpegtsmux&lt;/code&gt;. Combines ancillary
data on the same line if needed, as is required for MPEG-TS muxing. Can
accept individual ancillary metadata streams as inputs and/or the combined
stream from &lt;code&gt;st2038ancdemux&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the video framerate is known, it can be signalled to the ancillary
data muxer via the output caps by adding a capsfilter behind it, with
e.g. &lt;code&gt;meta/x-st-2038,framerate=30/1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This allows the muxer to bundle all packets belonging to the same frame (with
the same timestamp), but that is not required. In case there are multiple
streams with the same DID/SDID that have an ST-2038 packet for the same
frame, it will prioritise the one from more recently created request pads
over those from earlier created request pads (which might contain a
combined stream for example if that&#39;s fed first).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;st2038anctocc&lt;/strong&gt;: extracts closed captions (CEA-608 and/or CEA-708) from
SMPTE ST-2038 ancillary metadata streams and outputs them on the respective
sometimes source pad (&lt;code&gt;src_cea608&lt;/code&gt; or &lt;code&gt;src_cea708&lt;/code&gt;). The data is output as
a closed caption stream with caps &lt;code&gt;closedcaption/x-cea-608,format=s334-1a&lt;/code&gt;
or &lt;code&gt;closedcaption/x-cea-708,format=cdp&lt;/code&gt; for further processing by other
GStreamer closed caption processing elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;cctost2038anc&lt;/strong&gt;: takes closed captions (CEA-608 and/or CEA-708) as produced
by other GStreamer closed caption processing elements and converts them into
SMPTE ST-2038 ancillary data that can be fed to &lt;code&gt;st2038ancmux&lt;/code&gt; and then to
&lt;code&gt;mpegtsmux&lt;/code&gt; for splicing/muxing into an MPEG-TS container. The &lt;code&gt;line-number&lt;/code&gt;
and &lt;code&gt;horizontal-offset&lt;/code&gt; properties should be set to the desired line number
and horizontal offset.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please give it a spin and let us know how it goes!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Switching hlscmafsink to a new playlist without pausing</title>
    <author>
      <name>Piotr Brzeziński</name>
      <email>piotr+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-09/hlssink3-new-playlist/" />
    <updated>2024-09-28T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-09/hlssink3-new-playlist/</id>
    <content type="html">&lt;p&gt;Up until recently, when using &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/hlssink3/hlscmafsink.html&quot;&gt;&lt;code&gt;hlscmafsink&lt;/code&gt;&lt;/a&gt;, if you wanted to move to a new playlist you had to stop the pipeline, modify the relevant properties and then go to &lt;code&gt;PLAYING&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;This was problematic when working with live sources because some data was being lost between the state changes. Not anymore!&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/hlssink3/hlscmafsink.html#hlscmafsink::new-playlist&quot;&gt;&lt;code&gt;new-playlist&lt;/code&gt;&lt;/a&gt; signal has been added, which lets you switch output to a new location on the fly, without having any gaps between the content in each playlist.&lt;/p&gt;
&lt;p&gt;Simply change the relevant properties first and then emit the signal:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;hlscmafsink&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;playlist-location&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_playlist_location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
hlscmafsink&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;init-location&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_init_location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
hlscmafsink&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_segment_location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
hlscmafsink&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit_by_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;new-playlist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be useful if you&#39;re capturing a live source and want to switch to a different folder every couple of hours, for example.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JPEG XS encoding and decoding in GStreamer, with MPEG-TS mux support</title>
    <author>
      <name>Tim-Philipp Müller</name>
      <email>tim+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-09/jpeg-xs-mpegts/" />
    <updated>2024-09-20T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-09/jpeg-xs-mpegts/</id>
    <content type="html">&lt;h2&gt;What is JPEG XS?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/JPEG_XS&quot;&gt;JPEG XS&lt;/a&gt; is a visually lossless, low-latency, intra-only video codec
for video production workflows, standardised in ISO/IEC 21122.&lt;/p&gt;
&lt;p&gt;It&#39;s wavelet based, with low computational overhead and a latency measured
in scanlines, and it is designed to allow easy implementation in software,
GPU or FPGAs.&lt;/p&gt;
&lt;p&gt;Multi-generation robustness means repeated decoding and encoding will not
introduce unpleasant coding artefacts or noticeably degrade image quality,
which makes it suitable for video production workflows.&lt;/p&gt;
&lt;p&gt;It is often deployed in lieu of existing raw video workflows, where it allows
sending multiple streams over links designed to carry a single raw video
transport.&lt;/p&gt;
&lt;h2&gt;JPEG XS encoding / decoding in GStreamer&lt;/h2&gt;
&lt;p&gt;GStreamer now &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7430&quot;&gt;gained&lt;/a&gt; basic support for this codec.&lt;/p&gt;
&lt;p&gt;Encoding and decoding is supported via the Open Source
&lt;a href=&quot;https://github.com/OpenVisualCloud/SVT-JPEG-XS/&quot;&gt;Intel Scalable Video Technology JPEG XS library&lt;/a&gt;, but
third-party GStreamer plugins that provide GPU accelerated encoding
and decoding exist as well.&lt;/p&gt;
&lt;h2&gt;MPEG-TS container mapping&lt;/h2&gt;
&lt;p&gt;Support was also &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7172&quot;&gt;added&lt;/a&gt; for carriage inside MPEG-TS which should
enable a wide range of streaming applications including those based on the
Video Services Forum (VSF)&#39;s &lt;a href=&quot;https://www.vsf.tv/download/technical_recommendations/VSF_TR-07_2022-04-20.pdf&quot;&gt;Technical Recommendation TR-07&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;JPEG XS caps in GStreamer&lt;/h2&gt;
&lt;p&gt;It actually took us a few iterations to come up with GStreamer caps that
we were somewhat happy with for starters.&lt;/p&gt;
&lt;p&gt;Our starting point was what the SVT encoder/decoder output/consume, and
our initial target was MPEG-TS container format support.&lt;/p&gt;
&lt;p&gt;We checked various specifications to see how JPEG XS is mapped there and
how it could work, in particular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISO/IEC 21122-3 (Part 3: Transport and container formats)&lt;/li&gt;
&lt;li&gt;MPEG-TS JPEG XS mapping and VSF TR-07 - Transport of JPEG XS Video in MPEG-2 Transport Stream over IP&lt;/li&gt;
&lt;li&gt;RFC 9134: RTP Payload Format for ISO/IEC 21122 (JPEG XS)&lt;/li&gt;
&lt;li&gt;SMPTE ST 2124:2020 (Mapping JPEG XS Codestreams into the MXF)&lt;/li&gt;
&lt;li&gt;MP4 mapping&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and we think the current mapping will work for all of those cases.&lt;/p&gt;
&lt;p&gt;Basically each mapping wants some extra headers in addition to the codestream
data, for the out-of-band signalling required to make sense of the image
data. Originally we thought about putting some form of &lt;code&gt;codec_data&lt;/code&gt; header into
the caps, but it wouldn&#39;t really have made anything easier, and would just
have duplicated 99% of the info that&#39;s in the video caps already anyway.&lt;/p&gt;
&lt;p&gt;The current caps mapping is based on ISO/IEC 21122-3, Annex D, with
additional metadata in the caps, which should hopefully work just fine
for RTP, MP4, MXF and other mappings in future.&lt;/p&gt;
&lt;p&gt;Please give it a spin, and let us know if you have any questions or are
interested in additional container mappings such as MP4 or MXF, or
RTP payloaders / depayloaders.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Notifications about new segments in hlssink3</title>
    <author>
      <name>Piotr Brzeziński</name>
      <email>piotr+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-09/hlssink3-segment-added/" />
    <updated>2024-09-02T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-09/hlssink3-segment-added/</id>
    <content type="html">&lt;p&gt;When using &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/hlssink3/hlssink3.html&quot;&gt;&lt;code&gt;hlssink3&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/hlssink3/hlscmafsink.html&quot;&gt;&lt;code&gt;hlscmafsink&lt;/code&gt;&lt;/a&gt; elements, it&#39;s now possible to track new fragments being added by listening for the &lt;code&gt;hls-segment-added&lt;/code&gt; message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Got message #67 from element &amp;quot;hlscmafsink0&amp;quot; (element): hls-segment-added, location=(string)segment00000.m4s, running-time=(guint64)0, duration=(guint64)3000000000;
Got message #71 from element &amp;quot;hlscmafsink0&amp;quot; (element): hls-segment-added, location=(string)segment00001.m4s, running-time=(guint64)3000000000, duration=(guint64)3000000000;
Got message #74 from element &amp;quot;hlscmafsink0&amp;quot; (element): hls-segment-added, location=(string)segment00002.m4s, running-time=(guint64)6000000000, duration=(guint64)3000000000;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is similar to how you would listen for &lt;code&gt;splitmuxsink-fragment-closed&lt;/code&gt; when using the older &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/hls/hlssink2.html&quot;&gt;&lt;code&gt;hlssink2&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>webrtcsink now implements a generic control data channel</title>
    <author>
      <name>Mathieu Duponchelle</name>
      <email>mathieu+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-08/webrtcsink-control-channel/" />
    <updated>2024-08-22T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-08/webrtcsink-control-channel/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rswebrtc/webrtcsink.html&quot;&gt;webrtcsink&lt;/a&gt; already supported instantiating a data channel for the sole purpose
of carrying navigation events from the consumer to the producer, it can also now
create a generic control data channel through which the consumer can send JSON
requests in the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;id&amp;quot;: identifier used in the response message,
    &amp;quot;mid&amp;quot;: optional media identifier the request applies to,
    &amp;quot;request&amp;quot;: {
        &amp;quot;type&amp;quot;: currently &amp;quot;navigationEvent&amp;quot; and &amp;quot;customUpstreamEvent&amp;quot; are supported,
        &amp;quot;type-specific-field&amp;quot;: ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The producer will reply with such messages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;id&amp;quot;: identifier of the request,
  &amp;quot;error&amp;quot;: optional error message, successful if not set
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The example frontend was also updated with a text area for sending any arbitrary
request.&lt;/p&gt;
&lt;p&gt;The use case for this work was to make it possible for a consumer to control the
mix matrix used for the audio stream, with such a pipeline running on the producer
side:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch-1.0 audiotestsrc ! audioconvert ! webrtcsink enable-control-data-channel=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As &lt;code&gt;audioconvert&lt;/code&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7363&quot;&gt;now supports&lt;/a&gt; setting a mix matrix through a custom upstream event,
the consumer can simply input the following text in the request field of the frontend
to reverse the channels of a stereo audio stream:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;customUpstreamEvent&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;structureName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GstRequestAudioMixMatrix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;structure&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;matrix&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title>Encoding HEVC with an alpha channel on macOS/iOS</title>
    <author>
      <name>Piotr Brzeziński</name>
      <email>piotr+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-08/videotoolbox-hevc-alpha/" />
    <updated>2024-08-15T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-08/videotoolbox-hevc-alpha/</id>
    <content type="html">&lt;p&gt;GStreamer&#39;s VideoToolbox encoder recently &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6664&quot;&gt;gained support&lt;/a&gt; for encoding HEVC/H.265 videos containing an alpha channel.&lt;/p&gt;
&lt;p&gt;A separate &lt;code&gt;vtenc_h265a&lt;/code&gt; element has been added for this purpose. Assuming you&#39;re on macOS, you can use it like this:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gst-launch-1.0 &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; videotestsrc &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; alpha &lt;span class=&quot;token assign-left variable&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; videoconvert &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; vtenc_h265a &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; mp4mux &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; filesink &lt;span class=&quot;token assign-left variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;alpha.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/thewildtree/pen/VwNWomX&quot;&gt;Click here&lt;/a&gt; to see an example in action!
It should work fine on macOS and iOS, in both Chrome and Safari.
On other platforms it might not be displayed at all - compatibility is unfortunately still quite limited.&lt;/p&gt;
&lt;p&gt;If your browser supports this format correctly, you will see a moving GStreamer logo on a constantly changing background - something &lt;a href=&quot;https://cloud.centricular.com/s/NtarczbwLTRZBiA/download/preview.mp4&quot;&gt;like this&lt;/a&gt;.
That background is entirely separate from the video and is generated using CSS.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Default webrtcsink signaller now supports answering</title>
    <author>
      <name>Mathieu Duponchelle</name>
      <email>mathieu+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-08/webrtcsink-answer/" />
    <updated>2024-08-10T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-08/webrtcsink-answer/</id>
    <content type="html">&lt;p&gt;The default signaller for &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rswebrtc/webrtcsink.html&quot;&gt;webrtcsink&lt;/a&gt; can now produce an answer when the
consumer sends the offer first.&lt;/p&gt;
&lt;p&gt;To test this with the example, you can simply follow the usual steps but also
paste the following text in the text area before clicking on the producer name:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;offerToReceiveAudio&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;offerToReceiveVideo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I implemented this in order to test &lt;code&gt;multiopus&lt;/code&gt; support with webrtcsink, as it
seems to work better when munging the SDP offered by chrome.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Embedded servers in webrtcsink</title>
    <author>
      <name>Mathieu Duponchelle</name>
      <email>mathieu+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-08/webrtcsink-all-included/" />
    <updated>2024-08-09T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-08/webrtcsink-all-included/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rswebrtc/webrtcsink.html&quot;&gt;webrtcsink&lt;/a&gt; can now run both the default signalling server and a web server
for static content.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gst-launch-1.0 &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; videotestsrc webrtcsink &lt;span class=&quot;token assign-left variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ws run-signalling-server&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true run-web-server&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This comes in very handy for testing purposes, but could also prove useful in
production.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Decklink HDR support</title>
    <author>
      <name>Matthew Waters</name>
      <email>matthew+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-08/decklink-hdr/" />
    <updated>2024-08-08T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-08/decklink-hdr/</id>
    <content type="html">&lt;p&gt;A couple of weeks ago I implemented support for static HDR10 metadata in the
&lt;code&gt;decklinkvideosink&lt;/code&gt; and &lt;code&gt;decklinkvideosrc&lt;/code&gt; elements for Blackmagic video capture
and playout devices. The culmination of this work is available from
&lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7214&quot;&gt;MR 7124 - decklink: add support for HDR output and input&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This adds support for both PQ and HLG HDR alongside some improvements in
colorimetry negotiation.  Static HDR metadata in GStreamer is conveyed through
&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/gstreamer/gstcaps.html&quot;&gt;caps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first part of this is the &#39;colorimetry&#39; field in &lt;code&gt;video/x-raw&lt;/code&gt; caps.
&lt;code&gt;decklinkvideosink&lt;/code&gt; and &lt;code&gt;decklinkvideosrc&lt;/code&gt; now support the colorimetry values
&#39;bt601&#39;, &#39;bt709&#39;, &#39;bt2020&#39;, &#39;bt2100-hlg&#39;, and &#39;bt2100-pq&#39; for any resolution.
Previously the colorimetry used was fixed based on the resolution of the video
frames being sent or received. With some glue code, the colorimetry is now
retrieved from the Decklink API and the Decklink API can ask us for the
colorimetry of the submitted video frame. Arbitrary colorimetry support is not
supported on all Decklink devices and we fallback to the previous fixed list
based on frame resolution when not supported.&lt;/p&gt;
&lt;p&gt;Support for HDR metadata is a separate feature flag in the Decklink API and may
or may not be present independent of Decklink&#39;s arbitrary colour space support.
If the Decklink device does not support HDR metadata, then the colorimetry
values &#39;bt2100-hlg&#39;, and &#39;bt2100-pq&#39; are not supported.&lt;/p&gt;
&lt;p&gt;In the case of HLG, all that is necessary is to provide information that
the HLG gamma transfer function is being used.  Nothing else is required.&lt;/p&gt;
&lt;p&gt;In the case of PQ HDR, in addition to providing Decklink with the correct gamma
transfer function, Decklink also needs some other metadata conveyed in the
caps in the form of the &#39;mastering-display-info&#39; and &#39;light-content-level&#39;
fields. With some support from
&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/video/video-hdr.html#GstVideoMasteringDisplayInfo&quot;&gt;GstVideoMasteringDisplayInfo&lt;/a&gt;,
and
&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/video/video-hdr.html#GstVideoContentLightLevel&quot;&gt;GstVideoContentLightLevel&lt;/a&gt;
the relevant information signalled to Decklink and can be retrieved from each
individual video frame.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>RTSP source handling of non-compliant control URI handling of various RTSP servers</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-07/rtspsrc-control-uri/" />
    <updated>2024-07-04T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-07/rtspsrc-control-uri/</id>
    <content type="html">&lt;p&gt;In GStreamer 1.20 times I &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2868&quot;&gt;fixed&lt;/a&gt; the handling of RTSP control URIs in GStreamer&#39;s RTSP source element by making use of &lt;code&gt;GstUri&lt;/code&gt; for joining URIs and resolving relative URIs instead of using a wrong, custom implementation of those basic URI operations (see &lt;a href=&quot;https://www.ietf.org/rfc/rfc2396.txt&quot;&gt;RFC 2396&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This was in response to a &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/971&quot;&gt;bug report&lt;/a&gt; which was caused by a regression in 1.18 when fixing that custom implementation some years before. Now that this is handled according to the standards, one would expect that the topic is finally solved.&lt;/p&gt;
&lt;p&gt;Unfortunately that was not the case. As it turns out, various RTSP servers are
not actually implementing the URI operations for constructing the control URI
but instead do simple string concatenation. This works fine for simple cases
but once path separators or query parameters are involved this is not
sufficient. The fact that both VLC and ffmpeg on the client-side also only do string
concatenation unfortunately does not help this situation either as these
servers will work fine in VLC and ffmpeg but not in GStreamer, so it initially
appears like a GStreamer bug.&lt;/p&gt;
&lt;p&gt;To work around these cases automatically, a &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3127&quot;&gt;workaround&lt;/a&gt; with a couple of
follow-up fixes &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3854&quot;&gt;1&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4123&quot;&gt;2&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6213&quot;&gt;3&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6926&quot;&gt;4&lt;/a&gt; was implemented. This workaround is available since 1.20.4.&lt;/p&gt;
&lt;p&gt;Unfortunately this was also not enough as various servers don&#39;t just implement
the URI RFC wrong, but also don&#39;t implement the RTSP RFC correctly and don&#39;t
return any kind of meaningful errors but, for example, simply close the
connection.&lt;/p&gt;
&lt;p&gt;To solve this once and for all, &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7133&quot;&gt;Mathieu now added&lt;/a&gt; a new property to &lt;code&gt;rtspsrc&lt;/code&gt;
that forces it to directly use string concatenation and not attempt proper URI
operations first.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ gst-launch-1.0 rtspsrc &lt;span class=&quot;token assign-left variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;rtsp://1.2.3.4/test force-non-compliant-url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This property is available since 1.24.7 and should make it possible to use
such misbehaving and non-compliant servers.&lt;/p&gt;
&lt;p&gt;If GStreamer&#39;s &lt;code&gt;rtspsrc&lt;/code&gt; fails on an RTSP stream that is handled just fine by
VLC and ffmpeg, give this a try.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>DMABuf offloading in the GTK4 GStreamer video sink</title>
    <author>
      <name>Sebastian Dröge</name>
      <email>sebastian+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-04/gtk4-dmabuf-import/" />
    <updated>2024-04-20T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-04/gtk4-dmabuf-import/</id>
    <content type="html">&lt;p&gt;Last month as part of the GTK 4.14 release, GTK gained support for
directly importing DMABufs on Wayland. Among other things, this allows to
pass decoded video frames from hardware decoders to GTK, and then under
certain circumstances allows GTK to directly forward the DMABuf to the Wayland
compositor. And under even more special circumstances, this can then be
directly passed to the GPU driver. Matthias wrote some &lt;a href=&quot;https://blog.gtk.org/2023/11/15/introducing-graphics-offload/&quot;&gt;blog&lt;/a&gt; &lt;a href=&quot;https://blogs.gnome.org/gtk/2024/04/17/graphics-offload-revisited/&quot;&gt;posts&lt;/a&gt; about the details.&lt;/p&gt;
&lt;p&gt;In short, this reduces CPU usage and power consumption considerably when using
a suitable hardware decoder and running GTK on Wayland. A suitable hardware
decoder in this case is one provided by e.g. Intel or (newer) AMD GPUs via VA
but unfortunately not NVIDIA because they simply don&#39;t support DMABufs.&lt;/p&gt;
&lt;p&gt;I&#39;ve &lt;a href=&quot;https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1547&quot;&gt;added support&lt;/a&gt; for this to the GStreamer GTK4 video sink,
&lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/gtk4/&quot;&gt;&lt;code&gt;gtk4paintablesink&lt;/code&gt;&lt;/a&gt; that exists as part of the GStreamer Rust plugins.
Previously it was only possible to pass RGB system memory (i.e. after
downloading from the GPU in case of hardware decoders) or GL textures (with
all kinds of complications) from GStreamer to GTK4.&lt;/p&gt;
&lt;p&gt;In general the GTK4 sink now offers the most complete GStreamer / UI toolkit
integration, even more than the QML5/6 sinks, and it is used widely by various
GNOME applications.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Welcome to the Centricular Development Log</title>
    <author>
      <name>Tim-Philipp Müller</name>
      <email>tim+devlog@centricular.com</email>
    </author>
    <link href="https://centricular.com/devlog/2024-04/welcome/" />
    <updated>2024-04-15T00:00:00Z</updated>
    <id>https://centricular.com/devlog/2024-04/welcome/</id>
    <content type="html">&lt;p&gt;Hello and welcome to our little corner of the internet!&lt;/p&gt;
&lt;p&gt;This is where we will post little updates and going-ons about
&lt;a href=&quot;https://gstreamer.freedesktop.org/&quot;&gt;GStreamer&lt;/a&gt;,
&lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust&lt;/a&gt;,
&lt;a href=&quot;https://mesonbuild.com/&quot;&gt;Meson&lt;/a&gt;,
&lt;a href=&quot;https://gstreamer.freedesktop.org/modules/orc.html&quot;&gt;Orc&lt;/a&gt;,
&lt;a href=&quot;https://www.gnome.org/&quot;&gt;GNOME&lt;/a&gt;,
&lt;a href=&quot;https://github.com/ystreet/librice?tab=readme-ov-file#readme&quot;&gt;librice&lt;/a&gt;,
and other Free and Open Source Software projects we love to contribute to.&lt;/p&gt;
&lt;p&gt;This covers only a small part of our day-to-day upstream activity, but we&#39;ll
try to make time to post about interesting happenings between the everyday
hustle.&lt;/p&gt;
&lt;p&gt;Please check in regularly and bear with us while we look into adding more
convenient ways to get notified of updates.&lt;/p&gt;
&lt;p&gt;In the meantime please follow us on &lt;a href=&quot;https://fosstodon.org/@centricular@floss.social/&quot;&gt;Mastodon&lt;/a&gt;,
&lt;a href=&quot;https://bsky.app/profile/centricular.com&quot;&gt;Bluesky&lt;/a&gt;,
or (yes we still call it) &lt;a href=&quot;https://x.com/centricular&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
