https://github.com/superkabuki/skdc
Super Karate Death Car is the World's Fastest SCTE-35 Parse library. Written in Go
https://github.com/superkabuki/skdc
Last synced: about 1 month ago
JSON representation
Super Karate Death Car is the World's Fastest SCTE-35 Parse library. Written in Go
- Host: GitHub
- URL: https://github.com/superkabuki/skdc
- Owner: superkabuki
- License: bsd-2-clause
- Created: 2025-03-10T01:22:55.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2025-03-10T05:34:36.000Z (about 2 months ago)
- Last Synced: 2025-03-10T06:28:12.089Z (about 2 months ago)
- Language: Go
- Size: 32.2 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[Install](#install-skdc) | [Go Docs](https://pkg.go.dev/github.com/superkabuki/skdc) | [Examples](https://pkg.go.dev/github.com/superkabuki/skdc) |
# `S`uper `K`arate `D`eath `C`ar is a SCTE-35 Parser lib written in Go.
# Encoder/Decoder for SCTE-35- [x] Parses SCTE-35 Cues from MPEGTS or Bytes or Base64 or Hex or Int or Octal or even Base 36.
- [x] Parses SCTE-35 Cues spread over multiple MPEGTS packets
- [x] Supports multi-packet PAT and PMT tables
- [x] Supports multiple MPEGTS Programs and multiple SCTE-35 streams
- [x] Encodes Time Signals and Splice Inserts with Descriptors and Upids.
___## Super Karate Death Car is fast.
### Want to parse an MPEGTS video and print the SCTE-35? 🛰️
### Do it in ten lines.
```go
package mainimport (
"os"
"github.com/superkabuki/skdc"
)func main(){
arg := os.Args[1]
stream := skdc.NewStream()
stream.Decode(arg)
}
```
---# Documentation
* [Install](#install-skdc)
* [Examples](https://pkg.go.dev/github.com/superkabuki/skdc)
* skdc.Stream
* [Parse SCTE-35 from MPEGTS](#quick-demo)
* [Custom Cue Handling for MPEGTS Streams](#custom-cue-handling-for-mpegts-streams)
* [Multicast](#custom-cue-handling-for-mpegts-streams-over-multicast) _(New!)_* skdc.Cue
* [Parse Base64 encoded SCTE-35](#parse-base64-encoded-scte-35)
* [Use Dot Notation to access SCTE-35 Cue values](#use-dot-notation-to-access-scte-35-cue-values)
* [Shadow a Cue Struct Method ( override ) ](#shadow-a-cue-struct-method)
* [Shadow a Cue Method and call the Shadowed Method ( like super in python )](#call-a-shadowed-method)
* [Load a SCTE-35 Cue from JSON and Encode it](#load-json-and-encode)### `Install skdc`
```go
go install github.com/superkabuki/skdc@latest```
### `Quick Demo`
* skdcdemo.go
```go
package mainimport (
"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)
}}
```
#### `build skdcdemo`
```go
go build skdcdemo.go
```
#### `parse mpegts video for scte35`
```go
./skdcdemo a_video_with_scte35.ts
```
### SCTE-35 Cues
```go
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.
```awk
{
"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
}
}
```### `Parse base64 encoded SCTE-35`
```go
package mainimport (
"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()
}
```### `Shadow a Cue struct method`
```go
package mainimport (
"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()
}```
### `Call a shadowed method`
```go
package mainimport (
"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()
}```
### `Use Dot notation to access SCTE-35 Cue values`
* SuperKarate DeathCar doesn't use interfaces for SCTE-35 data
* SCTE-35 Data can be accessed directly via dot notation```go
/**
Show the packet PTS time and Splice Command Name of SCTE-35 Cues
in a MPEGTS stream.
**/
package mainimport (
"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 )
}
}```
### `Load JSON and Encode`
* 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```go
package mainimport (
"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
```smalltalk
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/+fwxPcwAAAAAACgAIQ1VFSQAAAADsizVOHex:
0xfc302a0000002673c0fffff00f050000163a7fcffe7f0c4f7300000000000a00084355454900000000ec8b354e```
## skdc.Stream
### `Custom Cue Handling for MPEGTS Streams`
##### Four Steps
1) Create Stream Instance
2) Read Bytes from the video stream (__in multiples of 188__)
3) Call Stream.DecodeBytes(Bytes)
4) Process [] *Cue returned by Stream.DecodeBytes```go
package mainimport (
"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
```php
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=
```### `Custom Cue Handling for MPEGTS Streams Over Multicast`
##### Need a multicast sender? Try [gums](https://github.com/superkabuki/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.1) Create Stream Instance
2) Read Bytes from the video stream (__in multiples of 188__)
3) Call Stream.DecodeBytes(Bytes)
4) Process [] *Cue returned by Stream.DecodeBytes
```go
package mainimport (
"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())
}
}
}
```