Install | Go Docs | Examples |
- Parses SCTE-35 Cues from MPEGTS or Bytes or Base64 or Hex or Int or Octal or even Base 36.
- Parses SCTE-35 Cues spread over multiple MPEGTS packets
- Supports multi-packet PAT and PMT tables
- Supports multiple MPEGTS Programs and multiple SCTE-35 streams
- Encodes Time Signals and Splice Inserts with Descriptors and Upids.
package main
import (
"os"
"github.com/superkabuki/skdc"
)
func main(){
arg := os.Args[1]
stream := skdc.NewStream()
stream.Decode(arg)
}
-
-
skdc.Stream
-
skdc.Cue
-
go install github.com/superkabuki/skdc@latest
- skdcdemo.go
package main
import (
"os"
"fmt"
"github.com/superkabuki/skdc"
)
func main(){
arg := os.Args[1]
stream := skdc.NewStream()
cues := stream.Decode(arg)
for _,cue := range cues {
fmt.Printf("Command is a %v\n", cue.Command.Name)
}
}
go build skdcdemo.go
./skdcdemo a_video_with_scte35.ts
type Cue struct {
InfoSection *InfoSection
Command *Command
Dll uint16 `json:"DescriptorLoopLength"`
Descriptors []Descriptor `json:",omitempty"`
Crc32 string
PacketData *packetData `json:",omitempty"`
}
- Output is JSON and looks like this.
{
"InfoSection": {
"Name": "Splice Info Section",
"TableID": "0xfc",
"SectionSyntaxIndicator": false,
"Private": false,
"SapType": 3,
"SapDetails": "No Sap Type",
"SectionLength": 44,
"ProtocolVersion": 0,
"EncryptedPacket": false,
"EncryptionAlgorithm": 0,
"PtsAdjustment": 2.3,
"CwIndex": "0x0",
"Tier": "0xfff",
"CommandLength": 10,
"CommandType": 5
},
"Command": {
"Name": "Splice Insert",
"CommandType": 5,
"SpliceEventID": 1,
"SpliceEventCancelIndicator": false,
"OutOfNetworkIndicator": false,
"ProgramSpliceFlag": true,
"DurationFlag": false,
"BreakDuration": 0,
"BreakAutoReturn": false,
"SpliceImmediateFlag": true,
"EventIDComplianceFlag": true,
"UniqueProgramID": 39321,
"AvailNum": 1,
"AvailExpected": 1
},
"DescriptorLoopLength": 17,
"Descriptors": [
{
"Tag": 2,
"Length": 15,
"Name": "Segmentation Descriptor",
"Identifier": "CUEI",
"SegmentationEventID": "0x0",
"SegmentationEventCancelIndicator": false,
"SegmentationEventIDComplianceIndicator": true,
"ProgramSegmentationFlag": true,
"SegmentationDurationFlag": false,
"DeliveryNotRestrictedFlag": false,
"WebDeliveryAllowedFlag": false,
"NoRegionalBlackoutFlag": false,
"ArchiveAllowedFlag": false,
"DeviceRestrictions": "Restrict Group 0",
"SegmentationDuration": 0,
"SegmentationMessage": "Provider Placement Opportunity End",
"SegmentationUpidType": 1,
"SegmentationUpidLength": 0,
"SegmentationUpid": null,
"SegmentationTypeID": 53,
"SegmentNum": 0,
"SegmentsExpected": 0,
"SubSegmentNum": 0,
"SubSegmentsExpected": 0
}
],
"Crc32": " 0x2d974195",
"PacketData": {
"Pid": 258,
"Program": 1,
"Pts": 72951.196988
}
}
package main
import (
"fmt"
"github.com/superkabuki/skdc"
)
func main(){
cue := skdc.NewCue()
data := "/DA7AAAAAAAAAP/wFAUAAAABf+/+AItfZn4AKTLgAAEAAAAWAhRDVUVJAAAAAX//AAApMuABACIBAIoXZrM="
cue.Decode(data)
fmt.Println("Cue as Json")
cue.Show()
}
package main
import (
"fmt"
"github.com/superkabuki/skdc"
)
type Cue2 struct {
skdc.Cue // Embed skdc.Cue
}
func (cue2 *Cue2) Show() { // Override Show
fmt.Printf("%+v",cue2.Command)
}
func main(){
var cue2 Cue2
data := "/DA7AAAAAAAAAP/wFAUAAAABf+/+AItfZn4AKTLgAAEAAAAWAhRDVUVJAAAAAX//AAApMuABACIBAIoXZrM="
cue2.Decode(data)
cue2.Show()
}
package main
import (
"fmt"
"github.com/superkabuki/skdc"
)
type Cue2 struct {
skdc.Cue // Embed skdc.Cue
}
func (cue2 *Cue2) Show() { // Override Show
fmt.Println("Cue2.Show()")
fmt.Printf("%+v",cue2.Command)
fmt.Println("\n\nskdc.Cue.Show() from cue2.Show()")
cue2.Cue.Show() // Call the Show method from embedded skdc.Cue
}
func main(){
var cue2 Cue2
data := "/DA7AAAAAAAAAP/wFAUAAAABf+/+AItfZn4AKTLgAAEAAAAWAhRDVUVJAAAAAX//AAApMuABACIBAIoXZrM="
cue2.Decode(data)
cue2.Show()
}
- SuperKarate DeathCar doesn't use interfaces for SCTE-35 data
- SCTE-35 Data can be accessed directly via dot notation
/**
Show the packet PTS time and Splice Command Name of SCTE-35 Cues
in a MPEGTS stream.
**/
package main
import (
"os"
"fmt"
"github.com/superkabuki/skdc"
)
func main() {
arg := os.Args[1]
stream := skdc.NewStream()
cues := stream.Decode(arg)
for _,c := range cues {
fmt.Printf("PTS: %v, Splice Command: %v\n",c.PacketData.Pts, c.Command.Name )
}
}
- skdc can accept SCTE-35 data as JSON and encode it to Base64, Bytes, or Hex string.
- The function skdc.Json2Cue() accepts SCTE-35 JSON as input and returns a *skdc.Cue
package main
import (
"fmt"
"github.com/superkabuki/skdc"
)
func main() {
js := `{
"InfoSection": {
"Name": "Splice Info Section",
"TableID": "0xfc",
"SectionSyntaxIndicator": false,
"Private": false,
"Reserved": "0x3",
"SectionLength": 42,
"ProtocolVersion": 0,
"EncryptedPacket": false,
"EncryptionAlgorithm": 0,
"PtsAdjustment": 0,
"CwIndex": "0xff",
"Tier": "0xfff",
"CommandLength": 15,
"CommandType": 5
},
"Command": {
"Name": "Splice Insert",
"CommandType": 5,
"SpliceEventID": 5690,
"OutOfNetworkIndicator": true,
"ProgramSpliceFlag": true,
"TimeSpecifiedFlag": true,
"PTS": 23683.480033
},
"DescriptorLoopLength": 10,
"Descriptors": [
{
"Length": 8,
"Identifier": "CUEI",
"Name": "Avail Descriptor"
}
],
"Crc32": "0xd7165c79"
}
`
cue := skdc.Json2Cue(js) //
cue.AdjustPts(28.0) // Apply pts adjustment
fmt.Println("\nBytes:\n\t", cue.Encode()) // Bytes
fmt.Println("\nBase64:\n\t",cue.Encode2B64()) // Base64
fmt.Println("\nHex:\n\t",cue.Encode2Hex()) // Hex
}
- Output
Bytes:
[252 48 42 0 0 0 38 115 192 255 255 240 15 5 0 0 22 58 127 207 254 127 12 79 115
0 0 0 0 0 10 0 8 67 85 69 73 0 0 0 0 236 139 53 78]
Base64:
/DAqAAAAJnPA///wDwUAABY6f8/+fwxPcwAAAAAACgAIQ1VFSQAAAADsizVO
Hex:
0xfc302a0000002673c0fffff00f050000163a7fcffe7f0c4f7300000000000a00084355454900000000ec8b354e
- Create Stream Instance
- Read Bytes from the video stream (in multiples of 188)
- Call Stream.DecodeBytes(Bytes)
- Process [] *Cue returned by Stream.DecodeBytes
package main
import (
"fmt"
"github.com/superkabuki/skdc"
"os"
)
func main() {
arg := os.Args[1]
stream := skdc.NewStream() // (1)
bufSize := 32768 * 188 // Always read in multiples of 188
file, err := os.Open(arg)
if err != nil {
fmt.Printf("Unable to read %v\n", arg)
}
buffer := make([]byte, bufSize)
for {
_, err := file.Read(buffer) // (2)
if err != nil {
break
}
cues := stream.DecodeBytes(buffer) // (3)
for _, c := range cues { // (4)
fmt.Printf(" %v, %v\n", c.PacketData.Pts, c.Encode2B64())
}
}
}
- Output
60638.745877, /DAWAAAAAAAAAP/wBQb/RUqw1AAAd6OnQA==
60638.745877, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60640.714511, /DAWAAAAAAAAAP/wBQb/RU1wqAAAoqaOaA==
60640.714511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60642.015811, /DAWAAAAAAAAAP/wBQb/RU9F4AAA9Te5ag==
60642.015811, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60642.749877, /DAWAAAAAAAAAP/wBQb/RVAwfAAAWOLrFQ==
60642.749877, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60644.718511, /DAWAAAAAAAAAP/wBQb/RVLwUAAAj7/Pgw==
60644.718511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60646.720511, /DAWAAAAAAAAAP/wBQb/RVWwJAAA8pm/jg==
60646.720511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60648.121911, /DAWAAAAAAAAAP/wBQb/RVec0gAAt0QzqA==
60648.121911, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60634.208011, /DAWAAAAAAAAAP/wBQb/RUR1fAAAik8gfQ==
60634.208011, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60634.675144, /DAWAAAAAAAAAP/wBQb/RUUxLAAABQVyEA==
60634.675144, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
60636.710511, /DAWAAAAAAAAAP/wBQb/RUfxAAAA0lhWhg==
60636.710511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=
Need a multicast sender? Try gums
for multicast we use the same four steps,
the only difference is we read the bytes from the network instead of a local file.
the only difference is we read the bytes from the network instead of a local file.
- Create Stream Instance
- Read Bytes from the video stream (in multiples of 188)
- Call Stream.DecodeBytes(Bytes)
- Process [] *Cue returned by Stream.DecodeBytes
package main
import (
"fmt"
"github.com/superkabuki/skdc"
"os"
"net"
)
func main() {
arg := os.Args[1]
stream := skdc.NewStream() // (1)
stream.Quiet = true
dgram:=1316 // <-- multicast dgram size is 1316 (188*7) for mpegts
bufSize := 100 * dgram
addr, _ := net.ResolveUDPAddr("udp", arg)
l, _ := net.ListenMulticastUDP("udp", nil, addr) // Multicast Connection
l.SetReadBuffer(bufSize)
for {
buffer := make([]byte, bufSize) // (2)
l.ReadFromUDP(buffer)
cues := stream.DecodeBytes(buffer) // (3)
for _, c := range cues { // (4)
fmt.Printf(" %v, %v\n", c.PacketData.Pts, c.Encode2B64())
}
}
}