mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
module-rtp: Allow aes67 send with a non PTP clock
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)
This commit is contained in:
parent
9ccf62d4f6
commit
9f643fec7e
4 changed files with 289 additions and 7 deletions
|
|
@ -98,6 +98,27 @@ struct impl {
|
|||
|
||||
int (*receive_rtp)(struct impl *impl, uint8_t *buffer, ssize_t len);
|
||||
void (*flush_timeout)(struct impl *impl, uint64_t expirations);
|
||||
|
||||
/*
|
||||
* pw_filter where the filter would be driven at the PTP clock
|
||||
* rate with RTP sink being driven at the sink driver clock rate
|
||||
* or some ALSA clock rate.
|
||||
*/
|
||||
struct pw_filter *ptp_sender;
|
||||
struct spa_hook ptp_sender_listener;
|
||||
struct spa_dll ptp_dll;
|
||||
double ptp_corr;
|
||||
bool separate_sender;
|
||||
bool refilling;
|
||||
|
||||
/* Track some variables we need from the sink driver */
|
||||
uint64_t sink_next_nsec;
|
||||
uint64_t sink_nsec;
|
||||
uint64_t sink_resamp_delay;
|
||||
uint64_t sink_quantum;
|
||||
/* And some bookkeping for the sender processing */
|
||||
uint64_t rtp_base_ts;
|
||||
uint32_t rtp_last_ts;
|
||||
};
|
||||
|
||||
static int do_emit_state_changed(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
||||
|
|
@ -161,7 +182,18 @@ static int stream_start(struct impl *impl)
|
|||
|
||||
rtp_stream_emit_state_changed(impl, true, NULL);
|
||||
|
||||
if (impl->separate_sender) {
|
||||
struct spa_dict_item items[1];
|
||||
items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_ALWAYS_PROCESS, "true");
|
||||
|
||||
pw_filter_set_active(impl->ptp_sender, true);
|
||||
pw_filter_update_properties(impl->ptp_sender, NULL, &SPA_DICT_INIT(items, 1));
|
||||
|
||||
pw_log_info("activated pw_filter for separate sender");
|
||||
}
|
||||
|
||||
impl->started = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +206,16 @@ static int stream_stop(struct impl *impl)
|
|||
if (!impl->timer_running)
|
||||
rtp_stream_emit_state_changed(impl, false, NULL);
|
||||
|
||||
if (impl->separate_sender) {
|
||||
struct spa_dict_item items[1];
|
||||
items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_ALWAYS_PROCESS, "false");
|
||||
|
||||
pw_filter_update_properties(impl->ptp_sender, NULL, &SPA_DICT_INIT(items, 1));
|
||||
|
||||
pw_log_info("deactivating pw_filter for separate sender");
|
||||
pw_filter_set_active(impl->ptp_sender, false);
|
||||
}
|
||||
|
||||
impl->started = false;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -304,7 +346,7 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core,
|
|||
const struct rtp_stream_events *events, void *data)
|
||||
{
|
||||
struct impl *impl;
|
||||
const char *str;
|
||||
const char *str, *aes67_driver;
|
||||
char tmp[64];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b;
|
||||
|
|
@ -516,11 +558,18 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core,
|
|||
impl->target_buffer = (uint32_t)((impl->target_buffer / ptime) * impl->psamples);
|
||||
}
|
||||
|
||||
aes67_driver = pw_properties_get(props, "aes67.driver-group");
|
||||
|
||||
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", impl->rate);
|
||||
if (direction == PW_DIRECTION_INPUT) {
|
||||
if (direction == PW_DIRECTION_INPUT && !aes67_driver) {
|
||||
/* While sending, we accept latency-sized buffers, and break it
|
||||
* up and send in ptime intervals using a timer */
|
||||
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
|
||||
impl->target_buffer, impl->rate);
|
||||
} else {
|
||||
/* For receive, and with split sending, we break up the latency
|
||||
* as half being in stream latency, and the rest in our own
|
||||
* ringbuffer latency */
|
||||
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
|
||||
impl->target_buffer / 2, impl->rate);
|
||||
}
|
||||
|
|
@ -559,7 +608,7 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core,
|
|||
params[n_params++] = spa_format_audio_build(&b,
|
||||
SPA_PARAM_EnumFormat, &impl->stream_info);
|
||||
flags |= PW_STREAM_FLAG_AUTOCONNECT;
|
||||
rtp_audio_init(impl, direction);
|
||||
rtp_audio_init(impl, core, direction, aes67_driver);
|
||||
break;
|
||||
case SPA_MEDIA_SUBTYPE_control:
|
||||
params[n_params++] = spa_pod_builder_add_object(&b,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue