From 09ea37cfdf6dcca45df75aa2a6598a60f4385161 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 25 Apr 2022 16:20:55 +0200 Subject: [PATCH] roc: add fec_code option Add fec_code option. Fix resample.profile parsing. --- .../modules/module-roc-sink-input.c | 6 + .../modules/module-roc-sink.c | 15 +-- .../modules/module-roc-source.c | 6 + src/modules/module-roc-sink.c | 79 ++++++++--- src/modules/module-roc-source.c | 124 +++++++++++------- 5 files changed, 152 insertions(+), 78 deletions(-) diff --git a/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c b/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c index bc84904e9..e3ec7b69d 100644 --- a/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c +++ b/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c @@ -116,6 +116,7 @@ static const struct spa_dict_item module_roc_sink_input_info[] = { { PW_KEY_MODULE_USAGE, "sink= " "sink_input_properties= " "resampler_profile=|disable|high|medium|low " + "fec_code=|disable|rs8m|ldpc " "sess_latency_msec= " "local_ip= " "local_source_port= " @@ -178,6 +179,11 @@ struct module *create_module_roc_sink_input(struct impl *impl, const char *argum pw_properties_set(props, "resampler_profile", NULL); } + if ((str = pw_properties_get(props, "fec_code")) != NULL) { + pw_properties_set(roc_props, "fec.code", str); + pw_properties_set(props, "fec_code", NULL); + } + module = module_new(impl, sizeof(*d)); if (module == NULL) { res = -errno; diff --git a/src/modules/module-protocol-pulse/modules/module-roc-sink.c b/src/modules/module-protocol-pulse/modules/module-roc-sink.c index 5a5450158..5c829b209 100644 --- a/src/modules/module-protocol-pulse/modules/module-roc-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-roc-sink.c @@ -35,10 +35,6 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define ROC_DEFAULT_IP "0.0.0.0" -#define ROC_DEFAULT_SOURCE_PORT "10001" -#define ROC_DEFAULT_REPAIR_PORT "10002" - struct module_roc_sink_data { struct module *module; @@ -119,6 +115,7 @@ static const struct spa_dict_item module_roc_sink_info[] = { { PW_KEY_MODULE_DESCRIPTION, "roc sink" }, { PW_KEY_MODULE_USAGE, "sink_name= " "sink_properties= " + "fec_code=|disable|rs8m|ldpc " "local_ip= " "remote_ip= " "remote_source_port= " @@ -172,22 +169,20 @@ struct module *create_module_roc_sink(struct impl *impl, const char *argument) if ((str = pw_properties_get(props, "local_ip")) != NULL) { pw_properties_set(roc_props, "local.ip", str); pw_properties_set(props, "local_ip", NULL); - } else { - pw_properties_set(roc_props, "local.ip", ROC_DEFAULT_IP); } if ((str = pw_properties_get(props, "remote_source_port")) != NULL) { pw_properties_set(roc_props, "remote.source.port", str); pw_properties_set(props, "remote_source_port", NULL); - } else { - pw_properties_set(roc_props, "remote.source.port", ROC_DEFAULT_SOURCE_PORT); } if ((str = pw_properties_get(props, "remote_repair_port")) != NULL) { pw_properties_set(roc_props, "remote.repair.port", str); pw_properties_set(props, "remote_repair_port", NULL); - } else { - pw_properties_set(roc_props, "remote.repair.port", ROC_DEFAULT_REPAIR_PORT); + } + if ((str = pw_properties_get(props, "fec_code")) != NULL) { + pw_properties_set(roc_props, "fec.code", str); + pw_properties_set(props, "fec_code", NULL); } module = module_new(impl, sizeof(*d)); diff --git a/src/modules/module-protocol-pulse/modules/module-roc-source.c b/src/modules/module-protocol-pulse/modules/module-roc-source.c index 1746c6858..8d33313c0 100644 --- a/src/modules/module-protocol-pulse/modules/module-roc-source.c +++ b/src/modules/module-protocol-pulse/modules/module-roc-source.c @@ -116,6 +116,7 @@ static const struct spa_dict_item module_roc_source_info[] = { { PW_KEY_MODULE_USAGE, "source_name= " "source_properties= " "resampler_profile=|disable|high|medium|low " + "fec_code=|disable|rs8m|ldpc " "sess_latency_msec= " "local_ip= " "local_source_port= " @@ -183,6 +184,11 @@ struct module *create_module_roc_source(struct impl *impl, const char *argument) pw_properties_set(props, "resampler_profile", NULL); } + if ((str = pw_properties_get(props, "fec_code")) != NULL) { + pw_properties_set(roc_props, "fec.code", str); + pw_properties_set(props, "fec_code", NULL); + } + module = module_new(impl, sizeof(*d)); if (module == NULL) { res = -errno; diff --git a/src/modules/module-roc-sink.c b/src/modules/module-roc-sink.c index e8488c173..dfda8a2b2 100644 --- a/src/modules/module-roc-sink.c +++ b/src/modules/module-roc-sink.c @@ -116,12 +116,28 @@ struct module_roc_sink_data { roc_context *context; roc_sender *sender; + roc_fec_code fec_code; char *local_ip; char *remote_ip; int remote_source_port; int remote_repair_port; }; +static int roc_parse_fec_code(roc_fec_code *out, const char *str) +{ + if (!str || !*str) + *out = ROC_FEC_DEFAULT; + else if (spa_streq(str, "disable")) + *out = ROC_FEC_DISABLE; + else if (spa_streq(str, "rs8m")) + *out = ROC_FEC_RS8M; + else if (spa_streq(str, "ldpc")) + *out = ROC_FEC_LDPC_STAIRCASE; + else + return -EINVAL; + return 0; +} + static void stream_destroy(void *d) { struct module_roc_sink_data *data = d; @@ -260,6 +276,7 @@ static int roc_sink_setup(struct module_roc_sink_data *data) uint32_t n_params; uint8_t buffer[1024]; int res; + roc_protocol audio_proto, repair_proto; if (roc_address_init(&data->local_addr, ROC_AF_AUTO, data->local_ip, 0)) { pw_log_error("Invalid local IP address"); @@ -291,6 +308,7 @@ static int roc_sink_setup(struct module_roc_sink_data *data) sender_config.frame_sample_rate = 44100; sender_config.frame_channels = ROC_CHANNEL_SET_STEREO; sender_config.frame_encoding = ROC_FRAME_ENCODING_PCM_FLOAT; + sender_config.fec_code = data->fec_code; /* Fixed to be the same as ROC sender config above */ info.rate = 44100; @@ -312,16 +330,34 @@ static int roc_sink_setup(struct module_roc_sink_data *data) return -EINVAL; } - if (roc_sender_connect(data->sender, ROC_PORT_AUDIO_SOURCE, ROC_PROTO_RTP_RS8M_SOURCE, + switch (data->fec_code) { + case ROC_FEC_DEFAULT: + case ROC_FEC_RS8M: + audio_proto = ROC_PROTO_RTP_RS8M_SOURCE; + repair_proto = ROC_PROTO_RS8M_REPAIR; + break; + case ROC_FEC_LDPC_STAIRCASE: + audio_proto = ROC_PROTO_RTP_LDPC_SOURCE; + repair_proto = ROC_PROTO_LDPC_REPAIR; + break; + default: + audio_proto = ROC_PROTO_RTP; + repair_proto = 0; + break; + } + + if (roc_sender_connect(data->sender, ROC_PORT_AUDIO_SOURCE, audio_proto, &data->remote_source_addr) != 0) { pw_log_error("can't connect roc sender to remote source address"); return -EINVAL; } - if (roc_sender_connect(data->sender, ROC_PORT_AUDIO_REPAIR, ROC_PROTO_RS8M_REPAIR, - &data->remote_repair_addr) != 0) { - pw_log_error("can't connect roc sender to remote repair address"); - return -EINVAL; + if (repair_proto != 0) { + if (roc_sender_connect(data->sender, ROC_PORT_AUDIO_REPAIR, repair_proto, + &data->remote_repair_addr) != 0) { + pw_log_error("can't connect roc sender to remote repair address"); + return -EINVAL; + } } data->capture = pw_stream_new(data->core, @@ -369,8 +405,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) struct module_roc_sink_data *data; struct pw_properties *props = NULL, *capture_props = NULL; const char *str; - char *local_ip = NULL, *remote_ip = NULL; - int res = 0, remote_repair_port, remote_source_port; + int res = 0; PW_LOG_TOPIC_INIT(mod_topic); @@ -420,7 +455,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) pw_properties_set(capture_props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); if ((str = pw_properties_get(props, "remote.ip")) != NULL) { - remote_ip = strdup(str); + data->remote_ip = strdup(str); pw_properties_set(props, "remote.ip", NULL); } else { pw_log_error("Remote IP not specified"); @@ -429,24 +464,34 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) } if ((str = pw_properties_get(props, "local.ip")) != NULL) { - local_ip = strdup(str); + data->local_ip = strdup(str); pw_properties_set(props, "local.ip", NULL); } else { - local_ip = strdup(ROC_DEFAULT_IP); + data->local_ip = strdup(ROC_DEFAULT_IP); } if ((str = pw_properties_get(props, "remote.source.port")) != NULL) { - remote_source_port = pw_properties_parse_int(str); + data->remote_source_port = pw_properties_parse_int(str); pw_properties_set(props, "remote.source.port", NULL); } else { - remote_source_port = ROC_DEFAULT_SOURCE_PORT; + data->remote_source_port = ROC_DEFAULT_SOURCE_PORT; } if ((str = pw_properties_get(props, "remote.repair.port")) != NULL) { - remote_repair_port = pw_properties_parse_int(str); + data->remote_repair_port = pw_properties_parse_int(str); pw_properties_set(props, "remote.repair.port", NULL); } else { - remote_repair_port = ROC_DEFAULT_REPAIR_PORT; + data->remote_repair_port = ROC_DEFAULT_REPAIR_PORT; + } + if ((str = pw_properties_get(props, "fec.code")) != NULL) { + if (roc_parse_fec_code(&data->fec_code, str)) { + pw_log_error("Invalid fec code %s, using default", str); + data->fec_code = ROC_FEC_DEFAULT; + } + pw_log_info("using fec.code %s %d", str, data->fec_code); + pw_properties_set(props, "fec.code", NULL); + } else { + data->fec_code = ROC_FEC_DEFAULT; } data->core = pw_context_get_object(data->module_context, PW_TYPE_INTERFACE_Core); @@ -472,12 +517,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) &data->core_listener, &core_events, data); - data->capture_props = capture_props; - data->local_ip = local_ip; - data->remote_ip = remote_ip; - data->remote_source_port = remote_source_port; - data->remote_repair_port = remote_repair_port; - if ((res = roc_sink_setup(data)) < 0) goto out; diff --git a/src/modules/module-roc-source.c b/src/modules/module-roc-source.c index 93a2bf7ca..ca7dbaba0 100644 --- a/src/modules/module-roc-source.c +++ b/src/modules/module-roc-source.c @@ -121,7 +121,8 @@ struct module_roc_source_data { roc_context *context; roc_receiver *receiver; - char *resampler_profile; + roc_resampler_profile resampler_profile; + roc_fec_code fec_code; char *local_ip; int local_source_port; int local_repair_port; @@ -137,25 +138,34 @@ static void stream_destroy(void *d) static int roc_parse_resampler_profile(roc_resampler_profile *out, const char *str) { - if (!str || !*str) { + if (!str || !*str) *out = ROC_RESAMPLER_DEFAULT; - return 0; - } else if (spa_streq(str, "disable") == 0) { + else if (spa_streq(str, "disable")) *out = ROC_RESAMPLER_DISABLE; - return 0; - } else if (spa_streq(str, "high") == 0) { + else if (spa_streq(str, "high")) *out = ROC_RESAMPLER_HIGH; - return 0; - } else if (spa_streq(str, "medium") == 0) { + else if (spa_streq(str, "medium")) *out = ROC_RESAMPLER_MEDIUM; - return 0; - } else if (spa_streq(str, "low") == 0) { + else if (spa_streq(str, "low")) *out = ROC_RESAMPLER_LOW; - return 0; - } else { - pw_log_error("Invalid resampler profile: %s", str); + else return -EINVAL; - } + return 0; +} + +static int roc_parse_fec_code(roc_fec_code *out, const char *str) +{ + if (!str || !*str) + *out = ROC_FEC_DEFAULT; + else if (spa_streq(str, "disable")) + *out = ROC_FEC_DISABLE; + else if (spa_streq(str, "rs8m")) + *out = ROC_FEC_RS8M; + else if (spa_streq(str, "ldpc")) + *out = ROC_FEC_LDPC_STAIRCASE; + else + return -EINVAL; + return 0; } static void playback_process(void *data) @@ -265,7 +275,6 @@ static void impl_destroy(struct module_roc_source_data *data) roc_context_close(data->context); free(data->local_ip); - free(data->resampler_profile); free(data); } @@ -291,6 +300,7 @@ static int roc_source_setup(struct module_roc_source_data *data) uint32_t n_params; uint8_t buffer[1024]; int res; + roc_protocol audio_proto, repair_proto; if (roc_address_init(&data->local_addr, ROC_AF_AUTO, data->local_ip, 0)) { pw_log_error("Invalid local IP address"); @@ -309,19 +319,18 @@ static int roc_source_setup(struct module_roc_source_data *data) return -EINVAL; } - memset(&context_config, 0, sizeof(context_config)); - + spa_zero(context_config); data->context = roc_context_open(&context_config); if (!data->context) { pw_log_error("Failed to create roc context"); return -EINVAL; } - memset(&receiver_config, 0, sizeof(receiver_config)); - + spa_zero(receiver_config); receiver_config.frame_sample_rate = 44100; receiver_config.frame_channels = ROC_CHANNEL_SET_STEREO; receiver_config.frame_encoding = ROC_FRAME_ENCODING_PCM_FLOAT; + receiver_config.resampler_profile = data->resampler_profile; /* Fixed to be the same as ROC receiver config above */ info.rate = 44100; @@ -333,12 +342,6 @@ static int roc_source_setup(struct module_roc_source_data *data) pw_properties_setf(data->playback_props, PW_KEY_NODE_RATE, "1/%d", info.rate); - if (roc_parse_resampler_profile(&receiver_config.resampler_profile, - data->resampler_profile)) { - pw_log_error("Invalid resampler profile"); - return -EINVAL; - } - /* * Note that target latency is in nano seconds. * @@ -358,16 +361,33 @@ static int roc_source_setup(struct module_roc_source_data *data) return -EINVAL; } - if (roc_receiver_bind(data->receiver, ROC_PORT_AUDIO_SOURCE, ROC_PROTO_RTP_RS8M_SOURCE, + switch (data->fec_code) { + case ROC_FEC_DEFAULT: + case ROC_FEC_RS8M: + audio_proto = ROC_PROTO_RTP_RS8M_SOURCE; + repair_proto = ROC_PROTO_RS8M_REPAIR; + break; + case ROC_FEC_LDPC_STAIRCASE: + audio_proto = ROC_PROTO_RTP_LDPC_SOURCE; + repair_proto = ROC_PROTO_LDPC_REPAIR; + break; + default: + audio_proto = ROC_PROTO_RTP; + repair_proto = 0; + break; + } + + if (roc_receiver_bind(data->receiver, ROC_PORT_AUDIO_SOURCE, audio_proto, &data->local_source_addr) != 0) { pw_log_error("can't connect roc receiver to local source address"); return -EINVAL; } - - if (roc_receiver_bind(data->receiver, ROC_PORT_AUDIO_REPAIR, ROC_PROTO_RS8M_REPAIR, - &data->local_repair_addr) != 0) { - pw_log_error("can't connect roc receiver to local repair address"); - return -EINVAL; + if (repair_proto != 0) { + if (roc_receiver_bind(data->receiver, ROC_PORT_AUDIO_REPAIR, repair_proto, + &data->local_repair_addr) != 0) { + pw_log_error("can't connect roc receiver to local repair address"); + return -EINVAL; + } } data->playback = pw_stream_new(data->core, @@ -402,6 +422,7 @@ static const struct spa_dict_item module_roc_source_info[] = { { PW_KEY_MODULE_DESCRIPTION, "roc source" }, { PW_KEY_MODULE_USAGE, "source.name= " "resampler.profile=|disable|high|medium|low " + "fec.code=|disable|rs8m|ldpc " "sess.latency.msec= " "local.ip= " "local.source.port= " @@ -417,8 +438,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) struct module_roc_source_data *data; struct pw_properties *props = NULL, *playback_props = NULL; const char *str; - char *local_ip = NULL, *resampler_profile = NULL; - int res = 0, local_repair_port, local_source_port, sess_latency_msec; + int res = 0; PW_LOG_TOPIC_INIT(mod_topic); @@ -466,36 +486,50 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) pw_properties_set(playback_props, PW_KEY_NODE_NETWORK, "true"); if ((str = pw_properties_get(props, "local.ip")) != NULL) { - local_ip = strdup(str); + data->local_ip = strdup(str); pw_properties_set(props, "local.ip", NULL); } else { - local_ip = strdup(ROC_DEFAULT_IP); + data->local_ip = strdup(ROC_DEFAULT_IP); } if ((str = pw_properties_get(props, "local.source.port")) != NULL) { - local_source_port = pw_properties_parse_int(str); + data->local_source_port = pw_properties_parse_int(str); pw_properties_set(props, "local.source.port", NULL); } else { - local_source_port = ROC_DEFAULT_SOURCE_PORT; + data->local_source_port = ROC_DEFAULT_SOURCE_PORT; } if ((str = pw_properties_get(props, "local.repair.port")) != NULL) { - local_repair_port = pw_properties_parse_int(str); + data->local_repair_port = pw_properties_parse_int(str); pw_properties_set(props, "local.repair.port", NULL); } else { - local_repair_port = ROC_DEFAULT_REPAIR_PORT; + data->local_repair_port = ROC_DEFAULT_REPAIR_PORT; } if ((str = pw_properties_get(props, "sess.latency.msec")) != NULL) { - sess_latency_msec = pw_properties_parse_int(str); + data->sess_latency_msec = pw_properties_parse_int(str); pw_properties_set(props, "sess.latency.msec", NULL); } else { - sess_latency_msec = ROC_DEFAULT_SESS_LATENCY; + data->sess_latency_msec = ROC_DEFAULT_SESS_LATENCY; } if ((str = pw_properties_get(props, "resampler.profile")) != NULL) { - resampler_profile = strdup(str); + if (roc_parse_resampler_profile(&data->resampler_profile, str)) { + pw_log_warn("Invalid resampler profile %s, using default", str); + data->resampler_profile = ROC_RESAMPLER_DEFAULT; + } pw_properties_set(props, "resampler.profile", NULL); + } else { + data->resampler_profile = ROC_RESAMPLER_DEFAULT; + } + if ((str = pw_properties_get(props, "fec.code")) != NULL) { + if (roc_parse_fec_code(&data->fec_code, str)) { + pw_log_error("Invalid fec code %s, using default", str); + data->fec_code = ROC_FEC_DEFAULT; + } + pw_properties_set(props, "fec.code", NULL); + } else { + data->fec_code = ROC_FEC_DEFAULT; } data->core = pw_context_get_object(data->module_context, PW_TYPE_INTERFACE_Core); @@ -521,12 +555,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) &data->core_listener, &core_events, data); - data->local_ip = local_ip; - data->local_source_port = local_source_port; - data->local_repair_port = local_repair_port; - data->sess_latency_msec = sess_latency_msec; - data->resampler_profile = resampler_profile; - if ((res = roc_source_setup(data)) < 0) goto out;