Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 145 additions & 124 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The results can be output as a:
* XML - compliant with the [SCTE 35 XML Schema](./docs/scte_35_20220816.xsd)
* JSON - for integrating with JSON based tools such as [jq](https://stedolan.github.io/jq/)

[decode.go](../examples/decode.go)
[examples/simple_decode/main.go](examples/simple_decode/main.go)

```go
package main
Expand All @@ -54,7 +54,7 @@ func main() {
sis, _ := scte35.DecodeBase64("/DA8AAAAAAAAAP///wb+06ACpQAmAiRDVUVJAACcHX//AACky4AMEERJU0NZTVdGMDQ1MjAwMEgxAQEMm4c0")

// details
_, _ = fmt.Fprintf(os.Stdout, "\nDetails: \n%s\n", sis)
_, _ = fmt.Fprintf(os.Stdout, "\nTable: \n%s\n", sis.Table("", "\t"))

// xml
b, _ := xml.MarshalIndent(sis, "", "\t")
Expand All @@ -67,96 +67,94 @@ func main() {
```

```shell
$ go run ./examples/decode.go
$ go run examples/simple_decode/main.go

Details:
Table:
splice_info_section() {
table_id: 0xfc
section_syntax_indicator: false
private_indicator: false
sap_type: Not Specified
section_length: 60 bytes
table_id: 0xfc
section_syntax_indicator: false
private_indicator: false
sap_type: 3 (Not Specified)
section_length: 60
}
protocol_version: 0
encrypted_packet: false
encryption_algorithm: No encryption
pts_adjustment: 0 ticks (0s)
encryption_algorithm: 0 (No encryption)
pts_adjustment: 0
cw_index: 0
tier: 4095
splice_command_length: 5 bytes
splice_command_length: 5
splice_command_type: 0x06
time_signal() {
time_specified_flag: true
pts_time: 3550479013 ticks (10h57m29.766811111s)
time_specified_flag: true
pts_time: 3550479013
}
descriptor_loop_length: 38 bytes
avail_descriptor() {
splice_descriptor_tag: 0x02
descriptor_length: 36 bytes
identifier: CUEI
segmentation_event_id: 39965
segmentation_event_cancel_indicator: false
program_segmentation_flag: true
segmentation_duration_flag: true
delivery_not_restricted_flag: true
segmentation_duration: 10800000 ticks (2m0s)
segmentation_upid_length: 16 bytes
segmentation_upid {
segmentation_upid_type: MPU() (0x0c)
format_identifer: DISC
segmentation_upid: 0x44495343594d57463034353230303048
}
segmentation_type_id: Provider Advertisement End (0x31)
segment_num: 1
segments_expected: 1
descriptor_loop_length: 38
segmentation_descriptor() {
splice_descriptor_tag: 0x02
descriptor_length: 36
identifier: 0x43554549 (CUEI)
segmentation_event_id: 39965
segmentation_event_cancel_indicator: false
segmentation_event_id_compliance_indicator: false
program_segmentation_flag: true
segmentation_duration_flag: true
delivery_not_restricted_flag: true
segmentation_duration: 10800000
segmentation_upid_length: 16
segmentation_upid[0] {
segmentation_upid_type: 0x0c (MPU())
format_identifier: DISC
segmentation_upid: WU1XRjA0NTIwMDBI
}
segmentation_type_id: 0x31 (Provider Advertisement End)
segment_num: 1
segments_expected: 1
}

XML:

XML:
<SpliceInfoSection xmlns="http://www.scte.org/schemas/35" sapType="3" tier="4095">
<EncryptedPacket xmlns="http://www.scte.org/schemas/35" encryptionAlgorithm="0" cwIndex="0"></EncryptedPacket>
<TimeSignal xmlns="http://www.scte.org/schemas/35">
<SpliceTime xmlns="http://www.scte.org/schemas/35" ptsTime="3550479013"></SpliceTime>
</TimeSignal>
<SegmentationDescriptor xmlns="http://www.scte.org/schemas/35" segmentationEventId="39965" segmentationDuration="10800000" segmentationTypeId="49" segmentNum="1" segmentsExpected="1">
<SegmentationUpid xmlns="http://www.scte.org/schemas/35" segmentationUpidType="12" segmentationUpidFormat="base-64" formatIdentifier="1145656131">WU1XRjA0NTIwMDBI</SegmentationUpid>
</SegmentationDescriptor>
<EncryptedPacket xmlns="http://www.scte.org/schemas/35" encryptionAlgorithm="0" cwIndex="0"></EncryptedPacket>
<TimeSignal xmlns="http://www.scte.org/schemas/35">
<SpliceTime xmlns="http://www.scte.org/schemas/35" ptsTime="3550479013"></SpliceTime>
</TimeSignal>
<SegmentationDescriptor xmlns="http://www.scte.org/schemas/35" segmentationEventId="39965" segmentationDuration="10800000" segmentationTypeId="49" segmentNum="1" segmentsExpected="1">
<SegmentationUpid xmlns="http://www.scte.org/schemas/35" segmentationUpidType="12" segmentationUpidFormat="base-64" formatIdentifier="1145656131">WU1XRjA0NTIwMDBI</SegmentationUpid>
</SegmentationDescriptor>
</SpliceInfoSection>

JSON:
JSON:
{
"protocolVersion": 0,
"ptsAdjustment": 0,
"sapType": 3,
"spliceCommand": {
"type": 6,
"spliceTime": {
"ptsTime": 3550479013
}
},
"spliceDescriptors": [
{
"type": 2,
"deliveryRestrictions": null,
"segmentationUpids": [
{
"segmentationUpidType": 12,
"formatIdentifier": 1145656131,
"format": "base-64",
"value": "WU1XRjA0NTIwMDBI"
}
],
"components": null,
"segmentationEventId": 39965,
"segmentationEventCancelIndicator": false,
"segmentationDuration": 10800000,
"segmentationTypeId": 49,
"segmentNum": 1,
"segmentsExpected": 1,
"subSegmentNum": null,
"subSegmentsExpected": null
}
],
"tier": 4095
"encryptedPacket": {
"encryptionAlgorithm": 0,
"cwIndex": 0
},
"sapType": 3,
"spliceCommand": {
"type": 6,
"spliceTime": {
"ptsTime": 3550479013
}
},
"spliceDescriptors": [
{
"type": 2,
"segmentationUpids": [
{
"segmentationUpidType": 12,
"segmentationUpidFormat": "base-64",
"formatIdentifier": 1145656131,
"value": "WU1XRjA0NTIwMDBI"
}
],
"segmentationEventId": 39965,
"segmentationDuration": 10800000,
"segmentationTypeId": 49,
"segmentNum": 1,
"segmentsExpected": 1
}
],
"tier": 4095
}
```

Expand All @@ -166,7 +164,7 @@ Encoding signals is equally simple. You can start from scratch and build a
`scte35.SpliceInfoSection` or decode an existing signal and modify it to suit
your needs.

[encode.go](./examples/encode.go)
[examples/simple_encode/main.go](examples/simple_encode/main.go)

```go
package main
Expand Down Expand Up @@ -197,9 +195,12 @@ func main() {
SegmentNum: 2,
},
},
EncryptedPacket: scte35.EncryptedPacket{CWIndex: 255},
Tier: 4095,
SAPType: 3,
EncryptedPacket: scte35.EncryptedPacket{
EncryptionAlgorithm: scte35.EncryptionAlgorithmNone,
CWIndex: 255,
},
Tier: 4095,
SAPType: 3,
}

// encode it
Expand All @@ -216,20 +217,18 @@ func main() {
)

// encode it again
_, _ = fmt.Fprintf(os.Stdout, "\nModified:\n")
_, _ = fmt.Fprintf(os.Stdout, "Original:\n")
_, _ = fmt.Fprintf(os.Stdout, "base-64: %s\n", sis.Base64())
_, _ = fmt.Fprintf(os.Stdout, "hex : %s\n", sis.Hex())
}

```

```shell
$ go run ./examples/encode.go
$ go run examples/simple_encode/main.go
Original:
base-64: /DAvAAAAAAAA///wBQb+cr0AUAAZAhdDVUVJSAAAjn+PCAg3ODUxMTQ1MjQCADhqB9E=
hex : fc302f000000000000fffff00506fe72bd005000190217435545494800008e7f8f08083738353131343532340200386a07d1

Modified:
Original:
base-64: /DA7AAAAAAAA///wBQb+cr0AUAAlAhdDVUVJSAAAjn+PCAg3ODUxMTQ1MjQCAAEKQ1VFSQCfQUJDKqtwQlQ=
hex : fc303b000000000000fffff00506fe72bd005000250217435545494800008e7f8f08083738353131343532340200010a43554549009f4142432aab704254
```
Expand All @@ -240,7 +239,7 @@ The SCTE 35 decoder will always return a non-nil `SpliceInfoSection`, even when
an error occurs. This is done to help better identify the specific cause of the
decoding failure.

[bad-signal](./examples/bad_signal.go)
[examples/bad_signal/main.go](examples/bad_signal/main.go)

```go
package main
Expand All @@ -249,79 +248,101 @@ import (
"fmt"
"os"

"code.comcast.com/jbaile223/scte35/pkg/scte35"
"github.com/Comcast/scte35-go/pkg/scte35"
)

func main() {
sis, err := scte35.DecodeBase64("FkC1lwP3uTQD0VvxHwVBEH89G6B7VjzaZ9eNuyUF9q8pYAIXsRM9ZpDCczBeDbytQhXkssQstGJVGcvjZ3tiIMULiA4BpRHlzLGFa0q6aVMtzk8ZRUeLcxtKibgVOKBBnkCbOQyhSflFiDkrAAIp+Fk+VRsByTSkPN3RvyK+lWcjHElhwa9hNFcAy4dm3DdeRXnrD3I2mISNc7DkgS0ReotPyp94FV77xMHT4D7SYL48XU20UM4bgg==")
if err != nil {
fmt.Fprintf(os.Stdout, "Error: %s\n", err)
_, _ = fmt.Fprintf(os.Stdout, "Error: %s\n", err)
}
// splice info section contains best-effort decoding
fmt.Fprintf(os.Stdout, "Signal: %s", sis)
_, _ = fmt.Fprintf(os.Stdout, "%s\n", sis.Table("", "\t"))
}
```

As we can see from the output below, the signal has a corrupted `component_count`,
causing the decoder to return a `scte.ErrBufferOverflow`:

```shell
$ go run ./examples/bad_signal.go
$ go run examples/bad_signal/main.go
Error: splice_insert: buffer overflow
Signal: splice_info_section() {
table_id: 0xfc
section_syntax_indicator: false
private_indicator: false
sap_type: Type 1
section_length: 343 bytes
splice_info_section() {
table_id: 0xfc
section_syntax_indicator: false
private_indicator: false
sap_type: 0 (Type 1)
section_length: 347
}
protocol_version: 151
encrypted_packet: false
encryption_algorithm: No encryption
pts_adjustment: 8451077123 ticks (26h5m0.856922222s)
encryption_algorithm: 1 (DES – ECB mode)
pts_adjustment: 8451077123
cw_index: 209
tier: 1471
splice_command_length: 326 bytes
splice_command_length: 326
splice_command_type: 0x05
splice_insert() {
splice_event_id: 1091600189
splice_event_cancel_indicator: false
out_of_network_indicator: true
program_splice_flag: false
duration_flag: true
splice_immediate_flag: false
component_count: 123
component[0] {
component_tag: 86
time_specified_flag: false
splice_event_id: 1091600189
splice_event_cancel_indicator: false
out_of_network_indicator: true
program_splice_flag: false
duration_flag: true
splice_immediate_flag: false
component_count: 123
component[0]
component_tag: 86
time_specified_flag: false
}

... additional components removed

component[122] {
component_tag: 0
time_specified_flag: false
}
auto_return: false
duration: 0 ticks (0s)
unique_program_id: 0
avail_num: 0
avails_expected: 0
auto_return: false
duration: 0
unique_program_id: 0
avail_num: 0
avails_expected: 0
}
descriptor_loop_length: 0
```

#### CRC_32 Validation

The SCTE 35 decoder performs automatic `CRC_32` validation. The returned error
can be explicitly ignored if desired.

[examples/ignore_crc32/main.go](examples/ignore_crc32/main.go)

```go
sis, err := scte35.DecodeBase64("/DA4AAAAAAAAAP/wFAUABDEAf+//mWEhzP4Azf5gAQAAAAATAhFDVUVJAAAAAX+/AQIwNAEAAKeYO3Q=")
if err != nil && !errors.Is(err, scte35.ErrCRC32Invalid) {
return err
package main

import (
"errors"
"fmt"
"os"

"github.com/Comcast/scte35-go/pkg/scte35"
)

func main() {
scte35.Logger.SetOutput(os.Stdout)

sis, err := scte35.DecodeBase64("/DA4AAAAAAAAAP/wFAUABDEAf+//mWEhzP4Azf5gAQAAAAATAhFDVUVJAAAAAX+/AQIwNAEAAKeYO3Q=")
if errors.Is(err, scte35.ErrCRC32Invalid) {
_, _ = fmt.Fprintf(os.Stderr, "Warning: CRC32 check failed!\n")
}
_, _ = fmt.Fprintf(os.Stdout, "%s\n", sis.Table("", "\t"))
}
```

```shell
$ go run examples/ignore_crc32/main.go
2025/03/15 00:44:42 CRC_32 calculated (2811771763) != reported (2811771764)
Warning: CRC32 check failed!
splice_info_section() {
table_id: 0xfc

... more output removed ...
```

#### Logging

Additional diagnostics can be enabled by redirecting the output of
Expand Down
Loading