diff --git a/spa/plugins/avb/avb-pcm-sink.c b/spa/plugins/avb/avb-pcm-sink.c index 183bc16a0..671d7e9db 100644 --- a/spa/plugins/avb/avb-pcm-sink.c +++ b/spa/plugins/avb/avb-pcm-sink.c @@ -45,6 +45,13 @@ static void reset_props(struct props *props) { + snprintf(props->ifname, sizeof(props->ifname), "%s", DEFAULT_IFNAME); + parse_addr(props->addr, DEFAULT_ADDR); + props->prio = DEFAULT_PRIO; + parse_streamid(&props->streamid, DEFAULT_STREAMID); + props->mtt = DEFAULT_MTT; + props->t_uncertainty = DEFAULT_TU; + props->frames_per_pdu = DEFAULT_FRAMES_PER_PDU; } static void emit_node_info(struct state *this, bool full) @@ -123,74 +130,10 @@ static int impl_node_enum_params(void *object, int seq, switch (id) { case SPA_PARAM_PropInfo: { - struct props *p = &this->props; - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.ifname"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB interface name"), - SPA_PROP_INFO_type, SPA_POD_Stringn(p->ifname, sizeof(p->ifname)), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.macaddr"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB MAC address"), - SPA_PROP_INFO_type, SPA_POD_Stringn(p->addr, sizeof(p->addr)), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 2: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.prio"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB stream priority"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->prio, 0, INT32_MAX), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 3: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.streamid"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB stream id"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(p->streamid, 0LL, UINT64_MAX), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 4: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.mtt"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB mtt"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->mtt, 0, INT32_MAX), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 5: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.time-uncertainty"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB time uncertainty"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->t_uncertainty, 0, INT32_MAX), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 6: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.frames-per-pdu"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB frames per packet"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->frames_per_pdu, 0, INT32_MAX), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 7: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("avb.ptime-tolerance"), - SPA_PROP_INFO_description, SPA_POD_String("The AVB packet tolerance"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->ptime_tolerance, 0, INT32_MAX), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; default: + param = spa_avb_enum_propinfo(this, result.index, &b); + if (param == NULL) return 0; } break; @@ -588,6 +531,7 @@ static int port_set_format(void *object, struct port *port, return 0; spa_log_debug(this->log, "clear format"); + port->have_format = false; spa_avb_clear_format(this); clear_buffers(this, port); } else { @@ -607,6 +551,7 @@ static int port_set_format(void *object, struct port *port, return err; port->current_format = info; + port->have_format = true; } this->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS; diff --git a/spa/plugins/avb/avb-pcm.c b/spa/plugins/avb/avb-pcm.c index e7834fbc0..85ab8fd95 100644 --- a/spa/plugins/avb/avb-pcm.c +++ b/spa/plugins/avb/avb-pcm.c @@ -47,6 +47,7 @@ static int avb_set_param(struct state *state, const char *k, const char *s) { + struct props *p = &state->props; int fmt_change = 0; if (spa_streq(k, SPA_KEY_AUDIO_CHANNELS)) { state->default_channels = atoi(s); @@ -64,6 +65,22 @@ static int avb_set_param(struct state *state, const char *k, const char *s) state->n_allowed_rates = spa_avb_parse_rates(state->allowed_rates, MAX_RATES, s, strlen(s)); fmt_change++; + } else if (spa_streq(k, "avb.ifname")) { + snprintf(p->ifname, sizeof(p->ifname), "%s", s); + } else if (spa_streq(k, "avb.macaddr")) { + parse_addr(p->addr, s); + } else if (spa_streq(k, "avb.prio")) { + p->prio = atoi(s); + } else if (spa_streq(k, "avb.streamid")) { + parse_streamid(&p->streamid, s); + } else if (spa_streq(k, "avb.mtt")) { + p->mtt = atoi(s); + } else if (spa_streq(k, "avb.time-uncertainty")) { + p->t_uncertainty = atoi(s); + } else if (spa_streq(k, "avb.frames-per-pdu")) { + p->frames_per_pdu = atoi(s); + } else if (spa_streq(k, "avb.ptime-tolerance")) { + p->ptime_tolerance = atoi(s); } else if (spa_streq(k, "latency.internal.rate")) { state->process_latency.rate = atoi(s); } else if (spa_streq(k, "latency.internal.ns")) { @@ -120,6 +137,8 @@ struct spa_pod *spa_avb_enum_propinfo(struct state *state, uint32_t idx, struct spa_pod_builder *b) { struct spa_pod *param; + struct props *p = &state->props; + char tmp[128]; switch (idx) { case 0: @@ -172,6 +191,72 @@ struct spa_pod *spa_avb_enum_propinfo(struct state *state, SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; } + case 5: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.ifname"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB interface name"), + SPA_PROP_INFO_type, SPA_POD_Stringn(p->ifname, sizeof(p->ifname)), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 6: + format_addr(tmp, sizeof(tmp), p->addr); + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.macaddr"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB MAC address"), + SPA_PROP_INFO_type, SPA_POD_String(tmp), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 7: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.prio"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB stream priority"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->prio, 0, INT32_MAX), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 8: + format_streamid(tmp, sizeof(tmp), p->streamid); + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.streamid"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB stream id"), + SPA_PROP_INFO_type, SPA_POD_String(tmp), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 9: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.mtt"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB mtt"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->mtt, 0, INT32_MAX), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 10: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.time-uncertainty"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB time uncertainty"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->t_uncertainty, 0, INT32_MAX), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 11: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.frames-per-pdu"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB frames per packet"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->frames_per_pdu, 0, INT32_MAX), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 12: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.ptime-tolerance"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB packet tolerance"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->ptime_tolerance, 0, INT32_MAX), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; case 13: param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, @@ -206,6 +291,7 @@ struct spa_pod *spa_avb_enum_propinfo(struct state *state, int spa_avb_add_prop_params(struct state *state, struct spa_pod_builder *b) { + struct props *p = &state->props; struct spa_pod_frame f[1]; char buf[1024]; @@ -232,6 +318,28 @@ int spa_avb_add_prop_params(struct state *state, struct spa_pod_builder *b) spa_pod_builder_string(b, SPA_KEY_AUDIO_ALLOWED_RATES); spa_pod_builder_string(b, buf); + spa_pod_builder_string(b, "avb.ifname"); + spa_pod_builder_string(b, p->ifname); + + format_addr(buf, sizeof(buf), p->addr); + spa_pod_builder_string(b, "avb.macadr"); + spa_pod_builder_string(b, buf); + + spa_pod_builder_string(b, "avb.prio"); + spa_pod_builder_int(b, p->prio); + + format_streamid(buf, sizeof(buf), p->streamid); + spa_pod_builder_string(b, "avb.streamid"); + spa_pod_builder_string(b, buf); + spa_pod_builder_string(b, "avb.mtt"); + spa_pod_builder_int(b, p->mtt); + spa_pod_builder_string(b, "avb.time-uncertainty"); + spa_pod_builder_int(b, p->t_uncertainty); + spa_pod_builder_string(b, "avb.frames-per-pdu"); + spa_pod_builder_int(b, p->frames_per_pdu); + spa_pod_builder_string(b, "avb.ptime-tolerance"); + spa_pod_builder_int(b, p->ptime_tolerance); + spa_pod_builder_string(b, "latency.internal.rate"); spa_pod_builder_int(b, state->process_latency.rate); @@ -456,7 +564,7 @@ static int setup_socket(struct state *state) snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", p->ifname); res = ioctl(fd, SIOCGIFINDEX, &req); if (res < 0) { - spa_log_error(state->log, "SIOCGIFINDEX failed: %m"); + spa_log_error(state->log, "SIOCGIFINDEX %s failed: %m", p->ifname); res = -errno; goto error_close; } @@ -473,7 +581,7 @@ static int setup_socket(struct state *state) res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &p->prio, sizeof(p->prio)); if (res < 0) { - spa_log_error(state->log, "setsockopt(SO_PRIORITY) failed: %m"); + spa_log_error(state->log, "setsockopt(SO_PRIORITY %d) failed: %m", p->prio); res = -errno; goto error_close; } @@ -505,7 +613,7 @@ static int setup_socket(struct state *state) res = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(struct packet_mreq)); if (res < 0) { - spa_log_error(state->log, "setsockopt(ADD_MEMBERSHIP failed: %m"); + spa_log_error(state->log, "setsockopt(ADD_MEMBERSHIP) failed: %m"); res = -errno; goto error_close; } @@ -589,6 +697,7 @@ int spa_avb_set_format(struct state *state, struct spa_audio_info *fmt, uint32_t state->format = fmt->info.raw.format; state->rate = fmt->info.raw.rate; state->channels = fmt->info.raw.channels; + state->blocks = 1; state->stride = state->channels * frame_size(state->format); if ((res = setup_socket(state)) < 0) @@ -785,6 +894,7 @@ static int handle_play(struct state *state, uint64_t current_time) avail = spa_ringbuffer_get_read_index(&state->ring, &index); if (avail < (int32_t) state->duration) { spa_log_warn(state->log, "underrun %d", avail); + goto done; } pdu_count = state->duration / p->frames_per_pdu; @@ -817,6 +927,7 @@ static int handle_play(struct state *state, uint64_t current_time) } spa_ringbuffer_read_update(&state->ring, index); +done: spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA); return 0; diff --git a/spa/plugins/avb/avb-pcm.h b/spa/plugins/avb/avb-pcm.h index 29a55f1b4..3f69816ad 100644 --- a/spa/plugins/avb/avb-pcm.h +++ b/spa/plugins/avb/avb-pcm.h @@ -59,6 +59,14 @@ extern "C" { #define MAX_RATES 16 +#define DEFAULT_IFNAME "eth0" +#define DEFAULT_ADDR "01:AA:AA:AA:AA:AA" +#define DEFAULT_PRIO 0 +#define DEFAULT_STREAMID "AA:BB:CC:DD:EE:FF:0000" +#define DEFAULT_MTT 5000000 +#define DEFAULT_TU 1000000 +#define DEFAULT_FRAMES_PER_PDU 8 + #define DEFAULT_PERIOD 1024u #define DEFAULT_RATE 48000u #define DEFAULT_CHANNELS 8u @@ -74,6 +82,53 @@ struct props { int ptime_tolerance; }; +static inline int parse_addr(unsigned char addr[ETH_ALEN], const char *str) +{ + unsigned char ad[ETH_ALEN]; + if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &ad[0], &ad[1], &ad[2], &ad[3], &ad[4], &ad[5]) != 6) + return -EINVAL; + memcpy(addr, ad, sizeof(ad)); + return 0; +} +static inline char *format_addr(char *str, size_t size, const unsigned char addr[ETH_ALEN]) +{ + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x", + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + return str; +} + +static inline int parse_streamid(uint64_t *streamid, const char *str) +{ + unsigned char addr[6]; + unsigned short unique_id; + if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hx", + &addr[0], &addr[1], &addr[2], &addr[3], + &addr[4], &addr[5], &unique_id) != 7) + return -EINVAL; + *streamid = (uint64_t) addr[0] << 56 | + (uint64_t) addr[1] << 48 | + (uint64_t) addr[2] << 40 | + (uint64_t) addr[3] << 32 | + (uint64_t) addr[4] << 24 | + (uint64_t) addr[5] << 16 | + unique_id; + return 0; +} +static inline char *format_streamid(char *str, size_t size, const uint64_t streamid) +{ + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x:%04x", + (uint8_t)(streamid >> 56), + (uint8_t)(streamid >> 48), + (uint8_t)(streamid >> 40), + (uint8_t)(streamid >> 32), + (uint8_t)(streamid >> 24), + (uint8_t)(streamid >> 16), + (uint16_t)(streamid)); + return str; +} + #define MAX_BUFFERS 32 struct buffer { diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index 3cfbbcfc7..44272688a 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -238,11 +238,19 @@ context.objects = [ #} { factory = adapter args = { - factory.name = avb.pcm.sink - node.name = AVB-sink - node.description = "AVB Sink" - media.class = "Audio/Sink" - audio.channels = 8 + factory.name = avb.pcm.sink + node.name = AVB-sink + node.description = "AVB Sink" + media.class = "Audio/Sink" + audio.channels = 8 + avb.ifname = "enp3s0" + #avb.macaddr = "01:AA:AA:AA:AA:AA" + #avb.prio = 0 + #avb.streamid = "AA:BB:CC:DD:EE:FF:0000" + #avb.mtt = 50000000 + #avb.time-uncertainty = 1000000 + #avb.frames-per-pdu = 8 + #avb.ptime-tolerance = 100000 } } ]