PipeWire expects the SPA_TYPE_OBJECT_ParamBuffers to be valid after
setting SPA_PARAM_Format. The pipewiresink knows the final buffer size
only after the pipewirepool has been activated.
There is a race between PipeWire asking the pipewiresink for the buffers
and GStreamer activating the buffer pool. If GStreamer has not activated
the buffer pool before PipeWire asks for the Buffer params, PipeWire
won't allocate buffers with the correct type and size.
The chance of hitting this window increases, if the upstream GStreamer
element doesn't use the buffer pool. In this case the buffer pool is
activated by the first buffer that arrives at the pipewiresink, which
may take some time.
Instead of not updating the Buffer params when the buffer pool is not
active, wait for the buffer pool to become active.
Add a helper function for updating the params instead of handling it in
the pool_activated callback. This allows to explicitly set the params
from the element.
Print the state of the stream not only as the numeric value, but also
print the name of the state to help the reader.
While at it, add the sink element to the log output to be able to
identify the sink that received the state change.
Construct this new class from both the src and sink to be able to share code
Consolidate the previous mess of open/close/start/stop into a single pair
of open/close functions in the new stream class
When the link on the pipewire side is destroyed, on video streams, buffers
are removed abruptly and there is no way this pipeline can be revived,
so let's post an element error to stop it.
On a normal shutdown, the pool is first set to flushing in change_state(),
so checking for the flushing state is a good indicator to know if this
is a normal shutdown or not.
See #1980
This is for readability and better control.
Make sure we clear out all pointers to anything related to the released
pw_buffer, including all the memories.
Both the GPtrArray and its contents are leaked in case of success.
`pw_stream_connect()` copies the params as needed, so use `g_autoptr()`
to free the array and with it, its contents.
Parse the audio rate from the caps and set this as the stream node.rate
property. This will try to switch the graph to this rate and avoids
resampling.
Rework some of the property handling.
gst-play uses autoaudiosink by default when playing audio, which
iterates over all sinks sorting them by rank. By default,
pipewiresink sets the rank to 0, but it can be overridden
by setting the GST_PLUGIN_FEATURE_RANK env. var. like this:
`GST_PLUGIN_FEATURE_RANK=pipewiresink:268 gst-play-1.0 /usr/share/sounds/alsa/test.wav`
But that doesn't work either because the autoaudiosink plugin also
filters the available options, testing for "Sink" and "Audio" to
appear in the classification metadata
(in the strstr comparison in
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/blob/main/subprojects/gst-plugins-good/gst/autodetect/gstautodetect.c?ref_type=heads#L220
klass is what's set by pipewire as classification,
self->type_klass is "Sink" and self->media_klass is "Audio")
Just adding the word Audio to the classification metadata fixes
this and allows pipewiresink to be selected by autoaudiosink.
I also set it in the source plugin since looking at the code,
autoaudiosrc works exactly the same.
Gstreamer 1.24 added and largely switched to a new, modifier aware
DMABuf API. Unfortunately that breaks the existing DMABuf support in the
PW Gst element.
Add support for the new API.
GStreamer uses a time stamp for the decoding time, but PipeWire uses an
offset to the presentation time. Thus, the pipewiresink must not use the
DTS as dts_offset, but has to calculate the offset.
If the buffer's DTS is invalid, assume that dts is pts.
Tabs and spaces are mixed for indentation in the pipewiresink. Replace
all tabs with 8 spaces although indentation is 2 spaces, since that
looks like the intended indent.
First, make the error permanent by calling pw_stream_set_error()
and when this emits an error state again, report that to GStreamer.
Do the same in pipewiresink, which didn't even have the
pw_stream_set_error() call before, so the stream wasn't really going
into an error state at all.
For encoded formats, buffer size is the size of the actual data in the
buffer and may change for each frame depending on the content. Thus,
configuring the buffer pool of the pipewiresrc with the size of the
first buffer may be insufficient for later buffers.
Configure the buffer pool to the maximum size of the first upstream
buffer and assume that the following buffers will be allocated with the
same size as the first buffer.
If the pipewiresink needs to copy the GStreamer buffers to the PipeWire
buffers, because the upstream element didn't use the pipewirepool, the
metadata must be copied, too. Otherwise the pipewire datas will be
filled with the metadata from the buffer during the initialization and
not the currently used buffer.
For example, without copying the metadata the buffer timestamp will be
missing on the pipewire buffers.
The offset in GstVideoMeta point to location of merge-mapped buffer memories (see "gst_buffer_find_memory()") instead of raw memory location for each plane, make adjustment to comply this rule.
Also some cleanups.
Fixes 023577e391
When in driver mode (mode=provide), the process() function is never
called. It needs to be triggered manually every now and then.
This fixes starting a mode=provide sink, but it doesn't fix re-starting
it... if the client disconnects while streaming, all buffers are getting
filled up and the pool blocks in aqcuiring one more; when the client
connects again, even if we signal the cond to unblock the pool, it still
can't acquire any more buffers and deadlocks.
Relates to: #1980
Use target-object=<serial/name> instead of path=<id> for specifying
sink/src targets. Deprecate path= argument.
Change device provider to preferably expose serials instead of ids.
Make the alignment parameter optional when negotiating buffers.
Default to a 16 bytes alignment and adjust for the max cpu
alignment.
Remove the useless align buffer parameter in plugins, we always
set it to 16 anyway.
Gstreamer simply mmap()s the DMA buffers. This can be very very slow on
some hardware, so don't ask for it. For gstreamer to support DMA
buffers, it will need to support a proper way of transferring the
buffers from the GPU to the CPU, e.g. using EGL/OpenGL.
When we manually acquire buffers from the pool, we never release them.
But because gst_buffer_pool_acquire_buffer() adds a reference to the pool,
this leaks the pool eventually.
To fix this, just unref the buffer after it has been sent. This releases
it back to the pool and unrefs the pool.
This has no significant effect to the stream, since the underlying
pw buffers are actually pooled in the pw_stream. It just prevents leaking.
When pipewiresink needs to copy data, it has to resize the destination
buffer (to a smaller size) in order to send the correct data size to
pipewire. When this dest buffer is reused later, it will still have
this smaller size as its total size and the copy may discard data
from upstream if the new upstream buffer is bigger than the last one
that was copied on the same dest buffer.
PipeWire does not give DMABUF anymore to clients unless they
explicitly ask for it. This results in v4l2 sending an fd and
the mmapoffset + size fields in the buffer.
GStreamer does not handle the mmapoffset in the fd allocator and
thus runs into mmap problems when using the memory.
Enable DMABuf explicitly to work around the problem until
GStreamer is fixed.
Fixes#1115
With sending and receiving pipelines:
gst-launch videotestsrc ! tee ! pipewiresink mode=provide
gst-launch pipewiresrc path=<node_id> ! videoconvert ! autovideosink
The first pipeline crashes when the other connects.
It happens because SPA_PARAM_BUFFERS_size passed to
pw_stream_update_params() upon pipewiresink's buffer pool activation is
zero. As a consequence, in gst_pipewire_pool_wrap_buffer(), d->type will
be SPA_ID_INVALID and the created GstBuffer will have no memory
appended. This ultimately leads to segfault in do_send_buffer().
If size hasn't been set in GstPipeWirePool's config before the
first buffer is to be rendered by pipewiresink, configure the pool with
the size of that first incoming buffer and activate the pool.