Let's make sure we own the memory in buffers, so that we can be
resilient to the PW link going away. This currently maintains the status
quo of copying data into the pipewirepool for sending to the remote end,
but moves the allocation of buffers so that ownership is maintained by
the sink in all cases.
There are some tricky corners, especially with bufferpool vs. buffers
param negotiation -- bufferpool parameters can be negotiated in
GStreamer before the link even comes up, so we try to adapt the buffers
param to use the negotiated value. For now, that is more brittle than
tying those two aspects together. We can revisit this if we can find a
way to tie pipeline state and link state more closely.
Co-authored-by: Arun Raghavan <arun@asymptotic.io>
Parse and use DSP formats.
Redo the conversion setup when the formats changed. We usually do this
when starting the node but the formats can change while running as well.
When the builder is overflowed, we might get a NULL pod. This is a valid
situation that we need to handle because it can be used to get the
required builder buffer size.
Get the dataType field from the Buffer param. This is a mask of the
supported data types for the buffers. Pass this to the allocating node
if there is one, otherwise use MemPtr as the allocated format.
The set_format function can return 1 when the format was adjusted to the
nearest supported format so return this from the port_set_param
function.
This instructs the adapter to recheck the configured format so that it
can store the adjuted format on the converter.
Because we advertize on out ports that we support DYNAMIC data, we need
to read the data pointer directly from the buffer and only fall back to
our cache (mmaped) pointer when it is NULL.
With DYNAMIC data, the peer element (mixer-dsp) directly copies the
input data pointer into the buffer data in the processing loop in order
to avoid a memcpy when there is no mixing needed.
When there is no data and the buffer is mmapable, try to mmap it. Unmap
again when clearing the buffers.
Use the mmaped data pointer of the buffer when processing.
Prefer to let the follower allocate buffers. If we are allocating
buffers, first do use_buffers on the allocating node or else the
non-allocating node just ends up with NULL buffers.
Keep the passthrough flag up to date when we unset a port format or when
it changes.
We should only fill in the buffer data/fd when the ALLOC flag is set.
We should only take the passthrough input buffer as output when we are
in passthrough mode.
Copy the header metadata.
A number of changes for correctness.
1) We expose the actualy min and max values we support in the
allocation query.
2) We don't support max_buffers as 0, as unlimited buffers is not an
option
3) In ParamBuffers, we request the max_buffers from bufferpool config,
as we cannot dynamically allocate buffers
We need to make sure the memory sizes are correctly initialised so the
meta makes sense, and we don't copy the meta from the input buffer as
that doesn't make sense given we have our own meta already.
The audioconverter starts in Convert mode, so make sure it goes to the
None mode before we attempt to reconfigure ourselves.
Also remove the ports on audioconvert when going to None mode. This used
to somewhat work because we configured it in DSP mode without any
params, which is like None without ports.
Setting the default size to 0 and outside of the min/max range now means
that there is no suggestion for the size and it should use the
suggestion of the peer.
Counter-intuitive as it seems, when we are driving the clock, we can't
also provide a clock from PipeWire to the pipeline -- we need the
pipeline to drive the graph.
So we make the mode control whether we provide a clock or not.
When using PW source, one might want to dynamically link PW source to
a different source. Setting possible_caps to NULL prevents the caps
intersect from returning a successful result on format change. Do not
set possible_caps to NULL as we get that from peer caps which should
stay the same ideally for the duration of pipeline run. That allows
re-linking PW source any number of times with a pipeline like below.
gst-launch-1.0 pipewiresrc autoconnect=false ! queue ! video/x-raw,format=YUY2 ! videoconvert ! xvimagesink
The above pipeline can be made to switch between a camera source and a
screen capture source like wf-recorder.
Note that this fix only improves the status quo and won't work if the
peer caps change due to a re-negotiation.
We might end up in a situation where depending on the pipeline,
intersect might not give us fixated caps.
Possible example of such a pipeline can be below.
gst-launch-1.0 -e pipewiresrc target-object=<path> ! audioconvert !
audio/x-raw,format=S16LE,rate=48000,channels=2 ! lamemp3enc !
filesink location=test.mp3
This results in non-fixated caps like below when intersecting caps from
format param and possible_caps which depends on what we have downstream
in the pipeline.
audio/x-raw, layout=(string)interleaved, format=(string)S16LE, rate=(int)48000, channels=(int)2, channel-mask=(bitmask)0x0000000000000003;
audio/x-raw, layout=(string)interleaved, format=(string)S16LE, rate=(int)48000, channels=(int)2
To fix this, fixate the caps explicitly.
The device passed to gst_device_provider_device_add() is transfer:floating, so
we need increase its ref, otherwise the pointer we keep internally will be a
dangling ref.
Also gst_device_provider_device_remove() doesn't actually release the device, so
we have to do it ourselves.
Fixes#4616
When we construct an Enum, check if we only added 1 value and remove the
duplicate default value we added. If we added more values, promote the
choice to an enum.
In case negotiation is first attempted with unfixed caps, bufferpool support was
unconditionally disabled. Then at a second caps negotiation attempt it wasn't
restored according to the property value.