commit 440f52398d25a5cddd80cefad5ba8631b5e7eaf7 from: Oliver Lowe date: Sat Jun 14 04:52:48 2025 UTC scte35: correctly pack AudioChannel Couple of problems. First we were unnecessarily masking bits. Secondly we were toggling the wrong bits in the final byte of an encoded audio channel in an AudioDescriptor. Resolves: https://github.com/untangledco/streaming/issues/39 commit - 62d865db8655e6d23b4fccc3280d73c3cdda8ce9 commit + 440f52398d25a5cddd80cefad5ba8631b5e7eaf7 blob - 5512a4cdb4a92941f104cc8e6744c9c2c656ce26 blob + 31da370b55aec9f363a841a38a0c7289fc9f4231 --- scte35/splice.go +++ scte35/splice.go @@ -116,6 +116,7 @@ func Encode(splice *Splice) ([]byte, error) { tier := splice.Tier & 0x0fff // just 12 bits // right 4 bits are for command length buf = binary.BigEndian.AppendUint16(buf, tier<<4) + if splice.Command == nil { return nil, fmt.Errorf("nil command") } blob - 6a360bdd85a88ac4a54558990481a227ade66c3c blob + 3626238f69586624ccd14ada2146e32302314a8d --- scte35/splice_descriptor.go +++ scte35/splice_descriptor.go @@ -296,11 +296,24 @@ type AudioChannel struct { Language [3]byte // A 3-bit integer from ATSC A/52 Table 5.7. BitstreamMode uint8 - // Number of channels as a 4-bit integer, from ATSC A/52 Table A4.5. - Count uint8 + // Number of channels as a 4 bit field, from ATSC A/52 Table A4.5. + Count NumChannels FullService bool } +type NumChannels uint8 + +const ( + OneChan NumChannels = 0b10000000 + (iota << 4) + TwoChan + ThreeChan + FourChan + FiveChan + SixChan + _ // Reserved + _ // Reserved +) + type AudioDescriptor []AudioChannel func (d AudioDescriptor) Tag() uint8 { return TagAudio } @@ -314,10 +327,10 @@ func (d AudioDescriptor) Data() []byte { b = append(b, ch.ComponentTag) b = append(b, ch.Language[:]...) var c byte - c |= (ch.BitstreamMode << 5) // set left 3 bits - c |= (ch.Count & 0x0f) // only want 4 bits + c |= (ch.BitstreamMode << 5) // set bits 0-2 + c |= byte(ch.Count) >> 3 // set bits 3-7 if ch.FullService { - c |= 0x01 // set last remaining bit + c |= 0x01 // set last bit } b = append(b, c) } blob - d875627cb08f6e0a7684ba84231564bfee9566f3 blob + c839ea9ff3a0b7ec1e16590c23631809266594d3 --- scte35/splice_test.go +++ scte35/splice_test.go @@ -165,3 +165,34 @@ func TestEncode(t *testing.T) { }) } } + +const bitstreamModeKaraoke uint8 = 0b00000111 + +func TestAudioDescriptor(t *testing.T) { + desc := []AudioChannel{ + { + ComponentTag: 0xff, // should be 0xff if unused. + Language: [3]byte{'e', 'n', 'g'}, // English + BitstreamMode: bitstreamModeKaraoke, + Count: SixChan, + FullService: true, + }, + } + + want := [6]byte{ + 0b00010000, // we have a single channel (above), shifted left 4. reserved bits untoggled. + 0xff, // ComponentTag + 'e', 'n', 'g', // Language + // bit layout is + // mode mode mode, count count count count, fullservice + 0b11111011, // Mode = karaoke, Count = SixChan, FullService = true + } + + var got [6]byte + marshalled := AudioDescriptor(desc).Data() + copy(got[:], marshalled) + if got != want { + t.Logf("final byte = %#08b, want %#08b", got[5], want[5]) + t.Fatalf("Data() = %#08bb, want %#08b", got, want) + } +}