{"id":28066163,"url":"https://github.com/futzu/cuei","last_synced_at":"2025-05-12T14:38:37.491Z","repository":{"id":58093810,"uuid":"528645654","full_name":"futzu/cuei","owner":"futzu","description":"cuei is the Fastest SCTE35 Encoder/ Decoder on the planet. Now with Multicast support. Written in Go. Parses MPEGTS, Bytes, Base64, Hex, Int and more. ","archived":false,"fork":false,"pushed_at":"2024-11-16T16:50:56.000Z","size":507,"stargazers_count":23,"open_issues_count":1,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-12T14:38:05.793Z","etag":null,"topics":["ad-insertion","adbreak","adrian-of-doom","golang","mpegts","scte-35","scte-marker","scte35","scte35-2022","scte35-2023","scte35-parser","threefive","your-momma"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/futzu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-25T01:00:21.000Z","updated_at":"2025-04-03T19:28:51.000Z","dependencies_parsed_at":"2023-11-06T02:37:18.602Z","dependency_job_id":"2f0e918d-9ee2-47a4-a067-f40cb98a254b","html_url":"https://github.com/futzu/cuei","commit_stats":{"total_commits":521,"total_committers":1,"mean_commits":521.0,"dds":0.0,"last_synced_commit":"1f232825f45105b99194acdc7983570617f19ba4"},"previous_names":[],"tags_count":82,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futzu%2Fcuei","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futzu%2Fcuei/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futzu%2Fcuei/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futzu%2Fcuei/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/futzu","download_url":"https://codeload.github.com/futzu/cuei/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253755893,"owners_count":21959107,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ad-insertion","adbreak","adrian-of-doom","golang","mpegts","scte-35","scte-marker","scte35","scte35-2022","scte35-2023","scte35-parser","threefive","your-momma"],"created_at":"2025-05-12T14:38:35.006Z","updated_at":"2025-05-12T14:38:37.445Z","avatar_url":"https://github.com/futzu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n [Install](#install-cuei)  | [Go Docs](https://pkg.go.dev/github.com/futzu/cuei)  | [Examples](https://pkg.go.dev/github.com/futzu/cuei) | [cuei wins the SCTE-35 Parser Shoot Out](https://github.com/futzu/SCTE35-threefive/blob/master/speedtest.md)\n\n\n\n\n# cuei is a SCTE-35 Parser lib written in Go.\n# Encoder/Decoder for SCTE-35\n\u003cbr\u003e\n\n\n- [x] Parses SCTE-35 Cues from MPEGTS or Bytes or Base64 or Hex or Int or Octal or even Base 36.\n- [x] Parses SCTE-35 Cues spread over multiple MPEGTS packets  \n- [x] Supports multi-packet PAT and PMT tables  \n- [x] Supports multiple MPEGTS Programs and multiple SCTE-35 streams \n- [x] Encodes Time Signals and Splice Inserts with Descriptors and Upids. \n \n___\n\n\n### Want to parse an MPEGTS video and print the SCTE-35?  🛰️\n\n### Do it in ten lines.\n```go\npackage main                        \n\nimport (                              \n        \"os\"                            \n        \"github.com/futzu/cuei\"       \n)                                    \n\nfunc main(){                         \n        arg := os.Args[1]             \n        stream := cuei.NewStream()    \n        stream.Decode(arg)           \n}                                    \n```\n---\n### Latest version is `One`  `Two`   `thirty-seven`\n* Cyclomatic complexity score for v1.2.39 is __1.96__\n\n# Documentation\n\n* [Releases](#releases)\n\n* [Install](#install-cuei)  \n\n\n* [Examples](https://pkg.go.dev/github.com/futzu/cuei)\n\t* cuei.Stream \n\t\t* [Parse SCTE-35 from MPEGTS](#quick-demo)\n   \t\t* [Custom Cue Handling for MPEGTS Streams](#custom-cue-handling-for-mpegts-streams)\n   \t\t* [Multicast](#custom-cue-handling-for-mpegts-streams-over-multicast) _(New!)_\n\n   \t* cuei.Cue\n\t\t* [Parse Base64 encoded SCTE-35](#parse-base64-encoded-scte-35) \n\t\t* [Use Dot Notation to access SCTE-35 Cue values](#use-dot-notation-to-access-scte-35-cue-values)\n\t\t* [Shadow a Cue Struct Method ( override ) ](#shadow-a-cue-struct-method)\n\t\t* [Shadow a Cue Method and call the Shadowed Method ( like super in python )](#call-a-shadowed-method)\n  \t\t* [Load a SCTE-35 Cue from JSON and Encode it](#load-json-and-encode)\n\n\n\n### `Releases`\n\u003e I do a lot of releases, well sometimes. Whenever I make an improvement or fix a bug, I do a release.\n\u003e Small changes as they happen not all the changes at once. \n\n### `Install cuei` \n\n```go\ngo get github.com/futzu/cuei@latest\n\n```\n\n\n\n### `Quick Demo` \n\n* cueidemo.go\n\n```go\npackage main\n\nimport (\n        \"os\"\n        \"fmt\"\n        \"github.com/futzu/cuei\"\n)\n\nfunc main(){\n\n        arg := os.Args[1]\n\n        stream := cuei.NewStream()\n        cues := stream.Decode(arg)\n        for _,cue := range cues {\n        fmt.Printf(\"Command is a %v\\n\", cue.Command.Name)\n        }\n\n}\n\n\n\n```\n\n#### `build cueidemo`\n```go\ngo build cueidemo.go\n```\n#### `parse mpegts video for scte35` \n```go\n./cueidemo a_video_with_scte35.ts\n```\n\n#### `output`\n```json\nNext File: mpegts/out.ts\n\n{\n    \"Name\": \"Splice Info Section\",\n    \"TableID\": \"0xfc\",\n    \"SectionSyntaxIndicator\": false,\n    \"Private\": false,\n    \"Reserved\": \"0x3\",\n    \"SectionLength\": 49,\n    \"ProtocolVersion\": 0,\n    \"EncryptedPacket\": false,\n    \"EncryptionAlgorithm\": 0,\n    \"PtsAdjustment\": 0,\n    \"CwIndex\": \"0x0\",\n    \"Tier\": \"0xfff\",\n    \"SpliceCommandLength\": 20,\n    \"SpliceCommandType\": 5,\n    \"DescriptorLoopLength\": 12,\n    \"Command\": {\n        \"Name\": \"Splice Insert\",\n        \"CommandType\": 5,\n        \"SpliceEventID\": \"0x5d\",\n        \"OutOfNetworkIndicator\": true,\n        \"ProgramSpliceFlag\": true,\n        \"DurationFlag\": true,\n        \"BreakDuration\": 90.023266,\n        \"TimeSpecifiedFlag\": true,\n        \"PTS\": 38113.135577\n    },\n    \"Descriptors\": [\n        {\n            \"Tag\": 1,\n            \"Length\": 10,\n            \"Identifier\": \"CUEI\",\n            \"Name\": \"DTMF Descriptor\",\n            \"PreRoll\": 177,\n            \"DTMFCount\": 4,\n            \"DTMFChars\": 4186542473\n        }\n    ],\n    \"Packet\": {\n        \"PacketNumber\": 73885,\n        \"Pid\": 515,\n        \"Program\": 51,\n        \"Pcr\": 38104.526277,\n        \"Pts\": 38105.268588\n    }\n}\n\n\n```\n\t\t\t\n\t\n\n### `Parse base64 encoded SCTE-35`\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/futzu/cuei\"\n)\n\nfunc main(){\n\n\tcue := cuei.NewCue()\n\tdata := \"/DA7AAAAAAAAAP/wFAUAAAABf+/+AItfZn4AKTLgAAEAAAAWAhRDVUVJAAAAAX//AAApMuABACIBAIoXZrM=\"\n        cue.Decode(data) \n        fmt.Println(\"Cue as Json\")\n        cue.Show()\n}\n```\n\n### `Shadow a Cue struct method`\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/futzu/cuei\"\n)\n\ntype Cue2 struct {\n    cuei.Cue               \t\t// Embed cuei.Cue\n}\nfunc (cue2 *Cue2) Show() {        \t// Override Show\n\tfmt.Printf(\"%+v\",cue2.Command)\n}\n\nfunc main(){\n\tvar cue2 Cue2\n\tdata := \"/DA7AAAAAAAAAP/wFAUAAAABf+/+AItfZn4AKTLgAAEAAAAWAhRDVUVJAAAAAX//AAApMuABACIBAIoXZrM=\"\n        cue2.Decode(data) \n        cue2.Show()\n}\n\n```\n\n### `Call a shadowed method`\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/futzu/cuei\"\n)\n\ntype Cue2 struct {\n    cuei.Cue               \t\t// Embed cuei.Cue\n}\nfunc (cue2 *Cue2) Show() {        \t// Override Show\n\tfmt.Println(\"Cue2.Show()\")\n\tfmt.Printf(\"%+v\",cue2.Command) \n\tfmt.Println(\"\\n\\ncuei.Cue.Show() from cue2.Show()\")\n\tcue2.Cue.Show()\t\t\t// Call the Show method from embedded cuei.Cue\n}\n\nfunc main(){\n\tvar cue2 Cue2\n\tdata := \"/DA7AAAAAAAAAP/wFAUAAAABf+/+AItfZn4AKTLgAAEAAAAWAhRDVUVJAAAAAX//AAApMuABACIBAIoXZrM=\"\n        cue2.Decode(data) \n        cue2.Show()\n\t\n}\n\n\n```\n### `Use Dot notation to access SCTE-35 Cue values`\n```go\n\n/**\nShow  the packet PTS time and Splice Command Name of SCTE-35 Cues\nin a MPEGTS stream.\n**/\npackage main\n\nimport (\n\t\"os\"\n\t\"fmt\"\n\t\"github.com/futzu/cuei\"\n)\n\nfunc main() {\n\targ := os.Args[1]\n\tstream := cuei.NewStream()\n\tcues :=\tstream.Decode(arg)\n\tfor _,c := range cues {\n\t\tfmt.Printf(\"PTS: %v, Splice Command: %v\\n\",c.PacketData.Pts, c.Command.Name )\n\t}\n}\n\n```\n\n### `Load JSON and Encode`\n* cuei can accept SCTE-35 data as JSON and encode it to Base64, Bytes, or Hex string.\n* The function __cuei.Json2Cue()__ accepts SCTE-35 JSON as input and returns a *cuei.Cue\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/futzu/cuei\"\n)\n\nfunc main() {\n\n\tjs := `{\n    \"InfoSection\": {\n        \"Name\": \"Splice Info Section\",\n        \"TableID\": \"0xfc\",\n        \"SectionSyntaxIndicator\": false,\n        \"Private\": false,\n        \"Reserved\": \"0x3\",\n        \"SectionLength\": 42,\n        \"ProtocolVersion\": 0,\n        \"EncryptedPacket\": false,\n        \"EncryptionAlgorithm\": 0,\n        \"PtsAdjustment\": 0,\n        \"CwIndex\": \"0xff\",\n        \"Tier\": \"0xfff\",\n        \"CommandLength\": 15,\n        \"CommandType\": 5\n    },\n    \"Command\": {\n        \"Name\": \"Splice Insert\",\n        \"CommandType\": 5,\n        \"SpliceEventID\": 5690,\n        \"OutOfNetworkIndicator\": true,\n        \"ProgramSpliceFlag\": true,\n        \"TimeSpecifiedFlag\": true,\n        \"PTS\": 23683.480033\n    },\n    \"DescriptorLoopLength\": 10,\n    \"Descriptors\": [\n        {\n            \"Length\": 8,\n            \"Identifier\": \"CUEI\",\n            \"Name\": \"Avail Descriptor\"\n        }\n    ],\n    \"Crc32\": \"0xd7165c79\"\n}\n`\ncue :=  cuei.Json2Cue(js)    // \ncue.AdjustPts(28.0)   \t // Apply pts adjustment\nfmt.Println(\"\\nBytes:\\n\\t\", cue.Encode())\t// Bytes\nfmt.Println(\"\\nBase64:\\n\\t\",cue.Encode2B64())  \t// Base64\nfmt.Println(\"\\nHex:\\n\\t\",cue.Encode2Hex()) \t// Hex\n\n}\n\n\n```\n* Output\n```smalltalk\nBytes:\n\t[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\n\t0 0 0 0 0 10 0 8 67 85 69 73 0 0 0 0 236 139 53 78]\n\nBase64:\n\t /DAqAAAAJnPA///wDwUAABY6f8/+fwxPcwAAAAAACgAIQ1VFSQAAAADsizVO\n\nHex:\n\t 0xfc302a0000002673c0fffff00f050000163a7fcffe7f0c4f7300000000000a00084355454900000000ec8b354e\n\n```\n## cuei.Stream\n### `Custom Cue Handling for MPEGTS Streams`\n##### Four Steps\n1) Create Stream Instance\n2) Read Bytes from the video stream (__in multiples of 188__)\n3) Call Stream.DecodeBytes(Bytes) \n4) Process [] *Cue returned by Stream.DecodeBytes\n\n```go\npackage main\n\nimport (\n        \"fmt\"\n        \"github.com/futzu/cuei\"\n        \"os\"\n)\n\nfunc main() {\n\n  arg := os.Args[1]\n  stream := cuei.NewStream()  //   (1)\n  bufSize := 32768 * 188   // Always read in multiples of 188\n  file, err := os.Open(arg)\n  if err != nil {\n    fmt.Printf(\"Unable to read %v\\n\", arg)\n  }\n  buffer := make([]byte, bufSize)\n  for {\n  \t_, err := file.Read(buffer)   //  (2)\n\tif err != nil {\n\t\tbreak\n\t}\n\tcues := stream.DecodeBytes(buffer)  // (3)\n\n\tfor _, c := range cues {  //   (4)\n\t\tfmt.Printf(\" %v, %v\\n\", c.PacketData.Pts, c.Encode2B64())\n\t}\n  }\n}\n\n```\n* Output\n```php\n60638.745877, /DAWAAAAAAAAAP/wBQb/RUqw1AAAd6OnQA==\n 60638.745877, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60640.714511, /DAWAAAAAAAAAP/wBQb/RU1wqAAAoqaOaA==\n 60640.714511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60642.015811, /DAWAAAAAAAAAP/wBQb/RU9F4AAA9Te5ag==\n 60642.015811, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60642.749877, /DAWAAAAAAAAAP/wBQb/RVAwfAAAWOLrFQ==\n 60642.749877, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60644.718511, /DAWAAAAAAAAAP/wBQb/RVLwUAAAj7/Pgw==\n 60644.718511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60646.720511, /DAWAAAAAAAAAP/wBQb/RVWwJAAA8pm/jg==\n 60646.720511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60648.121911, /DAWAAAAAAAAAP/wBQb/RVec0gAAt0QzqA==\n 60648.121911, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60634.208011, /DAWAAAAAAAAAP/wBQb/RUR1fAAAik8gfQ==\n 60634.208011, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60634.675144, /DAWAAAAAAAAAP/wBQb/RUUxLAAABQVyEA==\n 60634.675144, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n 60636.710511, /DAWAAAAAAAAAP/wBQb/RUfxAAAA0lhWhg==\n 60636.710511, /DAgAAAAAAAAAP/wDwUAAAABf//+AFJlwAABAAAAAMOOklg=\n```\n\n### `Custom Cue Handling for MPEGTS Streams Over Multicast`\n##### Need a multicast sender? Try [gums](https://github.com/futzu/gums)\n\n\u003cdiv\u003e for multicast we use the same four steps,\u003cbr\u003e the only difference is we read the bytes from the network instead of a local file. \u003c/div\u003e\n\n1) Create Stream Instance\n2) Read Bytes from the video stream (__in multiples of 188__)\n3) Call Stream.DecodeBytes(Bytes) \n4) Process [] *Cue returned by Stream.DecodeBytes\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/futzu/cuei\"\n\t\"os\"\n        \"net\"\n)\n\n\nfunc main() {\n  arg := os.Args[1]\n  stream := cuei.NewStream()  //  (1)\n  stream.Quiet = true\n  dgram:=1316  // \u003c-- multicast dgram size is 1316 (188*7) for mpegts\n  bufSize := 100 * dgram\n  addr, _ := net.ResolveUDPAddr(\"udp\", arg)\n  l, _ := net.ListenMulticastUDP(\"udp\", nil, addr)  // Multicast Connection \n  l.SetReadBuffer(bufSize)\n  for {\n\tbuffer := make([]byte, bufSize)  // (2)\n\tl.ReadFromUDP(buffer)\n\tcues := stream.DecodeBytes(buffer)   //  (3)\n\tfor _, c := range cues {      //  (4)\n\t\tfmt.Printf(\" %v, %v\\n\", c.PacketData.Pts, c.Encode2B64())\n\t}\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffutzu%2Fcuei","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffutzu%2Fcuei","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffutzu%2Fcuei/lists"}