splice.go (8257B)
1 // Package scte35 implements a subset of the 2 // Digital Program Insertion Cueing Message standard 3 // as specified in [ANSI/SCTE 35]. 4 // 5 // [ANSI/SCTE 35]: https://www.scte.org/standards/library/catalog/scte-35-digital-program-insertion-cueing-message/ 6 package scte35 7 8 import ( 9 "encoding/binary" 10 "fmt" 11 ) 12 13 // SAPType represents the two-bit field used to indicate that a Stream 14 // Access Point (SAP) in the stream 15 // as specified in SCTE 35 section 9.6.1. 16 type SAPType uint8 17 18 const ( 19 SAPClosedGOP SAPType = 0 20 SAPClosedGOPLeading = 0x10 21 SAPOpenGOP = 0x20 22 SAPNone = 0x30 23 ) 24 25 func (t SAPType) String() string { 26 switch t { 27 case SAPClosedGOP: 28 return "SAP Type 1 (closed GOP)" 29 case SAPClosedGOPLeading: 30 return "SAP Type 2 (closed GOP with leading pictures)" 31 case SAPOpenGOP: 32 return "SAP Type 3 (open GOP)" 33 case SAPNone: 34 return "none" 35 } 36 return "invalid" 37 } 38 39 type Splice struct { 40 SAPType SAPType 41 42 // If true, indicates that the contents of Command, 43 // Descriptors and CRC32 are encrypted with Cipher. 44 // TODO(otl): encoding and decoding of encrypted splices is not supported. 45 Encrypted bool 46 Cipher Cipher 47 // The control word (key) used to decrypt the message. 48 CWIndex uint8 49 50 // Holds a 33-bit unsigned integer representing the number of 51 // ticks of a 90KHz clock. The value is an offset added to 52 // timestamps in Descriptors by splice devices when executing the 53 // provided Command. 54 PTSAdjustment uint64 55 56 // Holds a 12-bit field representing an authorization tier. In 57 // most cases, its value should be 0x0fff for backwards 58 // compatibility. See 'tier' in SCTE 35 section 9.6.1. 59 Tier uint16 60 61 // Command points to this splice's specific instruction for splice devices. 62 Command *Command 63 // Descriptors holds zero or more parameters to Command. 64 Descriptors []SpliceDescriptor 65 66 // A checksum of the encoded splice. Splices returned from 67 // Decode() will hold a non-zero value. Splices passed to 68 // Encode() will have their checksums calculated automatically. 69 CRC32 uint32 70 } 71 72 // fields of Splice Info Section which MUST have their values set... 73 // as specified in SCTE 35 section 9.6.1. 74 const ( 75 tableID uint8 = 0xfc 76 protocolVersion = 0x0 77 sectionSyntax = false 78 privateIndicator = false 79 ) 80 81 // maximum 12-bit uint (2^12 - 1) 82 const maxTier uint16 = 0xfff 83 84 func Encode(splice *Splice) ([]byte, error) { 85 buf := make([]byte, 4) 86 buf[0] = byte(tableID) 87 // next 2 bits (section_syntax_indicator, private_indicator) must be 0. 88 // 0b00000000 89 buf[1] |= byte(splice.SAPType) 90 91 // length, buf[1,2] set at the end 92 buf[3] = protocolVersion 93 94 var b byte 95 if splice.Encrypted { 96 b |= (1 << 7) 97 if splice.Cipher > maxCipher { 98 return nil, fmt.Errorf("cipher %d larger than max %d", splice.Cipher, maxCipher) 99 } 100 // pack cipher, keeping 1 bit for PTSAdjustment. 101 b |= byte(splice.Cipher) << 1 102 } 103 buf = append(buf, b) 104 buf = append(buf, 0, 0, 0, 0) 105 putPTS(buf[4:], splice.PTSAdjustment) 106 if splice.Encrypted { 107 buf = append(buf, splice.CWIndex) 108 } else { 109 // unused; toggle all bits as in the spec. 110 buf = append(buf, 0xff) 111 } 112 113 if splice.Tier > maxTier { 114 return nil, fmt.Errorf("tier %d greater than max %d", splice.Tier, maxTier) 115 } 116 tier := splice.Tier & 0x0fff // just 12 bits 117 // right 4 bits are for command length 118 buf = binary.BigEndian.AppendUint16(buf, tier<<4) 119 120 if splice.Command == nil { 121 return nil, fmt.Errorf("nil command") 122 } 123 cmd, err := encodeCommand(splice.Command) 124 if err != nil { 125 return nil, fmt.Errorf("encode splice command: %w", err) 126 } 127 cmdlen := uint16(len(cmd)) & 0x0fff 128 // stuff remaining 4 bits into the last byte. 129 buf[len(buf)-1] |= byte(cmdlen >> 8) 130 buf = append(buf, byte(cmdlen)) 131 buf = append(buf, byte(splice.Command.Type)) 132 buf = append(buf, cmd...) 133 134 var buf1 []byte 135 for _, desc := range splice.Descriptors { 136 buf1 = append(buf1, encodeSpliceDescriptor(desc)...) 137 } 138 buf = binary.BigEndian.AppendUint16(buf, uint16(len(buf1))) 139 buf = append(buf, buf1...) 140 141 // want only 12 bits, left 4 bits are used by flags, saptype. 142 buflen := uint16(len(buf)) & 0x0fff 143 buflen++ // TODO(otl): is this required because of alignment stuffing? 144 buf[1] |= byte(buflen >> 8) 145 buf[2] = byte(buflen) 146 147 crc := ^updateCRC(0, buf) 148 return binary.BigEndian.AppendUint32(buf, crc), nil 149 } 150 151 func Decode(buf []byte) (*Splice, error) { 152 if len(buf) < 3 { 153 return nil, fmt.Errorf("need at least 2 bytes") 154 } 155 // skip buf[0], we don't store table_id. 156 157 var splice Splice 158 // skip 2 bits, straight to sap_type. 159 splice.SAPType = SAPType(buf[1] & 0b00110000) 160 length := binary.BigEndian.Uint16([]byte{buf[1], buf[2]}) 161 length &= 0x0fff // 12-bit field 162 buf = buf[3:] 163 if len(buf) != int(length) { 164 return nil, fmt.Errorf("message declares %d bytes but have %d", length, len(buf)) 165 } 166 167 // skip version byte at buf[0]. We don't store version as it's constant. 168 splice.Encrypted = buf[1]&0b10000000 > 0 169 if splice.Encrypted { 170 // right-most bit is used by PTSAdjustment. 171 splice.Cipher = Cipher(buf[1] & 0b01111110) 172 } 173 174 pts := make([]byte, 8) 175 pts[0] = buf[1] & (1 << 1) 176 copy(pts[1:], buf[2:6]) 177 splice.PTSAdjustment = binary.BigEndian.Uint64(pts) 178 splice.CWIndex = uint8(buf[6]) 179 180 // want left-most 12 bits, remaining is used by command length. 181 tier := binary.BigEndian.Uint16([]byte{buf[7], buf[8] & 0xf0}) 182 splice.Tier = tier >> 4 183 184 // 4-bits out of buf[8], then all of buf[9] for a 12-bit integer. 185 cmdlen := binary.BigEndian.Uint16([]byte{buf[8] & 0x0f, buf[9]}) 186 cmd, err := decodeCommand(buf[10 : 10+cmdlen+1]) 187 if err != nil { 188 return nil, fmt.Errorf("decode command: %w", err) 189 } 190 splice.Command = cmd 191 buf = buf[10+cmdlen+1:] 192 193 desclen := binary.BigEndian.Uint16([]byte{buf[0], buf[1]}) 194 descriptors, err := decodeAllDescriptors(buf[2 : 2+desclen]) 195 if err != nil { 196 return nil, fmt.Errorf("decode splice descriptors: %w", err) 197 } 198 splice.Descriptors = descriptors 199 200 buf = buf[2+desclen:] 201 if splice.Encrypted { 202 // TODO(otl): handle alignment_stuffing for encrypted packets. 203 // skip past E_CRC_32; we don't store it. 204 buf = buf[1:] 205 } 206 splice.CRC32 = binary.BigEndian.Uint32(buf) 207 return &splice, nil 208 } 209 210 func decodeCommand(buf []byte) (*Command, error) { 211 var cmd Command 212 cmd.Type = CommandType(buf[0]) 213 switch cmd.Type { 214 case SpliceNull, BandwidthReservation: 215 // nothing to decode 216 case TimeSignal: 217 // check if time specified flag is set. 218 // If so, extract the 33-bit integer timestamp. 219 if buf[1]&0x80 == 1<<7 { 220 b := make([]byte, 8) 221 b[3] = buf[1] & 0x01 // ignoring flag and reserved bits 222 copy(b[4:], buf[2:6]) 223 t := binary.BigEndian.Uint64(b) 224 cmd.TimeSignal = &t 225 } 226 case SpliceInsert: 227 var ins Insert 228 ins.ID = binary.BigEndian.Uint32(buf[1:5]) 229 ins.Cancel = buf[5]&0x80 > 0 230 if ins.Cancel { 231 cmd.Insert = &ins 232 // rebelelder told us to do this. 233 return &cmd, nil 234 } 235 ins.OutOfNetwork = buf[6]&(1<<7) > 0 236 // assume program_splice is set at bit 6; 237 // we don't support deprecated component mode. 238 durflag := buf[6]&(1<<5) > 0 239 ins.Immediate = buf[6]&(1<<4) > 0 240 ins.idCompliance = buf[6]&(1<<3) > 0 241 // next 3 bits are reserved. 242 243 if !ins.Immediate { 244 // is time_specified_flag set? if so, read the 33-bit time. 245 if buf[7]&(1<<7) > 0 { 246 b := make([]byte, 3) 247 b = append(b, buf[7]&0x01) // skip reserved bits. 248 b = append(b, buf[8:12]...) // read remaining 32 bits. 249 dur := binary.BigEndian.Uint64(b) 250 ins.SpliceTime = newuint64(dur) 251 buf = buf[12:] 252 } else { 253 buf = buf[8:] 254 } 255 } 256 257 if durflag { 258 a := [5]byte{buf[0], buf[1], buf[2], buf[3], buf[4]} 259 ins.Duration = readBreakDuration(a) 260 buf = buf[5:] 261 } 262 263 ins.ProgramID = binary.BigEndian.Uint16([]byte{buf[0], buf[1]}) 264 ins.AvailNum = uint8(buf[2]) 265 ins.AvailExpected = uint8(buf[3]) 266 cmd.Insert = &ins 267 case Private: 268 pcmd, err := decodePrivateCommand(buf[1:]) 269 if err != nil { 270 return nil, fmt.Errorf("decode private command: %w", err) 271 } 272 cmd.Private = &pcmd 273 default: 274 // TODO(otl): we could support more commands but we 275 // just haven't written the code yet. See issues 276 // #28 and #29. 277 return nil, fmt.Errorf("cannot decode command type %s", cmd.Type) 278 } 279 return &cmd, nil 280 } 281 282 func newuint64(i uint64) *uint64 { p := new(uint64); p = &i; return p }