When PW source is used with something like Camera and the camera is
disconnected, all buffers are removed and stream will be paused.
When using PW sink with source, the sink side pipeline can go to EOS.
This again results in all the buffers being removed and stream being
paused on the source side. PW source side pipeline can also crash if
the sink was in the middle of frame copying a buffer to render which
got removed.
Handle this scenario by sending a flush-start event at the start of
buffer removal and flush-stop at the end followed by an end of stream
or pipeline error depending on user selection.
For a pipeline like below, we might want to dynamically switch the audio
source.
gst-launch-1.0 -e pipewiresrc autoconnect=false ! queue ! audioconvert ! autoaudiosink
On switching to a different audio source, any one of driver, quantum
or clock rate might change which changes the return `result` value of
gst_pipewire_clock_get_internal_time.
This can result in the basesrc create function incorrectly waiting in
gst_clock_id_wait. We post clock lost message to fix this. In the case
of gst-launch, it will set the pipeline to PAUSED and then PLAYING to
to force a new clock and a new base_time distribution.
Without the clock lost message, the following can be seen
before re-linking to a different source
0:00:30.887602864 79499 0x7fffe8000d40 DEBUG GST_CLOCK gstsystemclock.c:1158:gst_system_clock_id_wait_jitter_unlocked:<pipewireclock0> entry 0x7fffd803fad0 time 0:00:17.024565416 now 0:00:17.024109144 diff (time-now) 456272
after re-linking to a different source
0:00:45.790843245 79499 0x7fffe8000d40 DEBUG GST_CLOCK gstsystemclock.c:1158:gst_system_clock_id_wait_jitter_unlocked:<pipewireclock0> entry 0x7fffd803fad0 time 0:00:31.927694059 now 0:00:17.066883864 diff (time-now) 14860810195
With the clock lost message, the following can be seen
before re-linking to a different source
0:01:09.336533552 89461 0x7fffe8000d40 DEBUG GST_CLOCK gstsystemclock.c:1158:gst_system_clock_id_wait_jitter_unlocked:<pipewireclock0> entry 0x7fffd803fad0 time 0:00:58.198536772 now 0:00:58.197444926 diff (time-now) 1091846
after re-linking to a different source
0:01:21.659827958 89461 0x7fffe8000d40 DEBUG GST_CLOCK gstsystemclock.c:1158:gst_system_clock_id_wait_jitter_unlocked:<pipewireclock0> entry 0x7fffd803fad0 time 0:28:24.853517646 now 0:28:24.853527204 diff (time-now) -9558
Note the difference in `time` and `now` fields of the above log message.
This is easy to reproduce by using a pipewiresink as the audio source
with a pipeline like below, as one of the sources during switching.
gst-launch-1.0 -e audiotestsrc wave=ticks ! audioconvert ! audio/x-raw,format=F32LE,rate=48000,channels=1 !
pipewiresink stream-properties="props,media.class=Audio/Source,node.description=pwsink" client-name=pwsink
Applications need to handle the GST_MESSAGE_CLOCK_LOST message in their
bus handlers.
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>
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.
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
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.
In case of the video, if the buffer to be rendered is from upstream and
not from the pipewirepool, map the memory into video frames and copy the
frames instead of doing a buffer copy.
Avoid splitting of buffers in the case of video, because that might break
the frame layout, especially planar formats, for the applications which
use pipewiresink as a camera source to capture video.
Deprecate the `always-copy` property in pipewiresrc and use the new
property `use-bufferpool` in both pipewiresrc and pipewiresink to
specify whether to use the gstpipewirepool or not.
The gstpipewirepool is used for video by default unless `use-bufferpool`
is set to `false` and it is not used for audio by default unless
`use-bufferpool` is set to `true`
During negotiation, ensure that possible_caps exists before calling
gst_caps_intersect_full(). Sometimes possible_caps seem to be NULL which
cause the GST_IS_CAPS assert to fail.
The reason for the occasional NULL possible_caps during initial
negotiation is unknown, but this is also possible during renegotiation.
The handle_format_change() may be triggered before the next create(),
win which case we have not yet discovered in the streaming thread that
we are in the NOT_NEGOTIATED state. We should guard against this
possibility as well.
When we do any other blocking in the render function, we should unblock
and call _wait_preroll() when we go to PAUSED.
We can have this situation when all the buffers are queued in the
pw_stream and we get a new _render() call. We can't get more buffers
from the pool and so we must block and wait. When we go to PAUSED we
need to unlock and go to _wait_preroll(). Implement this by setting a
pool paused flag that is set when the sink goes to paused, we can then
return a special value that does the wait_preroll().
See !2248
Setting bufferpool to flushing state in PAUSED state is preventing the
buffer flow if there is a seek/flush event.
Instead, set the bufferpool to flushing during the `flush-start` event
and clear it during the `flush-stop`
Deactivate/activate the stream during flush event only if the sink is
in the PLAYING state. In the PAUSED or READY state, the stream would be
inactive and we do not want to alter that
flush the pw buffers to the stream's queue during a FLUSH_START event
and return the unqueued pw buffers, if they are dropped/released without
being rendered, so they can be available to be dequeued for the subsequent
`acquire` calls
For some streams, the buffer size is changed and may exceed
the acquired buffer size which is acquired from the pool of
pipewiresink. Need split buffer and send them in turn for
this case.
The value is used when a the format changes in handle_format_change(),
and while it seems this was typically expected to happen async and thus
protected by the thread lock, there are cases (such as with
auto-port-config) where a param might be set within the
pw_stream_connect() call itself (in the case of auto-port-config, by the
impl_init() of the audioadapter).
Use the pipewire ticks again as the clock source.
This was disabled because the v4l2 sources created bad ticks for the
graph. Now that this is improved we can enable the ticks again.
This has the advantage that simple audio playback does not drift
anymore. The only remaining problem would be timestamp drift or
discontinuities, which we don't handle here yet.
If the peer announces an interlace mode then use it. Otherwise assume
that the video is not interlaced.
This also fixes a problem with caps negotiation:
If downstream reports caps with not fixated interlace mode, e.g.
"interlace-mode=(string){ progressive, interleaved, mixed }"
then without this, the caps handle_format_change() (in the pipewiresrc)
are not fixed and the source waits forever for the negotiation to
finish.
GSTs DMA_DRM API does explicitely not support implicit modifiers and
trying to use it would result in assertions like
```
gst_video_dma_drm_fourcc_to_string: assertion 'modifier != DRM_FORMAT_MOD_INVALID' failed
```
Fixes: f1b75fc6f ("gst: Add support for DMA_DRM / explicit modifiers")