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:
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)
}