streaming

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

commit 259fb85f2b3254aa6e5683141dde31026b211372
parent bf20623811837382195b3c2fb8dca039caf0478a
Author: Oliver Lowe <o@olowe.co>
Date:   Tue, 14 May 2024 13:54:41 +1000

internal/scte35: add tests from outside the SCTE 35 spec.

Sources as noted in comments.

Diffstat:
Minternal/scte35/scte35_test.go | 42++++++++++++++++++++++++++++++++++++++++++
Minternal/scte35/splice_info.go | 2+-
Minternal/scte35/splice_info_test.go | 63++++++++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 93 insertions(+), 14 deletions(-)

diff --git a/internal/scte35/scte35_test.go b/internal/scte35/scte35_test.go @@ -125,6 +125,48 @@ var samples = []sample{ CRC32: 0x9972e343, }, }, + { + // from "The Essential Guide to SCTE-35" by Bitmovin (https://bitmovin.com/scte-35-guide) + name: "dtmf", + encoded: "/DBcAAAAAAAAAP/wBQb//ciI8QBGAh1DVUVJXQk9EX+fAQ5FUDAxODAzODQwMDY2NiEEZAIZQ1VFSV0JPRF/3wABLit7AQVDMTQ2NDABAQEKQ1VFSQCAMTUwKnPhdcU=", + want: SpliceInfo{ + SAPType: SAPNone, + Tier: 0x0fff, + Command: &Command{ + Type: TimeSignal, + TimeSignal: newuint64(8552745201), + }, + Descriptors: []SpliceDescriptor{ + SegmentationDescriptor{ + EventID: 1560886545, + EventIDCompliance: true, + Restrictions: WebDeliveryAllowed | NoRegionalBlackout | DeviceRestrictionsNone, + UPID: UPID{ + Type: UPIDType(1), + Value: []byte{69, 80, 48, 49, 56, 48, 51, 56, 52, 48, 48, 54, 54, 54}, + }, + Type: 33, + Number: 4, + Expected: 100, + }, + SegmentationDescriptor{ + EventID: 1560886545, + EventIDCompliance: true, + Restrictions: WebDeliveryAllowed | NoRegionalBlackout | DeviceRestrictionsNone, + Duration: newuint64(19803003), + UPID: UPID{ + Type: UPIDType(1), + Value: []byte{67, 49, 52, 54, 52}, + }, + Type: 48, + Number: 1, + Expected: 1, + }, + DTMFDescriptor{Chars: []byte("150*")}, + }, + CRC32: 1944155589, + }, + }, } func newuint64(i uint64) *uint64 { p := new(uint64); p = &i; return p } diff --git a/internal/scte35/splice_info.go b/internal/scte35/splice_info.go @@ -82,7 +82,7 @@ func encodeSpliceInfo(sis *SpliceInfo) ([]byte, error) { if sis.Encrypted { buf = append(buf, sis.CWIndex) } else { - // unused; must toggle all bits. + // unused; toggle all bits as in the spec. buf = append(buf, 0xff) } diff --git a/internal/scte35/splice_info_test.go b/internal/scte35/splice_info_test.go @@ -6,12 +6,14 @@ import ( "reflect" "strings" "testing" + "time" ) func diffInfo(a, b SpliceInfo) string { buf := &strings.Builder{} if a.SAPType != b.SAPType { - fmt.Fprintf(buf, "SAP type = %s, %s\n", a.SAPType, b.SAPType) + fmt.Fprintln(buf, "SAP type differs") + fmt.Fprintf(buf, "< %s\n> %s\n", a.SAPType, b.SAPType) } if a.Cipher != b.Cipher { fmt.Fprintln(buf, "cipher differs") @@ -31,16 +33,9 @@ func diffInfo(a, b SpliceInfo) string { if !reflect.DeepEqual(a.Command, b.Command) { fmt.Fprintln(buf, "command = ", *a.Command, *b.Command) } - if !reflect.DeepEqual(a.Descriptors, b.Descriptors) { - if len(a.Descriptors) != len(b.Descriptors) { - fmt.Fprintf(buf, "descriptor count = %d, %d\n", len(a.Descriptors), len(b.Descriptors)) - fmt.Fprintf(buf, "descriptors = %+v, %+v\n", a.Descriptors, b.Descriptors) - } else { - fmt.Fprintln(buf, "descriptors differ") - for i := range a.Descriptors { - fmt.Fprintln(buf, "descriptor", i) - buf.WriteString(diffDescriptors(a.Descriptors[i], b.Descriptors[i])) - } + for i := range a.Descriptors { + if !reflect.DeepEqual(a.Descriptors[i], b.Descriptors[i]) { + buf.WriteString(diffDescriptors(a.Descriptors[i], b.Descriptors[i])) } } if a.CRC32 != b.CRC32 { @@ -106,6 +101,35 @@ func TestDecodeSpliceInfo(t *testing.T) { } }) } + + // these messages are from github.com/futzu/SCTE-35_threefive/examples/hls/ + inserts := map[string]time.Duration{ + "/DAlAAAAAAAAAP/wFAUAAAABf+/+ANgNkv4AFJlwAAEBAQAA5xULLA==": 15 * time.Second, + "/DAnAAAAAAAAAP/wBQb+AA27oAARAg9DVUVJAAAAAX+HCQA0AAE0xUZn": 10 * time.Second, + "/DAnAAAAAAAAAP/wBQb+AGb/MAARAg9DVUVJAAAAAn+HCQA0AALMua1L": 75 * time.Second, + } + for s, dur := range inserts { + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + t.Fatal(err) + } + splice, err := decodeSpliceInfo(b) + if err != nil { + t.Fatalf("decode splice info: %v", err) + } + var got time.Duration + switch splice.Command.Type { + case TimeSignal: + got = time.Duration(*splice.Command.TimeSignal/90000) * time.Second + case SpliceInsert: + got = time.Duration(splice.Command.Insert.Duration.Duration/90000) * time.Second + default: + t.Fatalf("no duration test supported for %s", splice.Command.Type) + } + if got != dur { + t.Errorf("want %s, got %s", dur, got) + } + } } func TestEncodeSpliceInfo(t *testing.T) { @@ -115,13 +139,26 @@ func TestEncodeSpliceInfo(t *testing.T) { if err != nil { t.Fatal(err) } - got := base64.StdEncoding.EncodeToString(b) bwant, err := base64.StdEncoding.DecodeString(tt.encoded) if err != nil { t.Fatal(err) } + // If we're not encrypted, set the CWIndex to be + // the same as desired; its value is now undefined + // and should be ignored downstream. This lets our + // test pass even if our test encoded value has a + // different CWIndex set than what we encode. + if !tt.want.Encrypted { + b[9] = bwant[9] + } + got := base64.StdEncoding.EncodeToString(b) if tt.encoded != got { - t.Errorf("expected encoded splice info differs from calculated") + // as above, since the undefined CWIndex is encoded differently, + // our checksum could be different. + // Only error if the value of the message *without* the CRC32 is different. + if tt.encoded[:len(tt.encoded)-7] != got[:len(got)-7] { + t.Errorf("expected encoded splice info differs from calculated") + } t.Logf("< %#x", bwant) t.Logf("> %#x", b) }