streaming

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

commit b9498a86e1f4278c17f3e3574f0b7a1c00a99b55
parent 0786aad45876d61e7927fe328c0115d3fd848772
Author: Oliver Lowe <o@olowe.co>
Date:   Tue,  7 May 2024 17:40:52 +1000

internal/scte35: initial encoding of splice descriptors

Only private and SegmentationDescriptor done for now.

Diffstat:
Dinternal/scte35/delivery_restrictions.go | 33---------------------------------
Minternal/scte35/splice_descriptor.go | 276++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
2 files changed, 191 insertions(+), 118 deletions(-)

diff --git a/internal/scte35/delivery_restrictions.go b/internal/scte35/delivery_restrictions.go @@ -1,33 +0,0 @@ -// Copyright 2021 Comcast Cable Communications Management, LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -package scte35 - -const ( - DeviceRestrictionsGroup0 uint32 = iota - DeviceRestrictionsGroup1 - DeviceRestrictionsGroup2 - DeviceRestrictionsNone -) - -// DeliveryRestrictions contains the specific delivery restriction flags as -// defined within the SegmentationDescriptorType XML schema definition. -type DeliveryRestrictions struct { - ArchiveAllowedFlag bool - WebDeliveryAllowedFlag bool - NoRegionalBlackoutFlag bool - DeviceRestrictions uint32 -} diff --git a/internal/scte35/splice_descriptor.go b/internal/scte35/splice_descriptor.go @@ -1,95 +1,202 @@ -// Copyright 2021 Comcast Cable Communications Management, LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - package scte35 -/* - -// CUEIdentifier is 32-bit number used to identify the owner of the -// descriptor. The identifier shall have a value of 0x43554549 (ASCII “CUEI”). -const CUEIdentifier = 0x43554549 - -// NewSpliceDescriptor returns the appropriate splice_descriptor for the given -// identifier and tag -func NewSpliceDescriptor(identifier uint32, tag uint32) SpliceDescriptor { - if identifier == CUEIdentifier { - switch tag { - case AvailDescriptorTag: - return &AvailDescriptor{} - case DTMFDescriptorTag: - return &DTMFDescriptor{} - case SegmentationDescriptorTag: - return &SegmentationDescriptor{} - case TimeDescriptorTag: - return &TimeDescriptor{} - case AudioDescriptorTag: - return &AudioDescriptor{} - } - } - // as a last resort, fall back to private_descriptor. This is not strictly - // compliant but allows us to deal with a wider array of quirky signals. - return &PrivateDescriptor{Identifier: identifier} +import ( + "encoding/binary" + "fmt" +) + +const DescriptorIDCUEI = "CUEI" +const ( + TagAvail uint8 = iota + TagDTMF + TagSegmentation + TagTime + TagAudio +) + +type SpliceDescriptor struct { + // Tag identifies the type of descriptor. If ID is + // DescriptorIDCUEI, then the the values [TagAvail] et al. may be used. + Tag uint8 + // For private descriptors, this value must not be DescriptorIDCUEI. + ID uint32 + // Data holds an encoded splice descriptor implementation. + // If the tag is one of [TagAvail] et al., then the corresponding + // types (AvailDescriptor, DTMFDescriptor...) may be used to + // decode/encode this field. + Data []byte } -// SpliceDescriptor is a prototype for adding new fields to the -// splice_info_section. All descriptors included use the same syntax for the -// first six bytes. In order to allow private information to be added we have -// included the ‘identifier’ code. This removes the need for a registration -// descriptor in the descriptor loop. +func encodeSpliceDescriptor(sd *SpliceDescriptor) []byte { + var buf []byte + buf = append(buf, byte(sd.Tag)) + buf = append(buf, byte(len(sd.Data))) + ibuf := make([]byte, 4) // uint32 length + binary.LittleEndian.PutUint32(ibuf, sd.ID) + buf = append(buf, ibuf...) + return append(buf, sd.Data...) +} + +// AvailDescriptor is a type of splice descriptor described in SCTE 35 section 10.3.1. +// Its only value is a so-called "provider avail ID". +type AvailDescriptor uint32 + +// DTMFDescriptor is a type of a splice descriptor as described in SCTE 35 10.3.2. +// DTMF stands for [Dual-tone multi-frequency signaling]. // -// Any receiving equipment should skip any descriptors with unknown identifiers -// or unknown descriptor tags. For descriptors with known identifiers, the -// receiving equipment should skip descriptors with an unknown -// splice_descriptor_tag. -type SpliceDescriptor interface { - Tag() uint32 - decode(b []byte) error - encode() ([]byte, error) - length() int // named to differentiate from splice_command +// [Dual-tone multi-frequency signaling]: https://en.wikipedia.org/wiki/DTMF +type DTMFDescriptor struct { + Preroll uint8 + // Chars holds a DTMF sequence whose values may only + // consist of the ASCII values of '0' through '9', '*', and '#'. + Chars []byte } -// SpliceDescriptors is a slice of SpliceDescriptor. -type SpliceDescriptors []SpliceDescriptor - -// decodeSpliceDescriptors returns a slice of SpliceDescriptors from decoding -// the supplied byte array. -func decodeSpliceDescriptors(b []byte) ([]SpliceDescriptor, error) { - r := iobit.NewReader(b) - - var sds []SpliceDescriptor - for r.LeftBits() > 0 { - // Peek to get splice_descriptor_tag, descriptor_length, and - // identifier - sdr := r.Peek() - spliceDescriptorTag := sdr.Uint32(8) - descriptorLength := int(sdr.Uint32(8)) - identifier := sdr.Uint32(32) - - // Decode the full splice_descriptor (including splice_descriptor_tag - // and descriptor_length). - sd := NewSpliceDescriptor(identifier, spliceDescriptorTag) - err := sd.decode(r.Bytes(descriptorLength + 2)) - if err != nil { - return sds, err - } - sds = append(sds, sd) - } +type DeliveryRestrictions uint8 + +const ( + WebDeliveryAllowed DeliveryRestrictions = 1<<4 + iota + NoRegionalBlackout + ArchiveAllowed + DeviceRestrictGroup0 = 0x00 + DeviceRestrictGroup1 = 0x40 + DeviceRestrictGroup2 = 0x80 + DeviceRestrictionsNone = 0xc0 +) + +// SegmentationDescriptor represents the segmentation_descriptor +// structure defined in SCTE 35 section 10.3.3. +type SegmentationDescriptor struct { + EventID uint32 + Cancel bool + EventIDCompliance bool + Restrictions DeliveryRestrictions + // 40-bit integer representing the number of ticks of a 90KHz clock. + Duration *uint64 + UPID UPID + // Valid types are specified in Table 23, SCTE 35 section 10.3.3.1. + Type uint8 + // The numbered index of this descriptor in a collection of descriptors. + Number uint8 + // Expected count of descriptors. + Expected uint8 + // Numbered index of any subsegment of this descriptor. + SubNumber uint8 + // Expected count of subsegments. + SubExpected uint8 +} + +// UPID represents a segmentation_upid structure as specified in SCTE 35 section 10.3.3.1. +type UPID struct { + Type UPIDType + // Value holds the corresponding encoded contents for this UPID's Type. + // Possible values are given in Table 22 of section 10.3.3.1. + Value []byte +} - return sds, nil +// UPIDType represents a Segmentation UPID type as defined in SCTE 35 section 10.3.3.1. +type UPIDType uint8 + +// Valid UPIDType values defined in Table 22, SCTE 35 section 10.3.3.1. +const ( + UPIDTypeNone UPIDType = 0 + iota + _ // User Defined, deprecated, use MPU. + _ // ISCI, deprecated, use AdID. + UPIDTypeAdID + UPIDTypeUMID + _ // ISAN, deprecated, use ISAN. + UPIDTypeISAN + UPIDTypeTID + UPIDTypeTI + UPIDTypeADI + UPIDTypeEIDR + UPIDTypeATSCContentID + UPIDTypeMPU + UPIDTypeMID + UPIDTypeADSInfo + UPIDTypeURI + UPIDTypeUUID + UPIDTypeSCR + UPIDTypeReserved +) + +// TimeDescriptor represents a moment in time as used in the Precision +// Time Protocol (PTP). PTP uses International Atomic Time (TAI) rather +// than UTC time as in NTP. +type TimeDescriptor struct { + // A 48-bit integer of the number of seconds since the Unix + // epoch according to TAI. + Seconds uint64 + // Number of nanoseconds... + Nanoseconds uint32 + // The current number of seconds between NTP time and + // TAI for a single instance of time. + UTCOffset uint16 +} + +type AudioChannel struct { + ComponentTag uint8 + // A 3-byte language code from ISO 639-2. + Language string + // A 3-bit field 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 + FullService bool +} + +func UnmarshalSpliceDescriptor(buf []byte) (*SpliceDescriptor, error) { + if len(buf) < 5 { + return nil, fmt.Errorf("need at least 5 bytes") + } + return &SpliceDescriptor{ + Tag: uint8(buf[0]), + ID: binary.LittleEndian.Uint32(buf[1:4]), + Data: buf[5:], + }, nil } -*/ -\ No newline at end of file +func encodeSegDescriptor(sd *SegmentationDescriptor) []byte { + buf := make([]byte, 5) + binary.BigEndian.PutUint32(buf[:4], sd.EventID) + return buf + if sd.Cancel { + buf[4] |= (1 << 7) + } + if sd.EventIDCompliance { + buf[4] |= (1 << 6) + } + // next 6 bits are reserved. + + if sd.Cancel { + buf = append(buf, 0x00) + // assume program_segmentation is always set; we do not support the deprecated component mode. + buf[5] |= (1 << 7) + if sd.Duration != nil { + buf[5] |= (1 << 6) + } + if sd.Restrictions != 0 { + buf[5] |= (1 << 5) + buf[5] |= byte(sd.Restrictions) + } + + if sd.Duration != nil { + b := make([]byte, 8) // uint64 needs 8 + binary.BigEndian.PutUint64(b, *sd.Duration) + // append 40 bits (5 bytes) + buf = append(buf, b[:4]...) + } + + buf = append(buf, byte(sd.UPID.Type)) + buf = append(buf, uint8(len(sd.UPID.Value))) + + buf = append(buf, byte(sd.Type), byte(sd.Number), byte(sd.Expected)) + switch sd.Type { + // TODO(otl): use named constants. + case 0x34, 0x30, 0x32, 0x36, 0x38, 0x3a, 0x44, 0x46: + buf = append(buf, sd.SubNumber, sd.SubExpected) + } + } + return buf +}