streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

commit 440f52398d25a5cddd80cefad5ba8631b5e7eaf7
parent 62d865db8655e6d23b4fccc3280d73c3cdda8ce9
Author: Oliver Lowe <o@olowe.co>
Date:   Sat, 14 Jun 2025 14:52:48 +1000

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

Diffstat:
Mscte35/splice.go | 1+
Mscte35/splice_descriptor.go | 23++++++++++++++++++-----
Mscte35/splice_test.go | 31+++++++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/scte35/splice.go b/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") } diff --git a/scte35/splice_descriptor.go b/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) } diff --git a/scte35/splice_test.go b/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) + } +}