From 5b8fa0a9b6907dd6cbb80eabcd41a954d471b9ea Mon Sep 17 00:00:00 2001 From: hackerman-kl Date: Mon, 1 Jun 2026 07:57:19 +0200 Subject: [PATCH] milan-avb: UNSUPPORTED_FORMAT per-PDU vs current format from descriptor --- src/modules/module-avb/aaf.h | 18 +++++++++++ src/modules/module-avb/aecp-aem.h | 28 ++++++++++------ src/modules/module-avb/stream.c | 54 +++++++++++++++++++------------ 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/modules/module-avb/aaf.h b/src/modules/module-avb/aaf.h index da61dc4a3..b50e7c109 100644 --- a/src/modules/module-avb/aaf.h +++ b/src/modules/module-avb/aaf.h @@ -79,4 +79,22 @@ struct avb_packet_aaf { uint8_t payload[0]; } __attribute__ ((__packed__)); +/* IEEE 1722-2016 Table 18: AAF nsr code to rate in Hz, 0 if user/reserved. */ +static inline uint32_t avb_aaf_nsr_to_rate(uint8_t nsr) +{ + switch (nsr) { + case AVB_AAF_PCM_NSR_8KHZ: return 8000; + case AVB_AAF_PCM_NSR_16KHZ: return 16000; + case AVB_AAF_PCM_NSR_24KHZ: return 24000; + case AVB_AAF_PCM_NSR_32KHZ: return 32000; + case AVB_AAF_PCM_NSR_44_1KHZ: return 44100; + case AVB_AAF_PCM_NSR_48KHZ: return 48000; + case AVB_AAF_PCM_NSR_88_2KHZ: return 88200; + case AVB_AAF_PCM_NSR_96KHZ: return 96000; + case AVB_AAF_PCM_NSR_176_4KHZ: return 176400; + case AVB_AAF_PCM_NSR_192KHZ: return 192000; + default: return 0; + } +} + #endif /* AVB_AAF_H */ diff --git a/src/modules/module-avb/aecp-aem.h b/src/modules/module-avb/aecp-aem.h index 997747ed8..efcb4be1b 100644 --- a/src/modules/module-avb/aecp-aem.h +++ b/src/modules/module-avb/aecp-aem.h @@ -13,6 +13,7 @@ #include "aecp.h" #include "aecp-aem-types.h" #include "packets.h" +#include "aaf.h" /* AVDECC stream_format decoder. * @@ -23,12 +24,8 @@ * plane plus an `is_audio` shortcut so callers can skip non-media streams * (CRF). Fields are 0 when not applicable. * - * NOTE: bit-level decoding inside each subtype is incomplete. Today the - * decoder identifies the subtype reliably and falls back to the historical - * 8 ch / 48 kHz / 24-bit defaults for audio — sufficient for current Milan - * builds where every stream_input_0 / stream_output_0 uses one of those - * formats. TODO Section H.1 (AAF) and Section F (IEC 61883-6 AM824) fields once a real - * conformance run needs other rates / channel counts. */ + * AAF (Section H.1) decodes each field. IEC 61883-6 (Section F) still uses the + * historical 8 ch / 48 kHz defaults, TODO decode it. */ enum avb_aem_stream_format_kind { AVB_AEM_STREAM_FORMAT_KIND_UNKNOWN = 0, AVB_AEM_STREAM_FORMAT_KIND_AAF, @@ -44,6 +41,9 @@ struct avb_aem_stream_format_info { uint16_t channels; uint8_t bit_depth; uint16_t samples_per_frame; + uint8_t format; + uint8_t nsr; + uint8_t sparse; }; static inline void avb_aem_stream_format_decode(uint64_t fmt_be, @@ -57,15 +57,23 @@ static inline void avb_aem_stream_format_decode(uint64_t fmt_be, out->channels = 0; out->bit_depth = 0; out->samples_per_frame = 0; + out->format = 0; + out->nsr = 0; + out->sparse = 0; switch (out->subtype) { case AVB_SUBTYPE_AAF: + /* Section H.1 quadlet: nsr[51:48] format[47:40] bit_depth[39:32] + * channels[31:22] samples_per_frame[21:12]. */ out->kind = AVB_AEM_STREAM_FORMAT_KIND_AAF; out->is_audio = true; - out->rate = 48000; - out->channels = 8; - out->bit_depth = 24; - out->samples_per_frame = 6; + out->nsr = (uint8_t)((f >> 48) & 0x0F); + out->format = (uint8_t)((f >> 40) & 0xFF); + out->bit_depth = (uint8_t)((f >> 32) & 0xFF); + out->channels = (uint16_t)((f >> 22) & 0x3FF); + out->samples_per_frame = (uint16_t)((f >> 12) & 0x3FF); + out->sparse = AVB_AAF_PCM_SP_NORMAL; + out->rate = avb_aaf_nsr_to_rate(out->nsr); break; case AVB_SUBTYPE_61883_IIDC: out->kind = AVB_AEM_STREAM_FORMAT_KIND_IEC_61883_6; diff --git a/src/modules/module-avb/stream.c b/src/modules/module-avb/stream.c index d784a9a61..58d4d496f 100644 --- a/src/modules/module-avb/stream.c +++ b/src/modules/module-avb/stream.c @@ -92,8 +92,8 @@ * MEDIA_RESET_IN TODO: tick when AVTPDU header sets the mr bit * (header reset notification) * TIMESTAMP_UNCERTAIN_IN TODO: tick when AVTPDU tu bit is set in the header - * UNSUPPORTED_FORMAT live: handle_aaf_packet drops + ticks any AAF PDU - * whose media format is not the Milan base format + * UNSUPPORTED_FORMAT live: handle_aaf_packet drops + ticks per PDU any AAF PDU + * whose format != the Stream Input current format * LATE_TIMESTAMP TODO: tick when p->timestamp < CLOCK_TAI now * (frame missed its presentation deadline) * EARLY_TIMESTAMP TODO: tick when p->timestamp > now + max_transit_time @@ -1064,17 +1064,31 @@ static void stream_mc_recover(struct stream *stream, const struct avb_packet_aaf * a Listener that joins mid-stream. Small, so genuine ongoing loss still counts. */ #define AVB_STREAM_SEQ_SETTLE 8 -/* Milan v1.2 Section 5.4: the listener supports only the Milan base stream - * format for decode — AAF PCM, 32-bit integer, 48 kHz, non-sparse. The channel - * count is checked separately by handle_aaf_packet against the stream's - * negotiated channel count (a frame from a mismatched talker is rejected). */ -static inline bool aaf_is_milan_format(const struct avb_packet_aaf *p) +/* Milan v1.2 Section 5.4: a received AAF AVTPDU matches the current format when + * subtype, format, nsr, bit depth, channels and sparse all match. */ +static inline bool aaf_pdu_format_matches(const struct avb_packet_aaf *p, + const struct avb_aem_stream_format_info *fi) { - return p->subtype == AVB_SUBTYPE_AAF && - p->format == AVB_AAF_FORMAT_INT_32BIT && - p->nsr == AVB_AAF_PCM_NSR_48KHZ && - p->bit_depth == 32 && - p->sp == AVB_AAF_PCM_SP_NORMAL; + return p->subtype == fi->subtype && + p->format == fi->format && + p->nsr == fi->nsr && + p->bit_depth == fi->bit_depth && + p->chan_per_frame == fi->channels && + p->sp == fi->sparse; +} + +/* Read the current format from the Stream Input descriptor. SET_STREAM_FORMAT + * updates it there, so this is always the current one. */ +static void stream_in_current_format(struct stream *stream, + struct avb_aem_stream_format_info *out) +{ + struct descriptor *desc; + struct avb_aem_desc_stream *body; + + desc = server_find_descriptor(stream->server, AVB_AEM_DESC_STREAM_INPUT, + stream->index); + body = desc ? descriptor_body(desc) : NULL; + avb_aem_stream_format_decode(body ? body->current_format : 0, out); } static void handle_aaf_packet(struct stream *stream, @@ -1082,6 +1096,7 @@ static void handle_aaf_packet(struct stream *stream, { struct aecp_aem_stream_input_state *si = stream_in_state(stream); struct aecp_aem_stream_input_counters *cnt = &si->counters; + struct avb_aem_stream_format_info cur; struct timespec now_ts; uint32_t index, n_bytes; int32_t filled; @@ -1089,15 +1104,10 @@ static void handle_aaf_packet(struct stream *stream, filled = spa_ringbuffer_get_write_index(&stream->ring, &index); n_bytes = ntohs(p->data_len); - /* milan-avb: accept ONLY frames matching this stream's negotiated format. - * EVERY received frame that is not a well-formed Milan AAF PDU — bad length, - * subtype/format/sample-rate/bit-depth/sparse not the Milan base format, or - * whose channel count differs from this stream's negotiated channel count — - * bumps UNSUPPORTED_FORMAT and is dropped, per frame: not counted as a valid - * frame, not media-locked, not written. The channel check rejects frames from - * a different talker/format sharing the VLAN (Milan Section 5.5.1.2). */ - if (n_bytes > (uint32_t)(len - (int)sizeof(*p)) || !aaf_is_milan_format(p) || - p->chan_per_frame != stream->info.info.raw.channels) { + /* IEEE 1722.1-2021 Table 7-156: per-PDU, bump UNSUPPORTED_FORMAT on any AVTPDU + * whose format != the Stream Input current format (from descriptor), or malformed. */ + stream_in_current_format(stream, &cur); + if (n_bytes > (uint32_t)(len - (int)sizeof(*p)) || !aaf_pdu_format_matches(p, &cur)) { cnt->unsupported_format++; stream_in_mark_counters_dirty(stream); return; @@ -1267,6 +1277,8 @@ static void handle_iec61883_packet(struct stream *stream, } } +/* TODO: RX is on the main loop, not the RT data_loop — preemption can drop PDUs + * (SEQ_NUM_MISMATCH). Move it to data_loop + a big SO_RCVBUF, like the flush_timer. */ static void on_socket_data(void *data, int fd, uint32_t mask) { struct stream *stream = data;