-
Notifications
You must be signed in to change notification settings - Fork 15
/
splice_info_section.go
455 lines (408 loc) · 15.1 KB
/
splice_info_section.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
// 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
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"time"
"github.com/bamiaux/iobit"
)
const (
// TableID is an 8-bit field that shall be 0xFC.
TableID = 0xFC
// SectionSyntaxIndicator is a 1-bit field that should always be set to ‘0’.
SectionSyntaxIndicator = false
// PrivateIndicator is a 1-bit flag that shall be set to 0.
PrivateIndicator = false
// SAPType1 indicates closed GOP with no leading pictures.
SAPType1 = uint32(0x0)
// SAPType2 indicates closed GOP with leading pictures.
SAPType2 = uint32(0x1)
// SAPType3 indicates Open GOP.
SAPType3 = uint32(0x2)
// SAPTypeNotSpecified indicates the type of SAP, if any, is not signaled.
SAPTypeNotSpecified = uint32(0x3)
)
// SpliceInfoSection shall be carried in transport packets whereby only one
// section or partial section may be in any transport packet.
// Splice_info_sections shall always start at the beginning of a transport
// packet payload. When a section begins in a transport packet and this is the
// first packet of the splice_info_section, the pointer_field shall be present
// and equal to 0x00 and the payload_unit_start_indicator bit shall be equal to
// one (per the requirements of section syntax usage per [MPEG Systems]).
type SpliceInfoSection struct {
XMLName xml.Name `xml:"http://www.scte.org/schemas/35 SpliceInfoSection"`
EncryptedPacket EncryptedPacket `xml:"http://www.scte.org/schemas/35 EncryptedPacket,omitempty"`
SpliceCommand SpliceCommand `xml:""`
SpliceDescriptors SpliceDescriptors `xml:""`
SAPType uint32 `xml:"sapType,attr"`
PTSAdjustment uint64 `xml:"ptsAdjustment,attr"`
ProtocolVersion uint32 `xml:"protocolVersion,attr"`
Tier uint32 `xml:"tier,attr"`
alignmentStuffing []byte // alignment_stuffing
ecrc32 []byte // decoded e_crc_32
crc32 []byte // decoded crc_32
}
// Base64 returns the SpliceInfoSection as a base64 encoded string.
func (sis *SpliceInfoSection) Base64() string {
b, err := sis.Encode()
if err != nil {
Logger.Printf("Error encoding splice_info_section: %s\n", err)
return ""
}
return base64.StdEncoding.EncodeToString(b)
}
// Decode the contents of a byte array into this SpliceInfoSection.
func (sis *SpliceInfoSection) Decode(b []byte) (err error) {
r := iobit.NewReader(b)
r.Skip(8) // table_id (shall be 0xFC)
r.Skip(1) // section_syntax_indicator (shall be 0)
r.Skip(1) // private_indicator (shall be 0)
sis.SAPType = r.Uint32(2)
r.Skip(12) // section_length (informative, can ignore)
sis.ProtocolVersion = r.Uint32(8)
encryptedPacket := r.Bit()
sis.EncryptedPacket.EncryptionAlgorithm = r.Uint32(6)
sis.PTSAdjustment = r.Uint64(33)
sis.EncryptedPacket.CWIndex = r.Uint32(8)
sis.Tier = r.Uint32(12)
spliceCommandLength := int(r.Uint32(12)) // in bytes
spliceCommandType := r.Uint32(8)
switch spliceCommandLength {
case 0xFFF:
// legacy signal, decode and skip (buffer underflow expected here)
r2 := r.Peek()
sis.SpliceCommand, err = decodeSpliceCommand(spliceCommandType, r2.LeftBytes())
if err != nil && !errors.Is(err, ErrBufferUnderflow) {
return err
}
r.Skip(uint(sis.SpliceCommand.length() * 8))
default:
// standard signal, decode as usual
sis.SpliceCommand, err = decodeSpliceCommand(spliceCommandType, r.Bytes(spliceCommandLength))
if err != nil {
return err
}
}
descriptorLoopLength := int(r.Uint32(16)) // bytes
sis.SpliceDescriptors, err = decodeSpliceDescriptors(r.Bytes(descriptorLoopLength))
if err != nil {
return err
}
if encryptedPacket {
stuffedBytes := (int(r.LeftBits()) - 64) / 8
if stuffedBytes > 0 {
sis.alignmentStuffing = r.Bytes(stuffedBytes)
}
sis.ecrc32 = r.Bytes(4)
} else {
stuffedBytes := (int(r.LeftBits()) - 32) / 8
if stuffedBytes > 0 {
sis.alignmentStuffing = r.Bytes(stuffedBytes)
}
}
sis.crc32 = r.Bytes(4)
if err := readerError(r); err != nil {
return fmt.Errorf("splice_info_section: %w", err)
}
if err = verifyCRC32(b); err != nil {
return fmt.Errorf("splice_info_section: %w", err)
}
return nil
}
// Duration attempts to return the duration of the signal.
func (sis *SpliceInfoSection) Duration() time.Duration {
// if this is a splice insert with a duration, use it
switch sc := sis.SpliceCommand.(type) {
case *SpliceInsert:
if sc.BreakDuration != nil {
return TicksToDuration(sc.BreakDuration.Duration)
}
}
ticks := uint64(0)
for _, sd := range sis.SpliceDescriptors {
switch sdt := sd.(type) {
case *SegmentationDescriptor:
if sdt.SegmentationDuration != nil {
ticks += *sdt.SegmentationDuration
}
}
}
return TicksToDuration(ticks)
}
// Encode returns the binary representation of this SpliceInfoSection as a
// byte array.
func (sis *SpliceInfoSection) Encode() ([]byte, error) {
buf := make([]byte, sis.length())
iow := iobit.NewWriter(buf)
iow.PutUint32(8, TableID)
iow.PutBit(SectionSyntaxIndicator)
iow.PutBit(PrivateIndicator)
iow.PutUint32(2, sis.SAPType)
iow.PutUint32(12, uint32(sis.sectionLength()))
iow.PutUint32(8, sis.ProtocolVersion)
iow.PutBit(sis.EncryptedPacketFlag())
iow.PutUint32(6, sis.EncryptedPacket.EncryptionAlgorithm)
iow.PutUint64(33, sis.PTSAdjustment)
iow.PutUint32(8, sis.EncryptedPacket.CWIndex)
iow.PutUint32(12, sis.Tier)
if sis.SpliceCommand != nil {
iow.PutUint32(12, uint32(sis.SpliceCommand.length()))
iow.PutUint32(8, sis.SpliceCommand.Type())
sc, err := sis.SpliceCommand.encode()
if err != nil {
return buf, err
}
if _, err = iow.Write(sc); err != nil {
return buf, err
}
}
iow.PutUint32(16, uint32(sis.descriptorLoopLength()))
for _, sd := range sis.SpliceDescriptors {
sde, err := sd.encode()
if err != nil {
return buf, err
}
if _, err = iow.Write(sde); err != nil {
return buf, err
}
}
// alignment_stuffing
_, _ = iow.Write(sis.alignmentStuffing)
// Encoding encrypted signals is untested.
if sis.EncryptedPacket.EncryptionAlgorithm != EncryptionAlgorithmNone {
iow.PutUint32(32, calculateCRC32(buf[:iow.Index()/8])) // E_CRC_32
}
// Re-calculate CRC_32 to ensure correctness
iow.PutUint32(32, calculateCRC32(buf[:iow.Index()/8])) // CRC_32
err := iow.Flush()
return buf, err
}
// EncryptedPacketFlag returns the value of encrypted_packet_flag
func (sis *SpliceInfoSection) EncryptedPacketFlag() bool {
return sis.EncryptedPacket.EncryptionAlgorithm != EncryptionAlgorithmNone
}
// Hex returns the SpliceInfoSection as a hexadecimal encoded string.
func (sis *SpliceInfoSection) Hex() string {
b, err := sis.Encode()
if err != nil {
Logger.Printf("Error encoding splice_info_section: %s\n", err)
return ""
}
return hex.EncodeToString(b)
}
// SAPTypeName returns the Stream Access Point type name.
func (sis *SpliceInfoSection) SAPTypeName() string {
switch sis.SAPType {
case SAPType1:
return "Type 1"
case SAPType2:
return "Type 2"
case SAPType3:
return "Type 3"
default:
return "Not Specified"
}
}
// Table returns the tabular description of this SpliceInfoSection as described
// in ANSI/SCTE 35 Table 5.
func (sis *SpliceInfoSection) Table(prefix, indent string) string {
if sis == nil {
return ""
}
var b bytes.Buffer
_, _ = fmt.Fprintf(&b, prefix+"splice_info_section() {\n")
_, _ = fmt.Fprintf(&b, prefix+indent+"table_id: %#02x\n", TableID)
_, _ = fmt.Fprintf(&b, prefix+indent+"section_syntax_indicator: %v\n", SectionSyntaxIndicator)
_, _ = fmt.Fprintf(&b, prefix+indent+"private_indicator: %v\n", PrivateIndicator)
_, _ = fmt.Fprintf(&b, prefix+indent+"sap_type: %s\n", sis.SAPTypeName())
_, _ = fmt.Fprintf(&b, prefix+indent+"section_length: %d bytes\n", sis.sectionLength())
_, _ = fmt.Fprintf(&b, prefix+"}\n")
_, _ = fmt.Fprintf(&b, prefix+"protocol_version: %d\n", sis.ProtocolVersion)
_, _ = fmt.Fprintf(&b, prefix+"encryption_algorithm: %s\n", sis.EncryptedPacket.encryptionAlgorithmName())
_, _ = fmt.Fprintf(&b, prefix+"pts_adjustment: %d ticks (%s)\n", sis.PTSAdjustment, TicksToDuration(sis.PTSAdjustment))
_, _ = fmt.Fprintf(&b, prefix+"cw_index: %d\n", sis.EncryptedPacket.CWIndex)
_, _ = fmt.Fprintf(&b, prefix+"tier: %d\n", sis.Tier)
if sis.SpliceCommand != nil {
_, _ = fmt.Fprintf(&b, prefix+"splice_command_length: %d bytes\n", sis.SpliceCommand.length())
_, _ = fmt.Fprintf(&b, prefix+"splice_command_type: %#02x\n", sis.SpliceCommand.Type())
_, _ = fmt.Fprintf(&b, sis.SpliceCommand.table(prefix, indent))
}
_, _ = fmt.Fprintf(&b, prefix+"descriptor_loop_length: %d bytes\n", sis.descriptorLoopLength())
for _, sd := range sis.SpliceDescriptors {
_, _ = fmt.Fprintf(&b, sd.table(prefix, indent))
}
return b.String()
}
// MarshalJSON encodes a SpliceInfoSection to JSON.
func (sis *SpliceInfoSection) MarshalJSON() ([]byte, error) {
// ensure JSONTypes are all set before marshalling. These are included in
// each SpliceCommand.Type() and SpliceDescriptor.Tag() implementation.
sis.SpliceCommand.Type()
for i := range sis.SpliceDescriptors {
sis.SpliceDescriptors[i].Tag()
}
m := map[string]interface{}{
"encryptedPacket": sis.EncryptedPacket,
"spliceCommand": sis.SpliceCommand,
"sapType": sis.SAPType,
"ptsAdjustment": sis.PTSAdjustment,
"protocolVersion": sis.ProtocolVersion,
"tier": sis.Tier,
}
if len(sis.SpliceDescriptors) > 0 {
m["spliceDescriptors"] = sis.SpliceDescriptors
}
return json.Marshal(m)
}
// UnmarshalJSON decodes a SpliceInfoSection from JSON.
func (sis *SpliceInfoSection) UnmarshalJSON(b []byte) error {
var tmp iSIS
if err := json.Unmarshal(b, &tmp); err != nil {
return err
}
sis.EncryptedPacket = tmp.EncryptedPacket
sis.SpliceCommand = tmp.SpliceCommand()
sis.SpliceDescriptors = tmp.SpliceDescriptors
sis.SAPType = tmp.SAPType
sis.PTSAdjustment = tmp.PTSAdjustment
sis.ProtocolVersion = tmp.ProtocolVersion
sis.Tier = tmp.Tier
return nil
}
// UnmarshalXML decodes a SpliceInfoSection from XML.
func (sis *SpliceInfoSection) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var tmp iSIS
if err := d.DecodeElement(&tmp, &start); err != nil {
return err
}
sis.EncryptedPacket = tmp.EncryptedPacket
sis.SpliceCommand = tmp.SpliceCommand()
sis.SpliceDescriptors = tmp.SpliceDescriptors
sis.SAPType = tmp.SAPType
sis.PTSAdjustment = tmp.PTSAdjustment
sis.ProtocolVersion = tmp.ProtocolVersion
sis.Tier = tmp.Tier
return nil
}
// length returns the expected length of the encoded splice_info_section, in
// bytes.
func (sis *SpliceInfoSection) length() int {
length := 8 // table_id
length++ // section_syntax_indicator
length++ // private_indicator
length += 2 // reserved
length += 12 // section_length (bytes remaining)
length += sis.sectionLength() * 8 // everything else (bytes -> bits)
return length / 8
}
// sectionLength returns the section_length, in bytes
func (sis *SpliceInfoSection) sectionLength() int {
length := 8 // protocol_version
length++ // encrypted_packet
length += 6 // encryption_algorithm
length += 33 // pts_adjustment
length += 8 // cw_index
length += 12 // tier
length += 12 // splice_command_length
length += 8 // splice_command_type
if sis.SpliceCommand != nil {
length += sis.SpliceCommand.length() * 8 // bytes -> bits
}
length += 16 // descriptor_loop_length (bytes remaining value)
length += sis.descriptorLoopLength() * 8 // bytes -> bits
length += len(sis.alignmentStuffing) * 8 // bytes -> bits
// we don't officially support encrypted signals so this section is untested.
// It's implemented here as a base-line if/when we decide to support
// encryption (ie, use at your own risk)
if sis.EncryptedPacket.EncryptionAlgorithm != EncryptionAlgorithmNone {
length += 32 // ECRC_32
}
length += 32 // CRC_32
return length / 8
}
// descriptorLoopLength return the descriptor_loop_length
func (sis *SpliceInfoSection) descriptorLoopLength() int {
length := 0
for _, d := range sis.SpliceDescriptors {
length += 8 // splice_descriptor_tag
length += 8 // descriptor_length
length += d.length() * 8 // splice_descriptor()
}
return length / 8
}
// iSIS is an internal SpliceInfoSection used to support (un)marshalling
// polymorphic fields.
type iSIS struct {
EncryptedPacket EncryptedPacket `xml:"http://www.scte.org/schemas/35 EncryptedPacket,omitempty" json:"encryptedPacket,omitempty"`
SpliceCommandRaw json.RawMessage `xml:"-" json:"spliceCommand"`
SpliceNull *SpliceNull `xml:"http://www.scte.org/schemas/35 SpliceNull" json:"-"`
SpliceSchedule *SpliceSchedule `xml:"http://www.scte.org/schemas/35 SpliceSchedule" json:"-"`
SpliceInsert *SpliceInsert `xml:"http://www.scte.org/schemas/35 SpliceInsert" json:"-"`
TimeSignal *TimeSignal `xml:"http://www.scte.org/schemas/35 TimeSignal" json:"-"`
BandwidthReservation *BandwidthReservation `xml:"http://www.scte.org/schemas/35 BandwidthReservation" json:"-"`
PrivateCommand *PrivateCommand `xml:"http://www.scte.org/schemas/35 PrivateCommand" json:"-"`
SpliceDescriptors SpliceDescriptors `xml:",any" json:"spliceDescriptors"`
SAPType uint32 `xml:"sapType,attr" json:"sapType"`
PTSAdjustment uint64 `xml:"ptsAdjustment,attr" json:"ptsAdjustment"`
ProtocolVersion uint32 `xml:"protocolVersion,attr" json:"protocolVersion"`
Tier uint32 `xml:"tier,attr" json:"tier"`
}
// SpliceCommand returns the polymorphic splice_command.
func (i *iSIS) SpliceCommand() SpliceCommand {
// xml unmarshalls to the corresponding struct
if i.SpliceNull != nil {
return i.SpliceNull
}
if i.SpliceSchedule != nil {
return i.SpliceSchedule
}
if i.SpliceInsert != nil {
return i.SpliceInsert
}
if i.TimeSignal != nil {
return i.TimeSignal
}
if i.BandwidthReservation != nil {
return i.BandwidthReservation
}
// no valid splice_command?
if i.SpliceCommandRaw == nil {
return nil
}
// struct to determine the splice command's type
type sctype struct {
Type uint32 `json:"type"`
}
// get the type
var st sctype
if err := json.Unmarshal(i.SpliceCommandRaw, &st); err != nil {
Logger.Printf("error unmarshalling splice command type: %s", err)
return nil
}
// and decode it
sc := NewSpliceCommand(st.Type)
if err := json.Unmarshal(i.SpliceCommandRaw, sc); err != nil {
Logger.Printf("error unmarshalling splice command: %s", err)
return nil
}
return sc
}