The started boolean is insufficient to fully cover the possible internal
states. For this reason, it needs to be replaced by an enum that covers
these states.
Also, due to potential access by both the dataloop and the mainloop,
access to that internal state needs to be synchronized.
Finally, a variable "internal_state" makes for code that is easier to
read, since it emphasizes that this is state that is fully internal
inside the stream (and is not visible to the rtp-sink and rtp-source
modules for example).
The state_changed callbacks fulfill multiple roles, which is both a problem
regarding separation of concerns and regarding code clarity. De facto,
these callbacks cover error reporting, opening connections, and closing
connection, all in one, depending on a state that is arguably an internal
stream detail. The code in these callbacks tie these internal states to
assumptions that opening/closing callbacks is directly tied to specific
state changes in a common way, which is not always true. For example,
stopping the stream may not _actually_ stop it if a background send timer
is still running.
The notion of a "state_changed" callback is also problematic because the
pw_streams that are used in rtp-sink and rtp-source also have a callback
for state changes, causing confusion.
Solve this by replacing state_changed with three new callbacks:
1. report_error : Used for reporting nonrecoverable errors to the caller.
Note that currently, no one does such error reporting, but the feature
does exist, so this callback is introduced to preserve said feature.
2. open_connection : Used for opening a connection. Its optional return
value informs about success or failure.
3. close_connection : Used for opening a connection. Its optional return
value informs about success or failure.
Importantly, these callbacks do not export any internal stream state. This
improves encapsulation, and also makes it possible to invoke these
callbacks in situations that may not neatly map to a state change. One
example could be to close the connection as part of a stream_start call
to close any connection(s) left over from a previous run. (Followup commits
will in fact introduce such measures.)
Improve the spa_ump_to_midi function so that it can consume multiple UMP
messages and produce multiple midi messages.
Some UMP messages (like program changes) need to be translated into up
to 3 midi messages. Do this byt adding a state to the function and by
making it consume the input bytes, just like the spa_ump_from_midi
function.
Adapt code to this new world. This is a little API break..
This fixes the case when synchronization is established but actually not
valid anymore. In such a case, the code would _first_ write to the ring
buffer (at the wrong position due to the invalid sync), and _then_ detect
the bogus synchronization. Reorder the code blocks to _first_ check the
current sync, then resynchronize if neeeded (or perform initial sync if
no sync is established yet), and _then_ write to the ring buffer.
Until now, the timestamp check was comparing the timestamp delta against
the value of the "quantum" variable. However, the timestamps use clock
samples as units, while the "quantum" variable uses nanoseconds. The
outcome is that this check virtually never returned true. Use the
spa_io_clock duration instead of that quantum nanosecond duration to make
the check actually work.
Also, do not just rely on vast timestamp deltas to detect discontinuities;
instead, check first for the presence of the SPA_IO_CLOCK_FLAG_DISCONT
flag to detect said discontinuities.
Direct timestamp mode was incorrectly using over/underrun detection logic
and fill level tracking logic that is actually meant for the other mode
(referred to from now on as "constant latency mode"). Over/underruns are
tracked implicitly in the direct timestamp mode, and the absolute fill
level is not relevant in that mode, since the latency is not needed to
be constant then.
Also improve log lines and the RTP module documentation to define these
buffer modes clearly and explain their differences and use cases.
Opus and MIDI code get TODOs added, since their direct timestamp mode
implementations still may be incorrect. Fixing those will be done in
a separate commit.
When a stream has some delay, a time t1 + delay has to be read in time
t1 to play it when expected.
Decrease target_buffer by delay to start playback sooner, so sound
is played at correct time when delay is applied.
Signed-off-by: Martin Geier <martin.geier@streamunlimited.com>
config.h needs to be consistently included before any standard headers
if we ever want to set feature test macros (like _GNU_SOURCE or whatever)
inside. It can lead to hard-to-debug issues without that.
It can also be problematic just for our own HAVE_* that it may define
if it's not consistently made available before our own headers. Just
always include it first, before everything.
We already did this in many files, just not consistently.
There is no need to encode the potential format in the format.dsp of
control ports, this is just for legacy compatibility with JACK apps. The
actual format can be negotiated with the types field.
Fixes midi port visibility with apps compiled against 1.2, such as JACK
apps in flatpaks.
Some of the more common errors (caused by packet loss, network jitter, ...)
should be reported with INFO unless there is some indication about how
to fix the problem.
Fixes#4559
Idle the source when no packets are received and resume when new packets
arrive.
Add a stream.may-pause property to pause the stream when no packets are
received during the timeout window.
Make sure the rtp.streaming property is updated correctly and as soon as
we get the first packet.
Fixes#4456
We want to track the difference between the PTP timestamp (now) and the
last RTP send, not the synthesized next RTP timestamp (which will always
be smoothly incrementing).
When not using PTP as the driver, it is possible that packet receive and
the process() callback are out of sync, meaning that the target buffer
fill level might be off by upto one ptime's worth of samples
occasionally. This would make the DLL hunt for the target rate, and
cause a constantly varying delay.
Accounting for the delta between the packet receive time and the
process() time allows us to eliminate this jitter, resulting in much
more consistent rate matching.
Make a function that can initialize raw audio info from a dict and fill
in the defaults. We can use this in many of the modules when the audio
format is parsed.
Use the helper instead of duplicating the same code.
Also add some helpers to parse a json array of uint32_t
Move some functions to convert between type name and id.
Add spa_json_begin_array/object to replace
spa_json_init+spa_json_begin_array/object
This function is better because it does not waste a useless spa_json
structure as an iterator. The relaxed versions also error out when the
container is mismatched because parsing a mismatched container is not
going to give any results anyway.
Our current AES67 sender setup requires that that PTP driver drive the
entire graph. This adds support for allowing the AES67 RTP sink to be
driven by an arbitrary driver, while still using the PTP driver for
sending data on the network.
When aes67.driver-group is specified a pw_filter is created with no
ports, node.always-process = true and node.group set to the
aes67.driver-group. When set to PTP, this gives us process callbacks at
the PTP rate which we use to get the current PTP time in the RTP sender
by interpolating the clock snapshots from the pw-filter.
Implementation ideas from Wim Taymans. Co-authored with Sanchayan Maity.
For a detailed reference, refer the following papers by Fons Adriaensen.
- Using a DLL to filter time
(https://kokkinizita.linuxaudio.org/papers/usingdll.pdf)
- Controlling adaptive resampling
(http://kokkinizita.linuxaudio.org/papers/adapt-resamp.pdf)
We allow a quantum of jitter in the write timestamp. The previous value
of 32 seems to be empirically determined, using the actual quantum
allows us to reason about this better.