diff --git a/Readme.md b/Readme.md index 085af3a..6166b6f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,4 @@ -[![GoDoc](https://godoc.org/github.com/Comcast/gots?status.svg)](https://godoc.org/github.com/Comcast/gots) +[![Go Reference](https://pkg.go.dev/badge/github.com/Comcast/gots/v2.svg)](https://pkg.go.dev/github.com/Comcast/gots/v2) [![Build Status](https://travis-ci.org/Comcast/gots.svg?branch=master)](https://travis-ci.org/Comcast/gots) [![Go Report Card](https://goreportcard.com/badge/github.com/Comcast/gots)](https://goreportcard.com/report/github.com/Comcast/gots) [![Coverage Status](https://coveralls.io/repos/github/Comcast/gots/badge.svg?branch=master)](https://coveralls.io/github/Comcast/gots?branch=master) diff --git a/cli/parsefile.go b/cli/parsefile.go index 757d31c..4b6588d 100644 --- a/cli/parsefile.go +++ b/cli/parsefile.go @@ -32,11 +32,11 @@ import ( "io" "os" - "github.com/Comcast/gots/ebp" - "github.com/Comcast/gots/packet" - "github.com/Comcast/gots/packet/adaptationfield" - "github.com/Comcast/gots/psi" - "github.com/Comcast/gots/scte35" + "github.com/Comcast/gots/v2/ebp" + "github.com/Comcast/gots/v2/packet" + "github.com/Comcast/gots/v2/packet/adaptationfield" + "github.com/Comcast/gots/v2/psi" + "github.com/Comcast/gots/v2/scte35" ) // main parses a ts file that is provided with the -f flag diff --git a/ebp/cablelabsebp.go b/ebp/cablelabsebp.go index 1ed0992..b3ca48c 100644 --- a/ebp/cablelabsebp.go +++ b/ebp/cablelabsebp.go @@ -29,7 +29,7 @@ import ( "encoding/binary" "time" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // cableLabsEbp is an encoder boundary point diff --git a/ebp/comcastebp.go b/ebp/comcastebp.go index 9270cb3..f063375 100644 --- a/ebp/comcastebp.go +++ b/ebp/comcastebp.go @@ -29,7 +29,7 @@ import ( "encoding/binary" "time" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // cableLabsEbp is an encoder boundary point diff --git a/ebp/ebp.go b/ebp/ebp.go index adc5da4..22dd27e 100644 --- a/ebp/ebp.go +++ b/ebp/ebp.go @@ -28,7 +28,7 @@ import ( "encoding/binary" "time" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // EBP tags diff --git a/ebp/ebp_test.go b/ebp/ebp_test.go index 1ee9fcd..bb44a1a 100644 --- a/ebp/ebp_test.go +++ b/ebp/ebp_test.go @@ -27,7 +27,7 @@ import ( "testing" "time" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) var CableLabsEBPBytes = []byte{ diff --git a/go.mod b/go.mod index bdfd633..1b733fe 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,3 @@ -// Deprecated: use github.com/Comcast/gots/v2 instead. -module github.com/Comcast/gots +module github.com/Comcast/gots/v2 go 1.18 diff --git a/packet/accumulator.go b/packet/accumulator.go index 8218d24..27a324e 100644 --- a/packet/accumulator.go +++ b/packet/accumulator.go @@ -27,7 +27,7 @@ package packet import ( "bytes" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // Iotas to track the state of the accumulator diff --git a/packet/accumulator_test.go b/packet/accumulator_test.go index 71946d8..6b9c816 100644 --- a/packet/accumulator_test.go +++ b/packet/accumulator_test.go @@ -28,7 +28,7 @@ import ( "encoding/hex" "testing" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // PacketAccumulator is not thread safe diff --git a/packet/adaptationfield.go b/packet/adaptationfield.go index 01b2e62..18f8c07 100644 --- a/packet/adaptationfield.go +++ b/packet/adaptationfield.go @@ -25,7 +25,7 @@ SOFTWARE. package packet import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) const ( diff --git a/packet/adaptationfield/adaptationfield.go b/packet/adaptationfield/adaptationfield.go index 4590e23..bb4b31d 100644 --- a/packet/adaptationfield/adaptationfield.go +++ b/packet/adaptationfield/adaptationfield.go @@ -1,8 +1,8 @@ package adaptationfield import ( - "github.com/Comcast/gots" - "github.com/Comcast/gots/packet" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/packet" ) // Length returns the length of the adaptation field in bytes diff --git a/packet/create.go b/packet/create.go index a61c9d8..6ce6478 100644 --- a/packet/create.go +++ b/packet/create.go @@ -25,7 +25,7 @@ SOFTWARE. package packet import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) var ( diff --git a/packet/io.go b/packet/io.go index b863e03..f1b964e 100644 --- a/packet/io.go +++ b/packet/io.go @@ -28,7 +28,7 @@ import ( "encoding/binary" "io" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // Peeker wraps the Peek method. diff --git a/packet/modify.go b/packet/modify.go index e589b63..49ebb35 100644 --- a/packet/modify.go +++ b/packet/modify.go @@ -25,7 +25,7 @@ SOFTWARE. package packet import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // flags that are reserved and should not be used. diff --git a/packet/packet.go b/packet/packet.go index 9dc2b0e..9cc012f 100644 --- a/packet/packet.go +++ b/packet/packet.go @@ -25,7 +25,7 @@ SOFTWARE. package packet import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) const ( diff --git a/packet/packetwriter.go b/packet/packetwriter.go index 5ca95ba..bc6e100 100644 --- a/packet/packetwriter.go +++ b/packet/packetwriter.go @@ -27,7 +27,7 @@ package packet import ( "io" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // PacketWriter is subject to all rules governing implementations of io.Writer diff --git a/pes/pes.go b/pes/pes.go index e196b22..1215d3b 100644 --- a/pes/pes.go +++ b/pes/pes.go @@ -24,7 +24,7 @@ SOFTWARE. package pes -import "github.com/Comcast/gots/packet" +import "github.com/Comcast/gots/v2/packet" // AlignedPUSI checks for a PUSI with aligned flag set and returns a bool // indicating a match when true, as well as the bytes for the PES data diff --git a/pes/pesheader.go b/pes/pesheader.go index 247be73..c7f561c 100644 --- a/pes/pesheader.go +++ b/pes/pesheader.go @@ -28,7 +28,7 @@ import ( "errors" "fmt" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // stream_id possibilities diff --git a/pes/pesheader_test.go b/pes/pesheader_test.go index dac6dbd..105f301 100644 --- a/pes/pesheader_test.go +++ b/pes/pesheader_test.go @@ -27,7 +27,7 @@ import ( "encoding/hex" "testing" - "github.com/Comcast/gots/packet" + "github.com/Comcast/gots/v2/packet" ) func parseHexString(h string) *packet.Packet { diff --git a/psi/pat.go b/psi/pat.go index f44a4f5..2a3e14b 100644 --- a/psi/pat.go +++ b/psi/pat.go @@ -28,8 +28,8 @@ import ( "errors" "io" - "github.com/Comcast/gots" - "github.com/Comcast/gots/packet" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/packet" ) const ( diff --git a/psi/pmt.go b/psi/pmt.go index ba4f344..7f3847c 100644 --- a/psi/pmt.go +++ b/psi/pmt.go @@ -30,8 +30,8 @@ import ( "fmt" "io" - "github.com/Comcast/gots" - "github.com/Comcast/gots/packet" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/packet" ) const PidNotFound int = 1<<16 - 1 diff --git a/psi/pmt_test.go b/psi/pmt_test.go index 9fe78bc..4f5061f 100644 --- a/psi/pmt_test.go +++ b/psi/pmt_test.go @@ -30,8 +30,8 @@ import ( "fmt" "testing" - "github.com/Comcast/gots" - "github.com/Comcast/gots/packet" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/packet" ) func parseHexString(h string) *packet.Packet { diff --git a/psi/psi.go b/psi/psi.go index cba20c5..1fb23fb 100644 --- a/psi/psi.go +++ b/psi/psi.go @@ -25,7 +25,7 @@ SOFTWARE. package psi import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // TableHeader struct represents operations available on all PSI diff --git a/scte35/descriptormodify.go b/scte35/descriptormodify.go index c37d310..7dc0233 100644 --- a/scte35/descriptormodify.go +++ b/scte35/descriptormodify.go @@ -25,7 +25,7 @@ SOFTWARE. package scte35 import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // SetUPIDType will set the type of the UPID diff --git a/scte35/doc.go b/scte35/doc.go index ea97e93..830cb50 100644 --- a/scte35/doc.go +++ b/scte35/doc.go @@ -26,8 +26,8 @@ SOFTWARE. package scte35 import ( - "github.com/Comcast/gots" - "github.com/Comcast/gots/psi" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/psi" ) // SpliceCommandType is a type used to describe the types of splice commands. diff --git a/scte35/modify.go b/scte35/modify.go index 715671d..46927d7 100644 --- a/scte35/modify.go +++ b/scte35/modify.go @@ -25,8 +25,8 @@ SOFTWARE. package scte35 import ( - "github.com/Comcast/gots" - "github.com/Comcast/gots/psi" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/psi" ) // CreateSCTE35 creates a default SCTE35 message and returns it. diff --git a/scte35/modify_test.go b/scte35/modify_test.go index 7f982be..a0cff4a 100644 --- a/scte35/modify_test.go +++ b/scte35/modify_test.go @@ -26,8 +26,8 @@ package scte35 import ( "bytes" - "github.com/Comcast/gots" - "github.com/Comcast/gots/psi" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/psi" "testing" ) diff --git a/scte35/scte35.go b/scte35/scte35.go index 878269f..4c42e0a 100644 --- a/scte35/scte35.go +++ b/scte35/scte35.go @@ -29,8 +29,8 @@ import ( "encoding/binary" "fmt" - "github.com/Comcast/gots" - "github.com/Comcast/gots/psi" + "github.com/Comcast/gots/v2" + "github.com/Comcast/gots/v2/psi" ) // Descriptor tag types and identifiers - only segmentation descriptors are used for now diff --git a/scte35/scte35_test.go b/scte35/scte35_test.go index 6fae769..6b51abf 100644 --- a/scte35/scte35_test.go +++ b/scte35/scte35_test.go @@ -30,7 +30,7 @@ import ( "strings" "testing" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) var testScte = []byte{ diff --git a/scte35/segmentationdescriptor.go b/scte35/segmentationdescriptor.go index 6a2b948..2a85c6f 100644 --- a/scte35/segmentationdescriptor.go +++ b/scte35/segmentationdescriptor.go @@ -29,7 +29,7 @@ import ( "encoding/binary" "strings" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // upidSt is the struct used for creating a Multiple UPID (MID) diff --git a/scte35/splicecommand.go b/scte35/splicecommand.go index cab9161..0f8b809 100644 --- a/scte35/splicecommand.go +++ b/scte35/splicecommand.go @@ -28,7 +28,7 @@ import ( "bytes" "encoding/binary" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // timeSignal is a struct that represents a time signal splice command in SCTE35 diff --git a/scte35/splicecommandmodify.go b/scte35/splicecommandmodify.go index 2ecf66d..8f7ea0b 100644 --- a/scte35/splicecommandmodify.go +++ b/scte35/splicecommandmodify.go @@ -25,7 +25,7 @@ SOFTWARE. package scte35 import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // CreateSpliceInsertCommand will create a default SpliceInsertCommand. diff --git a/scte35/state.go b/scte35/state.go index 8c6dc21..07392bd 100644 --- a/scte35/state.go +++ b/scte35/state.go @@ -25,7 +25,7 @@ SOFTWARE. package scte35 import ( - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" "strings" ) diff --git a/scte35/state_test.go b/scte35/state_test.go index c2aff0c..04e91c3 100644 --- a/scte35/state_test.go +++ b/scte35/state_test.go @@ -28,7 +28,7 @@ import ( "fmt" "testing" - "github.com/Comcast/gots" + "github.com/Comcast/gots/v2" ) // All signal data generated with scte_creator: https://github.comcast.com/mniebu200/scte_creator diff --git a/v2/CODE_OF_CONDUCT.md b/v2/CODE_OF_CONDUCT.md deleted file mode 100644 index 4f1afd6..0000000 --- a/v2/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,74 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at Comcast_Open_Source_Services@comcast.com. -All complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/v2/CONTRIBUTING b/v2/CONTRIBUTING deleted file mode 100644 index f43e1b4..0000000 --- a/v2/CONTRIBUTING +++ /dev/null @@ -1,13 +0,0 @@ -## Contributing - -If you would like to contribute code to this project you can do so through GitHub by forking the repository and sending a pull request. Your code must meet the following requirements. - -- Include Unit tests in your PR to test the new behaviour. -- Include comments -- Run go vet to assure idiomatic go - -## Contributor License Agreement - -Before Comcast merges your code into the project you must sign the [Comcast Contributor License Agreement (CLA)](https://gist.github.com/ComcastOSS/a7b8933dd8e368535378cda25c92d19a). - -If you haven't previously signed a Comcast CLA, you'll automatically be asked to when you open a pull request. Alternatively, we can e-mail you a PDF that you can sign and scan back to us. Please send us an e-mail or create a new GitHub issue to request a PDF version of the CLA. diff --git a/v2/LICENSE b/v2/LICENSE deleted file mode 100644 index b135154..0000000 --- a/v2/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - diff --git a/v2/Makefile b/v2/Makefile deleted file mode 100644 index 2573707..0000000 --- a/v2/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -DEFAULT: test - -.PHONY: test -test: - go test -race `go list ./... | grep -v vendor` diff --git a/v2/NOTICE b/v2/NOTICE deleted file mode 100644 index f4b8a3e..0000000 --- a/v2/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Go Transport Stream Library -Copyright 2016 Comcast Cable Communications Management, LLC -This product includes software developed at Comcast (http://www.comcast.com/). diff --git a/v2/Readme.md b/v2/Readme.md deleted file mode 100644 index 085af3a..0000000 --- a/v2/Readme.md +++ /dev/null @@ -1,56 +0,0 @@ -[![GoDoc](https://godoc.org/github.com/Comcast/gots?status.svg)](https://godoc.org/github.com/Comcast/gots) -[![Build Status](https://travis-ci.org/Comcast/gots.svg?branch=master)](https://travis-ci.org/Comcast/gots) -[![Go Report Card](https://goreportcard.com/badge/github.com/Comcast/gots)](https://goreportcard.com/report/github.com/Comcast/gots) -[![Coverage Status](https://coveralls.io/repos/github/Comcast/gots/badge.svg?branch=master)](https://coveralls.io/github/Comcast/gots?branch=master) - - -# goTS (Go Transport Streams) - -gots (Go Transport Streams) is a library for working with MPEG transport streams. It provides abstractions for reading packet information and program specific information (psi) - -## Bug / Feature Reporting -Add requests to Github issues. To submit a PR see [CONTRIBUTING](./CONTRIBUTING) -## Tests -```bash -go test -race ./... -``` -Travis-CI will run these tests: - -```bash -go test -v ./... -``` -## License -This software is licensed under the MIT license. For full text see [LICENSE](./LICENSE) - -## Code of Conduct -We take our [code of conduct](CODE_OF_CONDUCT.md) very seriously. Please abide by it. - -## Examples -This is a simple example that extracts all PIDs from a ts file and prints them. [CLI example parser can be found here](cli/parsefile.go) -```go -func main() { - pidSet := make(map[uint16]bool, 5) - filename := "./scenario1.ts" - file, err := os.Open(filename) - if err == nil { - pkt := make([]byte, packet.PacketSize) - for read, err := file.Read(pkt); read > 0 && err == nil; read, err = file.Read(pkt) { - if err != nil { - println(err) - return - } - pid, err := packet.Pid(pkt) - if err != nil { - println(err) - continue - } - pidSet[pid] = true - } - - for v := range pidSet { - fmt.Printf("Found pid %d\n", v) - } - } else { - fmt.Printf("Unable to open file [%s] due to [%s]\n", filename, err.Error()) -} -``` diff --git a/v2/cli/.gitignore b/v2/cli/.gitignore deleted file mode 100644 index 573c0c4..0000000 --- a/v2/cli/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cli diff --git a/v2/cli/parsefile.go b/v2/cli/parsefile.go deleted file mode 100644 index 4b6588d..0000000 --- a/v2/cli/parsefile.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// package main contains CLI utilities for testing -package main - -import ( - "bufio" - "flag" - "fmt" - "io" - "os" - - "github.com/Comcast/gots/v2/ebp" - "github.com/Comcast/gots/v2/packet" - "github.com/Comcast/gots/v2/packet/adaptationfield" - "github.com/Comcast/gots/v2/psi" - "github.com/Comcast/gots/v2/scte35" -) - -// main parses a ts file that is provided with the -f flag -func main() { - fileName := flag.String("f", "", "Required: Path to TS file to read") - showPmt := flag.Bool("pmt", true, "Output PMT info") - showEbp := flag.Bool("ebp", false, "Output EBP info. This is a lot of info") - dumpSCTE35 := flag.Bool("scte35", false, "Output SCTE35 signals and info.") - showPacketNumberOfPID := flag.Int("pid", 0, "Dump the contents of the first packet encountered on PID to stdout") - flag.Parse() - if *fileName == "" { - flag.Usage() - return - } - tsFile, err := os.Open(*fileName) - if err != nil { - fmt.Printf("Cannot access test asset %s.\n", *fileName) - return - } - defer func(file *os.File) { - err := file.Close() - if err != nil { - fmt.Println("Cannot close File", file.Name(), err) - } - }(tsFile) - // Verify if sync-byte is present and seek to the first sync-byte - reader := bufio.NewReader(tsFile) - _, err = packet.Sync(reader) - if err != nil { - fmt.Println(err) - return - } - pat, err := psi.ReadPAT(reader) - if err != nil { - fmt.Println(err) - return - } - printPat(pat) - - var pmts []psi.PMT - pm := pat.ProgramMap() - for pn, pid := range pm { - pmt, err := psi.ReadPMT(reader, pid) - if err != nil { - panic(err) - } - pmts = append(pmts, pmt) - if *showPmt { - printPmt(pn, pmt) - } - } - - var pkt packet.Packet - var numPackets uint64 - ebps := make(map[uint64]ebp.EncoderBoundaryPoint) - scte35PIDs := make(map[int]bool) - if *dumpSCTE35 { - for _, pmt := range pmts { - for _, es := range pmt.ElementaryStreams() { - if es.StreamType() == psi.PmtStreamTypeScte35 { - scte35PIDs[es.ElementaryPid()] = true - break - } - - } - } - } - - for { - if _, err := io.ReadFull(reader, pkt[:]); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - break - } - fmt.Println(err) - return - } - numPackets++ - if *dumpSCTE35 { - currPID := packet.Pid(&pkt) - if scte35PIDs[currPID] { - pay, err := packet.Payload(&pkt) - if err != nil { - fmt.Printf("Cannot get payload for packet number %d on PID %d Error=%s\n", numPackets, currPID, err) - continue - } - msg, err := scte35.NewSCTE35(pay) - if err != nil { - fmt.Printf("Cannot parse SCTE35 Error=%v\n", err) - continue - } - printSCTE35(currPID, msg) - - } - - } - if *showEbp { - ebpBytes, err := adaptationfield.EncoderBoundaryPoint(&pkt) - if err != nil { - // Not an EBP - continue - } - boundaryPoint, err := ebp.ReadEncoderBoundaryPoint(ebpBytes) - if err != nil { - fmt.Printf("EBP construction error %v", err) - continue - } - ebps[numPackets] = boundaryPoint - fmt.Printf("Packet %d contains EBP %+v\n\n", numPackets, boundaryPoint) - } - if *showPacketNumberOfPID != 0 { - pid := *showPacketNumberOfPID - pktPid := packet.Pid(&pkt) - if pktPid == pid { - fmt.Printf("First Packet of PID %d contents: %x\n", pid, pkt) - break - } - } - } - fmt.Println() -} - -func printSCTE35(pid int, msg scte35.SCTE35) { - fmt.Printf("SCTE35 Message on PID %d\n", pid) - printSpliceCommand(msg.CommandInfo()) - - if insert, ok := msg.CommandInfo().(scte35.SpliceInsertCommand); ok { - printSpliceInsertCommand(insert) - } - for _, segdesc := range msg.Descriptors() { - printSegDesc(segdesc) - } - -} - -func printSpliceCommand(spliceCommand scte35.SpliceCommand) { - fmt.Printf("\tCommand Type %v\n", scte35.SpliceCommandTypeNames[spliceCommand.CommandType()]) - - if spliceCommand.HasPTS() { - fmt.Printf("\tPTS %v\n", spliceCommand.PTS()) - } -} - -func printSegDesc(segdesc scte35.SegmentationDescriptor) { - if segdesc.IsIn() { - fmt.Printf("\t<--- IN Segmentation Descriptor\n") - } - if segdesc.IsOut() { - fmt.Printf("\t---> OUT Segmentation Descriptor\n") - } - - fmt.Printf("\t\tEvent ID %d\n", segdesc.EventID()) - fmt.Printf("\t\tType %+v\n", scte35.SegDescTypeNames[segdesc.TypeID()]) - if segdesc.HasDuration() { - fmt.Printf("\t\t Duration %v\n", segdesc.Duration()) - } - -} - -func printSpliceInsertCommand(insert scte35.SpliceInsertCommand) { - fmt.Println("\tSplice Insert Command") - fmt.Printf("\t\tEvent ID %v\n", insert.EventID()) - - if insert.HasDuration() { - fmt.Printf("\t\tDuration %v\n", insert.Duration()) - } -} - -func printPmt(pn int, pmt psi.PMT) { - fmt.Printf("Program #%v PMT\n", pn) - fmt.Printf("\tPIDs %v\n", pmt.Pids()) - fmt.Println("\tElementary Streams") - - for _, es := range pmt.ElementaryStreams() { - fmt.Printf("\t\tPid %v: StreamType %v: %v\n", es.ElementaryPid(), es.StreamType(), es.StreamTypeDescription()) - - for _, d := range es.Descriptors() { - fmt.Printf("\t\t\t%+v\n", d) - } - } -} - -func printPat(pat psi.PAT) { - fmt.Println("Pat") - fmt.Printf("\tPMT PIDs %v\n", pat.ProgramMap()) - fmt.Printf("\tNumber of Programs %v\n", pat.NumPrograms()) -} diff --git a/v2/doc.go b/v2/doc.go deleted file mode 100644 index 2280f35..0000000 --- a/v2/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Package mpegts proves utility functions for mpegts streams -package gots diff --git a/v2/ebp/baseebp.go b/v2/ebp/baseebp.go deleted file mode 100644 index 5467c28..0000000 --- a/v2/ebp/baseebp.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package ebp - -import ( - "time" -) - -// FragmentFlag returns true if the fragment flag is set. -func (ebp *baseEbp) FragmentFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x80 != 0 -} - -// SetFragmentFlag sets the fragment flag. -func (ebp *baseEbp) SetFragmentFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x80 - } -} - -// SegmentFlag returns true if the segment flag is set. -func (ebp *baseEbp) SegmentFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x40 != 0 -} - -// SetSegmentFlag sets the segment flag. -func (ebp *baseEbp) SetSegmentFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x40 - } -} - -// SapFlag returns true if the sap flag is set. -func (ebp *baseEbp) SapFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x20 != 0 -} - -// SetSapFlag sets the sap flag. -func (ebp *baseEbp) SetSapFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x20 - } -} - -// GroupingFlag returns true if the grouping flag is set. -func (ebp *baseEbp) GroupingFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x10 != 0 -} - -// SetGroupingFlag sets the grouping flag. -func (ebp *baseEbp) SetGroupingFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x10 - } -} - -// TimeFlag returns true if the time flag is set. -func (ebp *baseEbp) TimeFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x08 != 0 -} - -// SetTimeFlag sets the time flag -func (ebp *baseEbp) SetTimeFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x08 - } -} - -// EBPTime returns the EBP time as a UTC time. -func (ebp *baseEbp) EBPTime() time.Time { - return extractUtcTime(ebp.TimeSeconds, ebp.TimeFraction) -} - -// SetEBPTime sets the time of the EBP. Takes UTC time as an input. -func (ebp *baseEbp) SetEBPTime(t time.Time) { - ebp.TimeSeconds, ebp.TimeFraction = insertUtcTime(t) -} - -// EBPSuccessReadTime defines when the EBP was read successfully. -func (ebp *baseEbp) EBPSuccessReadTime() time.Time { - return ebp.SuccessReadTime -} - -// Sap returns the sap of the EBP. -func (ebp *baseEbp) Sap() byte { - return ebp.SapType -} - -// SetSap sets the sap of the EBP. -func (ebp *baseEbp) SetSap(sapType byte) { - ebp.SapType = sapType -} - -// ExtensionFlag returns true if the extension flag is set. -func (ebp *baseEbp) ExtensionFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x01 != 0 -} - -// SetExtensionFlag sets the extension flag. -func (ebp *baseEbp) SetExtensionFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x01 - } -} - -// IsEmpty returns if the EBP is empty (zero length) -func (ebp *baseEbp) IsEmpty() bool { - return ebp.DataFieldLength == 0 -} - -// SetIsEmpty sets if the EBP is empty (zero length) -func (ebp *baseEbp) SetIsEmpty(value bool) { - if value { - ebp.DataFieldLength = 0 - } else { - ebp.DataFieldLength = 1 - } -} - -// StreamSyncSignal is used by some transcoder vendors to indicate the transcoders for a stream are in sync or not in sync. -// The packagers can read this signal and then determine what to do. Ideally they will always be in sync if the transcoders are configured correctly and healthy. -// StreamSyncSignal checks the Grouping ID field and returns the Stream Sync Signal -// Note: The intention of the Grouping ID was very broad at first and we have had many discussions over the years as to how to use it, -// but Stream Sync and SCTE-35 were the only two ever to get implemented. MK was the only Transcoder to implement SCTE-35 within the -// Grouping ID section and we never leveraged that data on any downstream devices. -func (ebp *baseEbp) StreamSyncSignal() uint8 { - for _, groupID := range ebp.Grouping { - if groupID == StreamSynchronized || groupID == StreamNotSynchronized { - return groupID - } - } - return InvalidStreamSyncSignal -} diff --git a/v2/ebp/cablelabsebp.go b/v2/ebp/cablelabsebp.go deleted file mode 100644 index b3ca48c..0000000 --- a/v2/ebp/cablelabsebp.go +++ /dev/null @@ -1,217 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package ebp - -import ( - "bytes" - "encoding/binary" - "time" - - "github.com/Comcast/gots/v2" -) - -// cableLabsEbp is an encoder boundary point -type cableLabsEbp struct { - baseEbp - FormatIdentifier uint32 - PartitionFlags uint8 -} - -// CreateCableLabsEbp returns a new cableLabsEbp with default values. -func CreateCableLabsEbp() cableLabsEbp { - return cableLabsEbp{ - baseEbp: baseEbp{ - DataFieldTag: CableLabsEbpTag, - DataFieldLength: 1, // not empty by default - }, - FormatIdentifier: CableLabsFormatIdentifier, - } -} - -// EBPtype returns the type (what is the format) of the EBP. -func (ebp *cableLabsEbp) EBPType() byte { - return ebp.DataFieldTag -} - -// ConcealmentFlag returns true if the concealment flag is set. -func (ebp *cableLabsEbp) ConcealmentFlag() bool { - return ebp.DataFlags&0x04 != 0 -} - -// SetConcealmentFlag sets the concealment flag. -func (ebp *cableLabsEbp) SetConcealmentFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x04 - } -} - -// SetPartitionFlag returns true if the partition flag. -func (ebp *cableLabsEbp) PartitionFlag() bool { - return ebp.ExtensionFlag() && ebp.ExtensionFlags&0x80 != 0 -} - -// SetPartitionFlag sets the partition flag. -func (ebp *cableLabsEbp) SetPartitionFlag(value bool) { - if ebp.ExtensionFlag() && value { - ebp.ExtensionFlags |= 0x80 - } -} - -func readCableLabsEbp(data []byte) (ebp *cableLabsEbp, err error) { - ebp = &cableLabsEbp{ - baseEbp: baseEbp{DataFieldTag: CableLabsEbpTag}, - } - - // We read 2 bytes not based on flags - if len(data) < 2 { - return nil, gots.ErrNoPayload - } - - index := uint8(0) - - ebp.DataFieldTag = data[index] - index += uint8(1) - - ebp.DataFieldLength = data[index] - index += uint8(1) - - // Check if the data is as advertised - if ebp.DataFieldLength > 0 { - if len(data) >= 7 { - ebp.FormatIdentifier = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) - - ebp.DataFlags = data[index] - index += uint8(1) - } else { - return nil, gots.ErrInvalidEBPLength - } - } - - if ebp.ExtensionFlag() { - ebp.ExtensionFlags = data[index] - index += uint8(1) - } - - if ebp.SapFlag() { - ebp.SapType = data[index] - index += uint8(1) - } - - if ebp.GroupingFlag() { - var group byte - var groupExtFlag bool - groupExtFlag = data[index]&0x80 != 0 - group = data[index] & 0x7F - ebp.Grouping = append(ebp.Grouping, group) - index += uint8(1) - - for groupExtFlag { - groupExtFlag = data[index]&0x80 != 0 - group = data[index] & 0x7F - ebp.Grouping = append(ebp.Grouping, group) - index += uint8(1) - } - } - - if ebp.TimeFlag() { - ebp.TimeSeconds = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) - - ebp.TimeFraction = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) - } - - if ebp.PartitionFlag() { - ebp.PartitionFlags = data[index] - index += uint8(1) - } - - if index < ebp.DataFieldLength+2 { - if int(ebp.DataFieldLength+2) > len(data) { - return nil, gots.ErrInvalidEBPLength - } - ebp.ReservedBytes = data[index : ebp.DataFieldLength+2] - } - - // update the successful read time - ebp.SuccessReadTime = time.Now() - - return ebp, nil -} - -// Data will return the raw bytes of the EBP -func (ebp *cableLabsEbp) Data() []byte { - requiredFields := new(bytes.Buffer) - data := new(bytes.Buffer) - binary.Write(requiredFields, ebpEncoding, ebp.DataFieldTag) - - if ebp.DataFieldLength == 0 { - return data.Bytes() - } - - binary.Write(data, ebpEncoding, ebp.FormatIdentifier) - - binary.Write(data, ebpEncoding, ebp.DataFlags) - if ebp.ExtensionFlag() { - binary.Write(data, ebpEncoding, ebp.ExtensionFlags) - } - - if ebp.SapFlag() { - binary.Write(data, ebpEncoding, ebp.SapType) - } - - if ebp.GroupingFlag() { - for i := range ebp.Grouping { - var group byte - if i < len(ebp.Grouping)-1 { - // add the ext flag for all but the last group - group = ebp.Grouping[i] | 0x80 - } else { - // last group - no ext flag - group = ebp.Grouping[i] - } - binary.Write(data, ebpEncoding, group) - } - } - - if ebp.TimeFlag() { - binary.Write(data, ebpEncoding, ebp.TimeSeconds) - binary.Write(data, ebpEncoding, ebp.TimeFraction) - } - - if ebp.PartitionFlag() { - binary.Write(data, ebpEncoding, ebp.PartitionFlags) - } - - binary.Write(data, ebpEncoding, ebp.ReservedBytes) - - ebp.DataFieldLength = uint8(data.Len()) - - binary.Write(requiredFields, ebpEncoding, ebp.DataFieldLength) - binary.Write(requiredFields, ebpEncoding, data.Bytes()) - - return requiredFields.Bytes() -} diff --git a/v2/ebp/cablelabsebp_test.go b/v2/ebp/cablelabsebp_test.go deleted file mode 100644 index b652d52..0000000 --- a/v2/ebp/cablelabsebp_test.go +++ /dev/null @@ -1,158 +0,0 @@ -/* -MIT License - -# Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package ebp - -import ( - "bytes" - "testing" - "time" -) - -func TestParseCableLabsEBP(t *testing.T) { - ebp, err := readCableLabsEbp(CableLabsEBPBytes) - if err != nil { - t.Errorf("readCableLabsEbp() returned not null error on valid EBP %v", err) - } - if ebp == nil { - t.Errorf("readCableLabsEbp() returned null EBP on valid EBP") - } - if !ebp.FragmentFlag() { - t.Errorf("readCableLabsEbp() has incorrect fragment flag") - } - if ebp.SegmentFlag() { - t.Errorf("readCableLabsEbp() has incorrect segment flag") - } - if !ebp.SapFlag() { - t.Errorf("readCableLabsEbp() has incorrect SAP flag") - } - if !ebp.GroupingFlag() { - t.Errorf("readCableLabsEbp() has incorrect grouping flag") - } - if !ebp.TimeFlag() { - t.Errorf("readCableLabsEbp() has incorrect time flag") - } - if !ebp.ConcealmentFlag() { - t.Errorf("readCableLabsEbp() has incorrect concealment flag") - } - if !ebp.ExtensionFlag() { - t.Errorf("readCableLabsEbp() has incorrect extension flag") - } - if !ebp.PartitionFlag() { - t.Errorf("readCableLabsEbp() has incorrect partition flag") - } - if ebp.EBPTime() != time.Unix(0, 1396964696553818999).UTC() { - t.Errorf("readCableLabsEbp() has incorrect time") - } - if ebp.Sap() != byte(0x02) { - t.Errorf("readCableLabsEbp() has incorrect SAP") - } - if len(ebp.Grouping) != 2 { - t.Errorf("readCableLabsEbp() has incorrect grouping, %v", ebp.Grouping) - } - if len(ebp.ReservedBytes) != 2 { - t.Errorf("readCableLabsEbp() has incorrect reserved, %v", ebp.ReservedBytes) - } - if ebp.StreamSyncSignal() != StreamSynchronized { - t.Errorf("readCableLabsEbp() has incorrect stream sync signal") - } - - ebp, err = readCableLabsEbp([]byte{0xff, 0xf3}) - if err == nil { - t.Errorf("readCableLabsEbp() returned null error %v on invalid EBP", err) - } - if ebp != nil { - t.Errorf("readCableLabsEbp() returned not null EBP on invalid EBP") - } -} - -func TestCableLabsEBP(t *testing.T) { - expected := CableLabsEBPBytes - ebp := CreateCableLabsEbp() - ebp.SetFragmentFlag(true) - ebp.SetConcealmentFlag(true) - ebp.SetExtensionFlag(true) - ebp.SetPartitionFlag(true) - ebp.SetSapFlag(true) - ebp.SapType = 0x02 - ebp.SetGroupingFlag(true) - ebp.Grouping = []byte{0x80, 0x1D} - ebp.SetTimeFlag(true) - ebp.SetEBPTime(time.Unix(0, 1396964696553818999).UTC()) - ebp.PartitionFlags = 0x03 - ebp.ReservedBytes = []byte{0x04, 0x05} - - generated := ebp.Data() - if !bytes.Equal(generated, expected) { - t.Errorf("Data() does not produce expected raw data\nExpected: %X\n Got: %X", - expected, - generated, - ) - } -} - -func TestParseCableLabsEBPWithMultipleGroups(t *testing.T) { - var ebpBytes = []byte{0xdf, 0x11, 0x45, 0x42, 0x50, 0x30, 0x98, 0x80, 0xa3, 0xfe, 0x1d, 0xe6, 0xa1, 0x45, 0x18, 0xb8, 0x51, 0x00, 0x00} - ebp, err := readCableLabsEbp(ebpBytes) - if err != nil { - t.Logf("failed to parse CableLabs-style EBP: #{err}") - t.FailNow() - } - if ebp.StreamSyncSignal() != StreamSynchronized { - t.Errorf("Wrong stream sync signal (expected 0x1D, got #{ebp.StreamSyncSignal()})") - } - var expectedGroups = []uint8{0x00, 0x23, 0x7E, 0x1D} - if len(ebp.Grouping) != len(expectedGroups) { - t.Logf("Wrong number of group IDs (expected #{len(expectedGroups)}, got #{len(ebp.Grouping)}") - t.FailNow() - } - for i, gid := range expectedGroups { - other := ebp.Grouping[i] - if gid != other { - t.Errorf("Wrong group ID (expected #{gid}, got #{other})") - } - } -} - -func TestParseCableLabsEBPWithSyncInMiddle(t *testing.T) { - var ebpBytes = []byte{0xdf, 0x12, 0x45, 0x42, 0x50, 0x30, 0x98, 0x80, 0xa3, 0xfe, 0x9d, 0x05, 0xe6, 0xa1, 0x45, 0x18, 0xb8, 0x51, 0x00, 0x00} - ebp, err := readCableLabsEbp(ebpBytes) - if err != nil { - t.Logf("failed to parse CableLabs-style EBP: #{err}") - t.FailNow() - } - if ebp.StreamSyncSignal() != StreamSynchronized { - t.Errorf("Wrong stream sync signal (expected 0x1D, got #{ebp.StreamSyncSignal()})") - } - var expectedGroups = []uint8{0x00, 0x23, 0x7E, 0x1D, 0x05} - if len(ebp.Grouping) != len(expectedGroups) { - t.Logf("Wrong number of group IDs (expected #{len(expectedGroups)}, got #{len(ebp.Grouping)}") - t.FailNow() - } - for i, gid := range expectedGroups { - other := ebp.Grouping[i] - if gid != other { - t.Errorf("Wrong group ID (expected #{gid}, got #{other})") - } - } -} diff --git a/v2/ebp/comcastebp.go b/v2/ebp/comcastebp.go deleted file mode 100644 index f063375..0000000 --- a/v2/ebp/comcastebp.go +++ /dev/null @@ -1,172 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package ebp - -import ( - "bytes" - "encoding/binary" - "time" - - "github.com/Comcast/gots/v2" -) - -// cableLabsEbp is an encoder boundary point -type comcastEbp struct { - baseEbp -} - -// CreateComcastEBP returns a new comcastEbp with default values. -func CreateComcastEBP() comcastEbp { - return comcastEbp{ - baseEbp: baseEbp{ - DataFieldTag: ComcastEbpTag, - DataFieldLength: 1, // not empty by default - }, - } -} - -// EBPtype returns the type (what is the format) of the EBP. -func (ebp *comcastEbp) EBPType() byte { - return ebp.DataFieldTag -} - -// DiscontinuityFlag returns true if the discontinuity flag is set. -func (ebp *comcastEbp) DiscontinuityFlag() bool { - return ebp.DataFieldLength != 0 && ebp.DataFlags&0x04 != 0 -} - -// SetDiscontinuityFlag sets the discontinuity flag. -func (ebp *comcastEbp) SetDiscontinuityFlag(value bool) { - if ebp.DataFieldLength != 0 && value { - ebp.DataFlags |= 0x04 - } -} - -// readComcastEbp will parse raw bytes without the tag into a Comcast EBP. -func readComcastEbp(data []byte) (ebp *comcastEbp, err error) { - ebp = &comcastEbp{ - baseEbp: baseEbp{DataFieldTag: ComcastEbpTag}, - } - - // We read 2 bytes not based on flags - if len(data) < 2 { - return nil, gots.ErrNoPayload - } - - index := uint8(0) - - ebp.DataFieldTag = data[index] - index += uint8(1) - - ebp.DataFieldLength = data[index] - index += uint8(1) - - // Check if the data is as advertised - if ebp.DataFieldLength > 0 { - if len(data) >= 3 { - ebp.DataFlags = data[index] - index += uint8(1) - } else { - return nil, gots.ErrInvalidEBPLength - } - } - - if ebp.ExtensionFlag() { - ebp.ExtensionFlags = data[index] - index += uint8(1) - } - - if ebp.SapFlag() { - ebp.SapType = data[index] - index += uint8(1) - } - - if ebp.GroupingFlag() { - group := data[index] - ebp.Grouping = append(ebp.Grouping, group) - index += uint8(1) - } - - if ebp.TimeFlag() { - ebp.TimeSeconds = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) - - ebp.TimeFraction = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) - } - - if index < ebp.DataFieldLength+2 { - if int(ebp.DataFieldLength+2) > len(data) { - return nil, gots.ErrInvalidEBPLength - } - ebp.ReservedBytes = data[index : ebp.DataFieldLength+2] - } - - // update the successful read time - ebp.SuccessReadTime = time.Now() - - return ebp, nil -} - -// Data will return the raw bytes of the EBP -func (ebp *comcastEbp) Data() []byte { - requiredFields := new(bytes.Buffer) - data := new(bytes.Buffer) - binary.Write(requiredFields, ebpEncoding, ebp.DataFieldTag) - - if ebp.DataFieldLength == 0 { - return data.Bytes() - } - - binary.Write(data, ebpEncoding, ebp.DataFlags) - - if ebp.ExtensionFlag() { - binary.Write(data, ebpEncoding, ebp.ExtensionFlags) - } - - if ebp.SapFlag() { - binary.Write(data, ebpEncoding, ebp.SapType) - } - - if ebp.GroupingFlag() { - for i := range ebp.Grouping { - binary.Write(data, ebpEncoding, ebp.Grouping[i]) - } - } - - if ebp.TimeFlag() { - binary.Write(data, ebpEncoding, ebp.TimeSeconds) - binary.Write(data, ebpEncoding, ebp.TimeFraction) - } - - binary.Write(data, ebpEncoding, ebp.ReservedBytes) - - ebp.DataFieldLength = uint8(data.Len()) - - binary.Write(requiredFields, ebpEncoding, ebp.DataFieldLength) - binary.Write(requiredFields, ebpEncoding, data.Bytes()) - - return requiredFields.Bytes() -} diff --git a/v2/ebp/comcastebp_test.go b/v2/ebp/comcastebp_test.go deleted file mode 100644 index bf19afb..0000000 --- a/v2/ebp/comcastebp_test.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package ebp - -import ( - "bytes" - "testing" - "time" -) - -func TestParseComcastEBP(t *testing.T) { - // Read a valid EBP - ebp, err := readComcastEbp(ComcastEBPBytes) - if err != nil { - t.Errorf("readComcastEbp() returned not null error on valid EBP %v", err) - } - if ebp == nil { - t.Errorf("readComcastEbp() returned null EBP on valid EBP") - } - if !ebp.FragmentFlag() { - t.Errorf("readComcastEbp() has incorrect fragment flag") - } - if ebp.SegmentFlag() { - t.Errorf("readComcastEbp() has incorrect segment flag") - } - if !ebp.SapFlag() { - t.Errorf("readComcastEbp() has incorrect SAP flag") - } - if !ebp.GroupingFlag() { - t.Errorf("readComcastEbp() has incorrect grouping flag") - } - if !ebp.TimeFlag() { - t.Errorf("readComcastEbp() has incorrect time flag") - } - if !ebp.DiscontinuityFlag() { - t.Errorf("readComcastEbp() has incorrect disc flag") - } - if !ebp.ExtensionFlag() { - t.Errorf("readComcastEbp() has incorrect extension flag") - } - if ebp.EBPTime() != time.Unix(0, 1396964696553818999).UTC() { - t.Errorf("readComcastEbp() has incorrect time") - } - if ebp.ExtensionFlags != byte(0x01) { - t.Errorf("readComcastEbp() has incorrect extension") - } - if ebp.Sap() != byte(0x02) { - t.Errorf("readComcastEbp() has incorrect SAP") - } - if ebp.Grouping[0] != byte(0x03) { - t.Errorf("readComcastEbp() has incorrect grouping") - } - if ebp.StreamSyncSignal() != InvalidStreamSyncSignal { - t.Errorf("readComcastEbp() has incorrect stream sync signal") - } - - // Read an EBP with 0 length - ebp, err = readComcastEbp([]byte{ComcastEbpTag, 0x00}) - if err != nil { - t.Errorf("readComcastEbp() returned error %v on valid EBP", err) - } - if ebp == nil { - t.Errorf("readComcastEbp() returned not null EBP on valid EBP") - } - - // Read an EBP with an invalid length - ebp, err = readComcastEbp([]byte{ComcastEbpTag, 0xff}) - if err == nil { - t.Errorf("readComcastEbp() returned null error %v on invalid EBP", err) - } - if ebp != nil { - t.Errorf("readComcastEbp() returned not null EBP on invalid EBP") - } -} - -func TestCreateComcastEBP(t *testing.T) { - expected := ComcastEBPBytes - ebp := CreateComcastEBP() - ebp.SetFragmentFlag(true) - ebp.SetDiscontinuityFlag(true) - ebp.SetExtensionFlag(true) - ebp.ExtensionFlags = 0x01 - ebp.SetSapFlag(true) - ebp.SapType = 0x02 - ebp.SetGroupingFlag(true) - ebp.Grouping = []byte{0x03} - ebp.SetTimeFlag(true) - ebp.SetEBPTime(time.Unix(0, 1396964696553818999).UTC()) - ebp.ReservedBytes = []byte{0x04, 0x05} - - generated := ebp.Data() - if !bytes.Equal(generated, expected) { - t.Errorf("Data() does not produce expected raw data\nExpected: %X\n Got: %X", - expected, - generated, - ) - } -} diff --git a/v2/ebp/doc.go b/v2/ebp/doc.go deleted file mode 100644 index 60a3cfb..0000000 --- a/v2/ebp/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Package ebp is used to detect and extract EBP information. EBP information is stored in a packet adaptation field in the private data section. -package ebp diff --git a/v2/ebp/ebp.go b/v2/ebp/ebp.go deleted file mode 100644 index 22dd27e..0000000 --- a/v2/ebp/ebp.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package ebp - -import ( - "encoding/binary" - "time" - - "github.com/Comcast/gots/v2" -) - -// EBP tags -const ( - ComcastEbpTag = uint8(0xA9) - CableLabsEbpTag = uint8(0xDF) - CableLabsFormatIdentifier = 0x45425030 -) - -// Stream Sync constants -const ( - InvalidStreamSyncSignal = uint8(0xFF) - StreamNotSynchronized = uint8(0x1C) - StreamSynchronized = uint8(0x1D) -) - -var ebpEncoding = binary.BigEndian - -// EncoderBoundaryPoint represents shared operations available on an all EBPs. -type EncoderBoundaryPoint interface { - // SegmentFlag returns true if the segment flag is set. - SegmentFlag() bool - // SetSegmentFlag sets the segment flag. - SetSegmentFlag(bool) - // FragmentFlag returns true if the fragment flag is set. - FragmentFlag() bool - // SetFragmentFlag sets the fragment flag. - SetFragmentFlag(bool) - // TimeFlag returns true if the time flag is set. - TimeFlag() bool - // SetTimeFlag sets the time flag - SetTimeFlag(bool) - // GroupingFlag returns true if the grouping flag is set. - GroupingFlag() bool - // SetGroupingFlag sets the grouping flag. - SetGroupingFlag(bool) - // EBPTime returns the EBP time as a UTC time. - EBPTime() time.Time - // SetEBPTime sets the time of the EBP. Takes UTC time as an input. - SetEBPTime(time.Time) - // EBPSuccessReadTime defines when the EBP was read successfully. - EBPSuccessReadTime() time.Time - // SapFlag returns true if the sap flag is set. - SapFlag() bool - // SetSapFlag sets the sap flag. - SetSapFlag(bool) - // Sap returns the sap of the EBP. - Sap() byte - // SetSap sets the sap of the EBP. - SetSap(byte) - // ExtensionFlag returns true if the extension flag is set. - ExtensionFlag() bool - // SetExtensionFlag sets the extension flag. - SetExtensionFlag(bool) - // EBPtype returns the type (what is the format) of the EBP. - EBPType() byte - // IsEmpty returns if the EBP is empty (zero length) - IsEmpty() bool - // SetIsEmpty sets if the EBP is empty (zero length) - SetIsEmpty(bool) - // StreamSyncSignal returns Stream Sync byte - StreamSyncSignal() uint8 - // Data will return the raw bytes of the EBP - Data() []byte -} - -// baseEbp the base struct that is embedded in every ebp in gots -type baseEbp struct { - DataFieldTag uint8 - DataFieldLength uint8 - DataFlags uint8 - ExtensionFlags uint8 - SapType uint8 - - TimeSeconds uint32 - TimeFraction uint32 - - ReservedBytes []uint8 - SuccessReadTime time.Time - - Grouping []uint8 -} - -// ReadEncoderBoundaryPoint parses and creates an EncoderBoundaryPoint from the given -// reader. If the bytes do not conform to a know EBP type, an error is returned. -func ReadEncoderBoundaryPoint(data []byte) (ebp EncoderBoundaryPoint, err error) { - if len(data) == 0 { - return nil, gots.ErrNoEBPData - } - - switch data[0] { - case ComcastEbpTag: - ebp, err = readComcastEbp(data) - case CableLabsEbpTag: - ebp, err = readCableLabsEbp(data) - default: - ebp, err = nil, gots.ErrUnrecognizedEbpType - } - - return ebp, err -} - -func extractUtcTime(seconds uint32, fraction uint32) time.Time { - nanos := uint64(seconds) * 1e9 - nanos += (uint64(fraction) * 1e9) >> 32 // truncateing off 3 or 2 bits. - - if seconds&0x80000000 != 0 { // if MSB set - // If bit 0 is set, the UTC time is in the range 1968-2036 and UTC - // time is reckoned from 0h 0m 0s UTC on 1 January 1900. - return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanos)) - } - // If bit 0 is not set, the time is in the range 2036-2104 and UTC - // time is reckoned from 6h 28m 16s UTC on 7 February 2036. - return time.Date(2036, 2, 7, 6, 28, 16, 0, time.UTC).Add(time.Duration(nanos)) - -} - -func insertUtcTime(t time.Time) (seconds uint32, fraction uint32) { - var startingTime time.Time - if t.Before(time.Date(2036, 2, 7, 6, 28, 16, 0, time.UTC)) { // greater than or equal to - startingTime = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) - } else { - startingTime = time.Date(2036, 2, 7, 6, 28, 16, 0, time.UTC) - } - nanos := uint64(t.Sub(startingTime).Nanoseconds()) - seconds = uint32(nanos / 1e9) - // running extractUtcTime and then insertUtcTime will produce - // different results because of rounding that heppns twice. - // 1 is added to avoid truncating the second time. - fraction = uint32((((nanos % 1e9) + 1) << 32) / 1e9) - return -} diff --git a/v2/ebp/ebp_test.go b/v2/ebp/ebp_test.go deleted file mode 100644 index bb44a1a..0000000 --- a/v2/ebp/ebp_test.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package ebp - -import ( - "testing" - "time" - - "github.com/Comcast/gots/v2" -) - -var CableLabsEBPBytes = []byte{ - 0xDF, // Tag - 0x14, // Length - 0x45, 0x42, 0x50, 0x30, // Format Identifier - 0xBD, // Flags - 0x80, // ExtensionFlags - 0x02, // SapFlags - 0x80, 0x1D, // GroupingFlag - 0xD6, 0xEE, 0x7B, 0xD8, // Time Second - 0x8D, 0xC7, 0x14, 0xFC, // Time Fraction - 0x03, // Partition - 0x04, 0x05} - -var ComcastEBPBytes = []byte{ - 0xA9, // Tag - 0x0E, // Length - 0xBD, // Flags - 0x01, // Extensions - 0x02, // SAP - 0x03, // Grouping - 0xD6, 0xEE, 0x7B, 0xD8, // Time Second - 0x8D, 0xC7, 0x14, 0xFC, // Time Fraction - 0x04, 0x05} // Reserved - -func TestReadEncoderBoundaryPoint(t *testing.T) { - ebp, err := ReadEncoderBoundaryPoint(CableLabsEBPBytes) - if err != nil { - t.Errorf("ReadEncoderBoundaryPoint() returned error on valid: %v", err) - } - if ebp.EBPType() != CableLabsEbpTag { - t.Errorf("ReadEncoderBoundaryPoint() read wrong type of EBP") - } - - ebp, err = ReadEncoderBoundaryPoint(ComcastEBPBytes) - if err != nil { - t.Errorf("ReadEncoderBoundaryPoint() returned error on valid: %v", err) - } - if ebp.EBPType() != ComcastEbpTag { - t.Errorf("ReadEncoderBoundaryPoint() read wrong type of EBP") - } - - ebp, err = ReadEncoderBoundaryPoint([]byte{0xAB}) - if err != gots.ErrUnrecognizedEbpType { - t.Errorf("ReadEncoderBoundaryPoint() read wrong type of EBP") - } - - ebp, err = ReadEncoderBoundaryPoint([]byte{}) - if err != gots.ErrNoEBPData { - t.Errorf("ReadEncoderBoundaryPoint() should have returned gots.ErrNoEBPData") - } -} - -func TestExtractUtcTime(t *testing.T) { - s := uint32(0xD6EE7BD8) - f := uint32(0x8DC714FC) - got := extractUtcTime(s, f) - want := time.Unix(0, 1396964696553818999).UTC() - if want != got { - t.Errorf("TestUtcTime(), want=%v, got=%v, nanos=%d", want, got, got.UnixNano()) - } -} - -func TestInsertUtcTime(t *testing.T) { - s0 := uint32(0xD6EE7BD8) - f0 := uint32(0x8DC714FC) - want := time.Unix(0, 1396964696553818999).UTC() - s1, f1 := insertUtcTime(want) - if s0 != s1 { - t.Errorf("secondsInserted=%d, secondsExpected=%d\n", s1, s0) - } - if f0 != f1 { - t.Errorf("fractionInserted=%d, fractionExpected=%d\n", f1, f0) - } - -} - -// TODO TestUtcTimeAfter2036 diff --git a/v2/errors.go b/v2/errors.go deleted file mode 100644 index 51abd69..0000000 --- a/v2/errors.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package gots - -import "errors" - -var ( - // ErrBadSyncByte is returned when the sync byte (first byte of packet) is not valid - ErrBadSyncByte = errors.New("sync byte is not valid") - // ErrUnrecognizedEbpType is returned if the EBP cannot be parsed - ErrUnrecognizedEbpType = errors.New("unrecognized EBP") - // ErrNoEBP is returned when an attempt is made to extract an EBP from a packet that does not contain one - ErrNoEBP = errors.New("packet does not contain EBP") - // ErrNoEBPData is returned when an attempt is made to extract an EBP data that does not contain one - ErrNoEBPData = errors.New("empty data provided to EBP parser") - // ErrInvalidEBPLength is returned when an attempt is made to extract an EBP from a packet that contains an invalid amount of data - ErrInvalidEBPLength = errors.New("invalid EBP data length") - // ErrInvalidPacketLength denotes an packet length that is not packet.PacketSize bytes in length - ErrInvalidPacketLength = errors.New("invalid packet length") - // ErrInvalidTSCFlag is returned when the transport scrambling control bit is set to the bit reserved by the specification - ErrInvalidTSCFlag = errors.New("invalid transport scrambling control option.") - // ErrInvalidAFCFlag is returned when the adaptation field control bits dont have a payload or an adaptation field - ErrInvalidAFCFlag = errors.New("invalid packet length") - // ErrNoPayload denotes that the attempted operation is not valid on a packet with no payload - ErrNoPayload = errors.New("packet does not contain payload") - // ErrNoAdaptationField is returned if the adaptation field cannot be accessed or does not exist. - ErrNoAdaptationField = errors.New("packet does not contain an adaptation field") - // ErrAdaptationFieldTooLarge is returned if the adaptation field is too large to be put in a packet. - ErrAdaptationFieldTooLarge = errors.New("adaptation field is too large and cannot shrink") - // ErrAdaptationFieldCannotGrow is returned if the adaptation field will overwrite the payload if it grows. - ErrAdaptationFieldCannotGrow = errors.New("adaptation field cannot cannot grow beyond its allocated length") - // ErrAdaptationFieldZeroLength is returned if the adaptation field is empty and only used for stuffing. - ErrAdaptationFieldZeroLength = errors.New("adaptation field is empty") - // ErrNoPrivateTransportData is returned when an attempt is made to access private transport data when none exists - ErrNoPrivateTransportData = errors.New("adaptation field has no private transport data") - // ErrNoSplicePoint is returned when an attempt to access a splice countdown and no splice point exists - ErrNoSplicePoint = errors.New("adaptation field has no splice point") - // ErrNoPCR is returned when an attempt is made to access adaptation field PRC that does not exist - ErrNoPCR = errors.New("adaptation field has no Program Clock Reference") - // ErrNoOPCR is returned when an attempt is made to access an adaptation field OPCR that does not exist - ErrNoOPCR = errors.New("adaptation field has no Original Program Clock Reference") - // ErrNoAdaptationFieldExtension is returned when an attempt is made to access adaptation field's - // Adaptation Field Extension when it does not exist - ErrNoAdaptationFieldExtension = errors.New("adaptation field has no Adaptation Field Extension") - // ErrPATNotFound is returned when expected PAT packet is not found when - // reading TS packets. - ErrPATNotFound = errors.New("No PAT was found while reading TS") - // ErrPMTNotFound is returned when expected PMT packet(s) are not found when - // reading TS packets. - ErrPMTNotFound = errors.New("No PMT was found while reading TS") - // ErrPMTParse is returned when malformed PMT data is present - ErrPMTParse = errors.New("PMT malformed.") - // ErrParsePMTDescriptor is returned when a PMT descriptor cannot be parsed - ErrParsePMTDescriptor = errors.New("unable to parse PMT descriptor") - // ErrInvalidPATLength is returned when a PAT cannot be parsed because there are not enough bytes - ErrInvalidPATLength = errors.New("too few bytes to parse PAT") - // ErrNoPayloadUnitStartIndicator should be returned when a packet is expected to have a PUSI and does not. - ErrNoPayloadUnitStartIndicator = errors.New("packet does not have payload unit start indicator") - // ErrUnknownTableID is returned when PSI is parsed with an unknown table id - ErrUnknownTableID = errors.New("Unknown table id received") - // ErrTableHeaderShort is returned when a PSI table header is too short to parse - ErrShortPayload = errors.New("provided data is too short to parse") - // ErrInvalidSCTE35Length is returned when a SCTE35 cue cannot be parsed because there are not enough bytes - ErrInvalidSCTE35Length = errors.New("too few bytes to parse SCTE35") - // ErrSCTE35EncryptionUnsupported is returned when a scte35 cue cannot be parsed because it is encrypted - ErrSCTE35EncryptionUnsupported = errors.New("SCTE35 is encrypted, which is not supported") - // ErrSCTE35UnsupportedSpliceCommand is returned when a SCTE35 cue - // cannot be parsed because the command type is not supported - ErrSCTE35UnsupportedSpliceCommand = errors.New("SCTE35 cue can't be parsed because only time_signal with a pts value and splice_null commands are supported") - // ErrSCTE35InvalidDescriptorID is returned when a segmentation descriptor is found with an id that is not CUEI - ErrSCTE35InvalidDescriptorID = errors.New("SCTE35 segmentation descriptor has a id that is not \"CUEI\"") - // ErrSCTE35DuplicateSignal is returned when a duplicate or equivalent descriptor is received by state - ErrSCTE35DuplicateDescriptor = errors.New("Duplicate or equivalent descriptor received by scte35.State") - // ErrSCTE35InvalidDescriptor is returned when a descriptor is invalid given the current state (i.e. a ProgramResumption received when no it breakaway) - ErrSCTE35InvalidDescriptor = errors.New("Invalid descriptor given the current state") - // ErrSCTE35MissingOut is returned when an in descriptor is received by state with no matching out - ErrSCTE35MissingOut = errors.New("In descriptor received with no matching out") - // ErrSCTE35DescriptorNotFound is returned when a descriptor is closed that's not in the open list - ErrSCTE35DescriptorNotFound = errors.New("Cannot close descriptor that's not in the open list") - // ErrNilPAT is returned when a PAT is passed into a function for which it cannot be nil. - ErrNilPAT = errors.New("Nil PAT not allowed here.") - // ErrSyncByteNotFound is returned when a packet sync byte could not be found - // when reading. - ErrSyncByteNotFound = errors.New("Sync-byte not found.") - // ErrVSSSignalIdNotFound is returned when we do not find SignalID in the VSS signal's MID. - ErrVSSSignalIdNotFound = errors.New("VSS Signal ID not found in the VSS signal received.") - // ErrPIDNotInPMT - ErrPIDNotInPMT = errors.New("PID(s) %d not found in PMT.") - // ErrDone signals an accumulator is done accumulating - ErrAccumulatorDone = errors.New("Accumulation is complete.") - // ErrInvalidState should be unreachable but is returned if the accumulator has reached an invalid state - ErrAccumulatorInvalidState = errors.New("Accumulator is in an invalid state.") -) diff --git a/v2/go.mod b/v2/go.mod deleted file mode 100644 index 1b733fe..0000000 --- a/v2/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/Comcast/gots/v2 - -go 1.18 diff --git a/v2/go.sum b/v2/go.sum deleted file mode 100644 index e69de29..0000000 diff --git a/v2/packet/accumulator.go b/v2/packet/accumulator.go deleted file mode 100644 index 27a324e..0000000 --- a/v2/packet/accumulator.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "bytes" - - "github.com/Comcast/gots/v2" -) - -// Iotas to track the state of the accumulator -const ( - stateStarting = iota - stateAccumulating - stateDone -) - -// Accumulator is used to gather multiple packets -// and return their concatenated payloads. -// Accumulator is not thread safe. -type Accumulator interface { - // WritePacket adds a packet to the accumulator and returns got.ErrAccumulatorDone if done - WritePacket(*Packet) (int, error) - // Bytes returns the payload bytes from the underlying buffer - Bytes() []byte - // Packets returns the packets used to fill the payload buffer - Packets() []*Packet - // Reset resets the accumulator state - Reset() -} -type accumulator struct { - f func([]byte) (bool, error) - buf *bytes.Buffer - packets []*Packet - state int -} - -// NewAccumulator creates a new packet accumulator that is done when -// the provided function returns done as true. -func NewAccumulator(f func(data []byte) (done bool, err error)) Accumulator { - return &accumulator{ - f: f, - buf: &bytes.Buffer{}, - packets: []*Packet{}, - state: stateStarting} -} - -// Add a packet to the accumulator. If the added packet completes -// the accumulation, based on the provided doneFunc, gots.ErrAccumulatorDone is returned. -// Returns an error if the packet is not valid. -func (a *accumulator) WritePacket(pkt *Packet) (int, error) { - switch a.state { - case stateStarting: - // need to check if the packet contains a payloadUnitStartIndicator to start - if !PayloadUnitStartIndicator(pkt) { - return PacketSize, gots.ErrNoPayloadUnitStartIndicator - } - - a.buf.Reset() - a.packets = a.packets[:0] - a.state = stateAccumulating - - case stateAccumulating: - // need to check if the packet contains a payloadUnitStartIndicator so we know - // to drop old packets and start re-accumulation - if PayloadUnitStartIndicator(pkt) { - a.state = stateStarting - return a.WritePacket(pkt) - } - - case stateDone: - return 0, gots.ErrAccumulatorDone - } - - var cpyPkt = &Packet{} - copy(cpyPkt[:], pkt[:]) - a.packets = append(a.packets, cpyPkt) - - if b, err := Payload(pkt); err != nil { - return PacketSize, err - } else if _, err := a.buf.Write(b); err != nil { - return PacketSize, err - } - - if done, err := a.f(a.buf.Bytes()); err != nil { - return PacketSize, err - } else if done { - a.state = stateDone - return PacketSize, gots.ErrAccumulatorDone - } - - return PacketSize, nil -} - -// Bytes returns the payload bytes from the underlying buffer -func (a *accumulator) Bytes() []byte { - b := make([]byte, a.buf.Len()) - copy(b, a.buf.Bytes()) - - return b -} - -// Packets returns the packets used to fill the payload buffer -// NOTE: Not thread safe -func (a *accumulator) Packets() []*Packet { - b := make([]*Packet, len(a.packets)) - copy(b, a.packets) - - return b -} - -// Reset resets the accumulator state -func (a *accumulator) Reset() { - a.state = stateStarting - a.buf.Reset() - a.packets = a.packets[:0] -} diff --git a/v2/packet/accumulator_test.go b/v2/packet/accumulator_test.go deleted file mode 100644 index 6b9c816..0000000 --- a/v2/packet/accumulator_test.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "encoding/hex" - "testing" - - "github.com/Comcast/gots/v2" -) - -// PacketAccumulator is not thread safe -// For a PSI specific example, see psi.PmtAccumulatorDoneFunc(PacketAccumulator) -func TestPacketAccumulator(t *testing.T) { - b, _ := hex.DecodeString("474064100002b0ba0001c10000e065f00b0504435545490e03c03dd01be065f016970028046400283fe907108302808502800e03c0392087e066f0219700050445414333cc03c0c2100a04656e6700e907108302808502800e03c000f087e067f0219700050445414333cc03c0c4100a0473706100e907108302808502800e03c001e00fe068f01697000a04656e6700e907108302808502800e03c000f00fe069f01697000a0473706100e907108302808502800e03c000f086e0dc") - firstPacket := &Packet{} - copy(firstPacket[:], b) - b, _ = hex.DecodeString("47006411f0002b59bc22ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - secondPacket := &Packet{} - copy(secondPacket[:], b) - - var called = false - - packets := []*Packet{firstPacket, secondPacket} - // Just a simple func to accumulate two packets - dFunc := func(b []byte) (bool, error) { - if len(b) <= PacketSize { - return false, nil - } - - called = true - - return true, nil - } - - acc := NewAccumulator(dFunc) - for _, pkt := range packets { - _, err := acc.WritePacket(pkt) - if err == gots.ErrAccumulatorDone { - // Accumulation is done - break - } else if err != nil { - t.Errorf("Unexpected accumulator error: %s", err) - } - } - - if !called { - t.Error("Expected Accumulator doneFunc to be called") - } -} diff --git a/v2/packet/adaptationfield.go b/v2/packet/adaptationfield.go deleted file mode 100644 index 18f8c07..0000000 --- a/v2/packet/adaptationfield.go +++ /dev/null @@ -1,595 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "github.com/Comcast/gots/v2" -) - -const ( - PayloadFlag AdaptationFieldControlOptions = 1 // 10 - AdaptationFieldFlag AdaptationFieldControlOptions = 2 // 01 - PayloadAndAdaptationFieldFlag AdaptationFieldControlOptions = 3 // 11 -) - -// AdaptationField is an optional part of the packet. -type AdaptationField Packet - -// AdaptationFieldControlOptions is a set of constants for -// selecting the adaptation field control. -type AdaptationFieldControlOptions byte - -// NewPacket creates a new packet with a Null ID, sync byte, and with the adaptation field control set to payload only. -// This function is error free. -func NewAdaptationField() *AdaptationField { - p := New() - p.SetAdaptationFieldControl(AdaptationFieldFlag) - af, _ := p.AdaptationField() - return af -} - -// setBit sets a bit in the adaptation field -// index is the byte in the packet where the bit is located -// mask is the bitmask that has that bit set to one and the rest of the bits set to zero -// value is the value that the bit will be set to -func (af *AdaptationField) setBit(index int, mask byte, value bool) { - if value { - af[index] |= mask - } else { - af[index] &= ^mask - } -} - -// getBit gets a bit in the adaptation field -// index is the byte in the packet where the bit is located -// mask is the bitmask that has that bit set to one and the rest of the bits set to zero -// value is the value of that bit in the adaptation field -func (af *AdaptationField) getBit(index int, mask byte) bool { - return af[index]&mask != 0 -} - -// bitDelta returns the difference of a bit (boolean) and the bit that is -// currently set in the adaptation field. -// 1 is returned if the packet is changed from 0 to 1. -// -1 is returned if the bit is changed from 1 to 0. -// 0 is returned if the bit is unchanged. -// this can be used to find if a field is growing or shrinking. -func (af *AdaptationField) bitDelta(index int, mask byte, value bool) int { - if value == af.getBit(index, mask) { - return 0 // same - } - if value { - return 1 // growing - } - return -1 // shrinking -} - -// valid returns any errors that prevent the AdaptationField from being valid. -func (af *AdaptationField) valid() error { - if !af.getBit(3, 0x20) { - return gots.ErrNoAdaptationField - } - if af[4] == 0 { - return gots.ErrAdaptationFieldZeroLength - } - return nil -} - -// initAdaptationField initializes the adaptation field to have all false flags -// it will also occupy the remainder of the packet. -func initAdaptationField(p *Packet) { - af := (*AdaptationField)(p) - af[4] = 183 // adaptation field will take up the rest of the packet by Default - af[5] = 0x00 // no flags are set by default - for i := 6; i < len(af); i++ { - af[i] = 0xFF - } -} - -// returns if the adaptation field has a PCR, this does not check for errors. -func (af *AdaptationField) hasPCR() bool { - return af.getBit(5, 0x10) -} - -// returns if the adaptation field has an OPCR, this does not check for errors. -func (af *AdaptationField) hasOPCR() bool { - return af.getBit(5, 0x08) -} - -// returns the adaptation field's Splicing Point flag, this does not check for errors. -func (af *AdaptationField) hasSplicingPoint() bool { - return af.getBit(5, 0x04) -} - -// returns if the adaptation field has a Transport Private Data, this does not check for errors. -func (af *AdaptationField) hasTransportPrivateData() bool { - return af.getBit(5, 0x02) -} - -// returns if the adaptation field has an adaptation field extension, this does not check for errors. -func (af *AdaptationField) hasAdaptationFieldExtension() bool { - return af.getBit(5, 0x01) -} - -// pcrLength returns the length of the PCR, if there is no PCR then its length is zero -func (af *AdaptationField) pcrLength() int { - if af.hasPCR() { - return 6 - } - return 0 -} - -const pcrStart = 6 // start of the pcr with respect to the start of the packet - -// opcrLength returns the length of the OPCR, if there is no OPCR then its length is zero -func (af *AdaptationField) opcrLength() int { - if af.hasOPCR() { - return 6 - } - return 0 -} - -// opcrStart returns the start index of where the OPCR field should -// be with respect to the start of the packet. -func (af *AdaptationField) opcrStart() int { - return pcrStart + af.pcrLength() -} - -// spliceCountdownLength returns the length of the splice countdown, -// if there is no splice countdown then its length is zero. -func (af *AdaptationField) spliceCountdownLength() int { - if af.hasSplicingPoint() { - return 1 - } - return 0 -} - -// spliceCountdownStart returns the start index of where the splice -// countdown field should be with respect to the start of the packet. -func (af *AdaptationField) spliceCountdownStart() int { - return pcrStart + af.pcrLength() + af.opcrLength() -} - -// transportPrivateDataLength returns the length of the transport private data, -// if there is no transport private data then its length is zero. -func (af *AdaptationField) transportPrivateDataLength() int { - if !af.hasTransportPrivateData() { - return 0 - } - - if af.transportPrivateDataStart() >= PacketSize { - return 0 - } - - // cannot extend beyond adaptation field, number of bytes - // for field stored in transportPrivateDataLength - return 1 + int(af[af.transportPrivateDataStart()]) -} - -// transportPrivateDataStart returns the start index of where the -// transport private data should be with respect to the start of the packet. -func (af *AdaptationField) transportPrivateDataStart() int { - return pcrStart + af.pcrLength() + af.opcrLength() + af.spliceCountdownLength() -} - -// adaptationExtensionLength returns the length of the adaptation field extension, -// if there is no adaptation field extension then its length is zero -func (af *AdaptationField) adaptationExtensionLength() int { - if !af.hasAdaptationFieldExtension() { - return 0 - } - - if af.adaptationExtensionStart() >= PacketSize { - return 0 - } - - return 1 + int(af[af.adaptationExtensionStart()]) -} - -// adaptationExtensionStart returns the start index of where the -// adaptation extension start should be with respect to the start of the packet. -func (af *AdaptationField) adaptationExtensionStart() int { - return pcrStart + af.pcrLength() + af.opcrLength() + - af.spliceCountdownLength() + af.transportPrivateDataLength() -} - -// stuffingStart returns the start index of where the -// stuffing bytes should be with respect to the start of the packet. -func (af *AdaptationField) stuffingStart() int { - return pcrStart + - af.pcrLength() + af.opcrLength() + af.spliceCountdownLength() + - af.transportPrivateDataLength() + af.adaptationExtensionLength() -} - -// stuffingEnd returns the index where the stuffing bytes end -// (first index without stuffing bytes) with respect to the start of the packet. -func (af *AdaptationField) stuffingEnd() int { - stuffingEnd := int(af[4]) + 5 - - if stuffingEnd >= PacketSize { - return PacketSize - 1 - } - - return stuffingEnd -} - -// setLength sets the length field of the adaptation field. -func (af *AdaptationField) setLength(length int) { - af[4] = byte(length) -} - -// stuffAF will ensure that the stuffing bytes are all 0xFF. -func (af *AdaptationField) stuffAF() { - for i := af.stuffingStart(); i < af.stuffingEnd(); i++ { - af[i] = 0xFF // stuffing byte must be 0xFF - } -} - -// resizeAF will resize the adaptation field to insert a new field into it. -// start is the start of the field being manipulated (smallest index) -// delta is how much shifting needs to be done. -// this function must be called before the field is marked as present. -func (af *AdaptationField) resizeAF(start int, delta int) error { - if delta > 0 { // shifting for growing - end := af.stuffingStart() - startRight := start + delta - endRight := af.stuffingStart() + delta - if af.stuffingEnd() < endRight { - // cannot grow in size, payload will be corrupted. - return gots.ErrAdaptationFieldCannotGrow - } - src := af[start:end] - dst := af[startRight:endRight] - copy(dst, src) - } - if delta < 0 { - startRight := start - delta - endRight := af.stuffingStart() - end := endRight + delta - src := []byte(af[startRight:endRight]) - dst := []byte(af[start:end]) - copy(dst, src) - for i := end; i < endRight; i++ { - af[i] = 0xFF // fill in the gap with stuffing - } - } - return nil -} - -// Length returns the length of the adaptation field -func (af *AdaptationField) Length() int { - return int(af[4]) -} - -// SetDiscontinuity sets the Discontinuity field of the packet. -func (af *AdaptationField) SetDiscontinuity(value bool) error { - if err := af.valid(); err != nil { - return err - } - af.setBit(5, 0x80, value) - return nil -} - -// Discontinuity returns the value of the discontinuity field in the packet. -func (af *AdaptationField) Discontinuity() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.getBit(5, 0x80), nil -} - -// SetRandomAccess sets the value of the random access field in the packet. -func (af *AdaptationField) SetRandomAccess(value bool) error { - if err := af.valid(); err != nil { - return err - } - af.setBit(5, 0x40, value) - return nil -} - -// RandomAccess returns the value of the random access field in the packet. -func (af *AdaptationField) RandomAccess() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.getBit(5, 0x40), nil -} - -// SetElementaryStreamPriority sets the Elementary Stream Priority Flag. -func (af *AdaptationField) SetElementaryStreamPriority(value bool) error { - if err := af.valid(); err != nil { - return err - } - af.setBit(5, 0x20, value) - return nil -} - -// ElementaryStreamPriority returns the Elementary Stream Priority Flag. -func (af *AdaptationField) ElementaryStreamPriority() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.getBit(5, 0x20), nil -} - -// SetHasPCR sets HasPCR -// HasPCR determines if the packet has a PCR -func (af *AdaptationField) SetHasPCR(value bool) error { - if err := af.valid(); err != nil { - return err - } - delta := 6 * af.bitDelta(5, 0x10, value) - err := af.resizeAF(pcrStart, delta) - if err != nil { - return err - } - af.setBit(5, 0x10, value) - return nil -} - -// HasPCR returns if the packet has a PCR -func (af *AdaptationField) HasPCR() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.hasPCR(), nil -} - -// SetPCR sets the PCR of the adaptation field. -// If impossible an error is returned. -func (af *AdaptationField) SetPCR(PCR uint64) error { - if err := af.valid(); err != nil { - return err - } - if !af.hasPCR() { - return gots.ErrNoPCR - } - gots.InsertPCR(af[pcrStart:af.opcrStart()], PCR) - return nil -} - -// PCR returns the PCR from the adaptation field if possible. -// if it is not possible an error is returned. -func (af *AdaptationField) PCR() (uint64, error) { - if err := af.valid(); err != nil { - return 0, err - } - if !af.hasPCR() { - return 0, gots.ErrNoPCR - } - return gots.ExtractPCR(af[pcrStart:af.opcrStart()]), nil -} - -// SetHasOPCR sets HasOPCR -// HasOPCR determines if the packet has a OPCR -func (af *AdaptationField) SetHasOPCR(value bool) error { - if err := af.valid(); err != nil { - return err - } - delta := 6 * af.bitDelta(5, 0x08, value) - err := af.resizeAF(af.opcrStart(), delta) - if err != nil { - return err - } - af.setBit(5, 0x08, value) - return nil -} - -// HasOPCR returns if the packet has an OPCR -func (af *AdaptationField) HasOPCR() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.hasOPCR(), nil -} - -// SetOPCR sets the OPCR of the adaptation field. -// If impossible an error is returned. -func (af *AdaptationField) SetOPCR(PCR uint64) error { - if err := af.valid(); err != nil { - return err - } - if !af.hasOPCR() { - return gots.ErrNoOPCR - } - gots.InsertPCR(af[af.opcrStart():af.spliceCountdownStart()], PCR) - return nil -} - -// OPCR returns the OPCR from the adaptation field if possible. -// if it is not possible an error is returned. -func (af *AdaptationField) OPCR() (uint64, error) { - if err := af.valid(); err != nil { - return 0, err - } - if !af.hasOPCR() { - return 0, gots.ErrNoOPCR - } - return gots.ExtractPCR(af[af.opcrStart():af.spliceCountdownStart()]), nil -} - -// SetHasSplicingPoint sets HasSplicingPoint -// HasSplicingPoint determines if the packet has a Splice Countdown -func (af *AdaptationField) SetHasSplicingPoint(value bool) error { - if err := af.valid(); err != nil { - return err - } - delta := 1 * af.bitDelta(5, 0x04, value) - err := af.resizeAF(af.spliceCountdownStart(), delta) - if err != nil { - return err - } - af.setBit(5, 0x04, value) - return nil -} - -// HasSplicingPoint returns if the packet has a Splice Countdown -func (af *AdaptationField) HasSplicingPoint() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.hasSplicingPoint(), nil -} - -// SetSpliceCountdown sets the Splice Countdown of the adaptation field. -// If impossible an error is returned. -func (af *AdaptationField) SetSpliceCountdown(value byte) error { - if err := af.valid(); err != nil { - return err - } - if !af.hasSplicingPoint() { - return gots.ErrNoSplicePoint - } - af[af.spliceCountdownStart()] = value - return nil -} - -// SpliceCountdown returns the Splice Countdown from the adaptation field if possible. -// if it is not possible an error is returned. -func (af *AdaptationField) SpliceCountdown() (int, error) { - if err := af.valid(); err != nil { - return 0, err - } - if !af.hasSplicingPoint() { - return 0, gots.ErrNoSplicePoint - } - return int(int8(af[af.spliceCountdownStart()])), nil // int8 cast is for 2s complement numbers -} - -// SetHasTransportPrivateData sets HasTransportPrivateData -// HasTransportPrivateData determines if the packet has Transport Private Data -func (af *AdaptationField) SetHasTransportPrivateData(value bool) error { - if err := af.valid(); err != nil { - return err - } - delta := 1 * af.bitDelta(5, 0x02, value) - err := af.resizeAF(af.transportPrivateDataStart(), delta) - if err != nil { - return err - } - af[af.transportPrivateDataStart()] = 0 // zero length by default - af.setBit(5, 0x02, value) - return nil -} - -// HasTransportPrivateData returns if the packet has an Transport Private Data -func (af *AdaptationField) HasTransportPrivateData() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.hasTransportPrivateData(), nil -} - -// SetTransportPrivateData sets the Transport Private Data of the adaptation field. -// If impossible an error is returned. -func (af *AdaptationField) SetTransportPrivateData(data []byte) error { - if err := af.valid(); err != nil { - return err - } - if !af.hasTransportPrivateData() { - return gots.ErrNoPrivateTransportData - } - delta := len(data) - (af.transportPrivateDataLength() - 1) - start := af.transportPrivateDataStart() + 1 - end := start + len(data) - err := af.resizeAF(start, delta) - if err != nil { - return err - } - copy(af[start:end], data) - af[start-1] = byte(len(data)) - return nil -} - -// TransportPrivateData returns the Transport Private Data from the adaptation field if possible. -// if it is not possible an error is returned. -func (af *AdaptationField) TransportPrivateData() ([]byte, error) { - hasTPD, err := af.HasTransportPrivateData() - if err != nil { - return nil, err - } - if !hasTPD { - return nil, gots.ErrNoPrivateTransportData - } - return af[af.transportPrivateDataStart():af.adaptationExtensionStart()], nil -} - -// SetHasAdaptationFieldExtension sets HasAdaptationFieldExtension -// HasAdaptationFieldExtension determines if the packet has an Adaptation Field Extension -func (af *AdaptationField) SetHasAdaptationFieldExtension(value bool) error { - if err := af.valid(); err != nil { - return err - } - delta := 1 * af.bitDelta(5, 0x01, value) - err := af.resizeAF(af.adaptationExtensionStart(), delta) - if err != nil { - return err - } - af[af.adaptationExtensionStart()] = 0 - af.setBit(5, 0x01, value) - return nil -} - -// HasAdaptationFieldExtension returns if the packet has an Adaptation Field Extension -func (af *AdaptationField) HasAdaptationFieldExtension() (bool, error) { - if err := af.valid(); err != nil { - return false, err - } - return af.hasAdaptationFieldExtension(), nil -} - -// SetAdaptationFieldExtension sets the Adaptation Field Extension of the adaptation field. -// If impossible an error is returned. -func (af *AdaptationField) SetAdaptationFieldExtension(data []byte) error { - if err := af.valid(); err != nil { - return err - } - if !af.hasAdaptationFieldExtension() { - return gots.ErrNoAdaptationFieldExtension - } - delta := len(data) - (af.adaptationExtensionLength() - 1) - start := af.adaptationExtensionStart() + 1 - end := start + len(data) - err := af.resizeAF(start, delta) - if err != nil { - return err - } - copy(af[start:end], data) - af[start-1] = byte(len(data)) - return nil -} - -// AdaptationFieldExtension returns the Adaptation Field Extension from the adaptation field if possible. -// if it is not possible an error is returned. -func (af *AdaptationField) AdaptationFieldExtension() ([]byte, error) { - hasAFC, err := af.HasAdaptationFieldExtension() - if err != nil { - return nil, err - } - if !hasAFC { - return nil, gots.ErrNoAdaptationFieldExtension - } - return af[af.adaptationExtensionStart():af.stuffingStart()], nil -} diff --git a/v2/packet/adaptationfield/adaptationfield.go b/v2/packet/adaptationfield/adaptationfield.go deleted file mode 100644 index bb4b31d..0000000 --- a/v2/packet/adaptationfield/adaptationfield.go +++ /dev/null @@ -1,133 +0,0 @@ -package adaptationfield - -import ( - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/packet" -) - -// Length returns the length of the adaptation field in bytes -func Length(pkt *packet.Packet) uint8 { - return uint8(pkt[4]) -} - -// IsDiscontinuous returns the discontinuity indicator for this adaptation field -func IsDiscontinuous(pkt *packet.Packet) bool { - return pkt[5]&0x80 != 0 -} - -// IsRandomAccess returns the random access indicator for this adaptation field -func IsRandomAccess(pkt *packet.Packet) bool { - return pkt[5]&0x40 != 0 -} - -// IsESHigherPriority returns true if this elementary stream is -// high priority. Corresponds to the elementary stream -// priority indicator. -func IsESHigherPriority(pkt *packet.Packet) bool { - return pkt[5]&0x20 != 0 -} - -// HasPCR returns true when the PCR flag is set -func HasPCR(pkt *packet.Packet) bool { - return pkt[5]&0x10 != 0 -} - -// HasOPCR returns true when the OPCR flag is set -func HasOPCR(pkt *packet.Packet) bool { - return pkt[5]&0x08 != 0 -} - -// HasSplicingPoint returns true when the splicing countdown field is present -func HasSplicingPoint(pkt *packet.Packet) bool { - return pkt[5]&0x04 != 0 -} - -// HasTransportPrivateData returns true when the private data field is present -func HasTransportPrivateData(pkt *packet.Packet) bool { - return pkt[5]&0x02 != 0 -} - -// HasAdaptationFieldExtension returns true if this adaptation field contains an extension field -func HasAdaptationFieldExtension(pkt *packet.Packet) bool { - return pkt[5]&0x01 != 0 -} - -// EncoderBoundaryPoint returns the byte array located in the optional TransportPrivateData of the (also optional) -// AdaptationField of the Packet. If either of these optional fields are missing an empty byte array is returned with an error -func EncoderBoundaryPoint(pkt *packet.Packet) ([]byte, error) { - hasAdapt := packet.ContainsAdaptationField(pkt) - if hasAdapt && Length(pkt) > 0 && HasTransportPrivateData(pkt) { - ebp, err := TransportPrivateData(pkt) - if err != nil { - return nil, err - } - return ebp, nil - } - return nil, gots.ErrNoEBP -} - -// PCR is the Program Clock Reference. -// First 33 bits are PCR base. -// Next 6 bits are reserved. -// Final 9 bits are PCR extension. -func PCR(pkt *packet.Packet) ([]byte, error) { - if !HasPCR(pkt) { - return nil, gots.ErrNoPCR - } - offset := 6 - return pkt[offset : offset+6], nil -} - -// OPCR is the Original Program Clock Reference. -// First 33 bits are original PCR base. -// Next 6 bits are reserved. -// Final 9 bits are original PCR extension. -func OPCR(pkt *packet.Packet) ([]byte, error) { - if !HasOPCR(pkt) { - return nil, gots.ErrNoOPCR - } - offset := 6 - if HasPCR(pkt) { - offset += 6 - } - return pkt[offset : offset+6], nil -} - -// SpliceCountdown returns a count of how many packets after this one until -// a splice point occurs or an error if none exist. This function calls -// HasSplicingPoint to check for the existence of a splice countdown. -func SpliceCountdown(pkt *packet.Packet) (uint8, error) { - if !HasSplicingPoint(pkt) { - return 0, gots.ErrNoSplicePoint - } - offset := 6 - if HasPCR(pkt) { - offset += 6 - } - if HasOPCR(pkt) { - offset += 6 - } - return pkt[offset], nil -} - -// TransportPrivateData returns the private data from this adaptation field -// or an empty array and an error if there is none. This function calls -// HasTransportPrivateData to check for the existence of private data. -func TransportPrivateData(pkt *packet.Packet) ([]byte, error) { - if !HasTransportPrivateData(pkt) { - return nil, gots.ErrNoPrivateTransportData - } - offset := 6 - if HasPCR(pkt) { - offset += 6 - } - if HasOPCR(pkt) { - offset += 6 - } - if HasSplicingPoint(pkt) { - offset++ - } - dataLength := uint8(pkt[offset]) - offset++ - return pkt[uint8(offset) : uint8(offset)+dataLength], nil -} diff --git a/v2/packet/adaptationfield/doc.go b/v2/packet/adaptationfield/doc.go deleted file mode 100644 index e56e54e..0000000 --- a/v2/packet/adaptationfield/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// AdaptationField provides functions for accessing and reading packet adaptation fields -package adaptationfield diff --git a/v2/packet/adaptationfield_test.go b/v2/packet/adaptationfield_test.go deleted file mode 100644 index 49ddf87..0000000 --- a/v2/packet/adaptationfield_test.go +++ /dev/null @@ -1,305 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "bytes" - "testing" -) - -func generatePacketAF(t *testing.T, AFString string) (*Packet, *AdaptationField) { - p := createPacketEmptyAdaptationField(t, "47000030"+AFString) - a, err := p.AdaptationField() - if err != nil { - t.Errorf("failed to get adaptation field. error: %s", err.Error()) - } - if a == nil { - t.Errorf("adaptation field does not exist") - } - return p, a -} - -func TestDiscontinuity(t *testing.T) { - _, a := generatePacketAF(t, "0180") - if discontinuity, err := a.Discontinuity(); !discontinuity || err != nil { - t.Errorf("failed to read discontinuity correctly. expected false got true.") - } - _, a = generatePacketAF(t, "0190") - if discontinuity, err := a.Discontinuity(); !discontinuity || err != nil { - t.Errorf("failed to read discontinuity correctly. expected true got false.") - } - _, a = generatePacketAF(t, "0170") - if discontinuity, err := a.Discontinuity(); discontinuity || err != nil { - t.Errorf("failed to read discontinuity correctly. expected false got true.") - } -} - -func TestSetDiscontinuity(t *testing.T) { - target, _ := generatePacketAF(t, "0180") - generated, a := generatePacketAF(t, "0100") - a.SetDiscontinuity(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Discontinuity to true has failed.", *generated, *target) - } - target, _ = generatePacketAF(t, "0100") - generated, a = generatePacketAF(t, "0180") - a.SetDiscontinuity(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Discontinuity to false has failed.", *generated, *target) - } -} - -func TestRandomAccess(t *testing.T) { - _, a := generatePacketAF(t, "0140") - if randomAccess, err := a.RandomAccess(); !randomAccess || err != nil { - t.Errorf("failed to read RandomAccess correctly. expected true got false.") - } - _, a = generatePacketAF(t, "0130") - if randomAccess, err := a.RandomAccess(); randomAccess || err != nil { - t.Errorf("failed to read RandomAccess correctly. expected false got true.") - } -} - -func TestSetRandomAccess(t *testing.T) { - target, _ := generatePacketAF(t, "0140") - generated, a := generatePacketAF(t, "0100") - a.SetRandomAccess(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the RandomAccess to true has failed.", *generated, *target) - } - target, _ = generatePacketAF(t, "0100") - generated, a = generatePacketAF(t, "0140") - a.SetRandomAccess(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the RandomAccess to false has failed.", *generated, *target) - } -} - -func TestElementaryStreamPriority(t *testing.T) { - _, a := generatePacketAF(t, "0120") - if esp, err := a.ElementaryStreamPriority(); !esp || err != nil { - t.Errorf("failed to read ElementaryStreamPriority correctly. expected true got false.") - } - _, a = generatePacketAF(t, "0110") - if esp, err := a.ElementaryStreamPriority(); esp || err != nil { - t.Errorf("failed to read ElementaryStreamPriority correctly. expected false got true.") - } -} - -func TestSetElementaryStreamPriority(t *testing.T) { - target, _ := generatePacketAF(t, "0120") - generated, a := generatePacketAF(t, "0100") - a.SetElementaryStreamPriority(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the ElementaryStreamPriority to true has failed.", *generated, *target) - } - target, _ = generatePacketAF(t, "0100") - generated, a = generatePacketAF(t, "0120") - a.SetElementaryStreamPriority(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the ElementaryStreamPriority to false has failed.", *generated, *target) - } -} - -func TestHasSplicingPoint(t *testing.T) { - _, a := generatePacketAF(t, "0104") - if hsp, err := a.HasSplicingPoint(); !hsp || err != nil { - t.Errorf("failed to read HasSplicingPoint correctly. expected true got false.") - } - _, a = generatePacketAF(t, "0111") - if hsp, err := a.HasSplicingPoint(); hsp || err != nil { - t.Errorf("failed to read HasSplicingPoint correctly. expected false got true.") - } -} - -func TestSetHasSplicingPoint(t *testing.T) { - target, _ := generatePacketAF(t, "0F04") - generated, a := generatePacketAF(t, "0100") - if a.SetHasSplicingPoint(true) == nil { - t.Error("adaptation field cannot fit a splice countdown field but no error was returned") - } - generated, a = generatePacketAF(t, "0F00") - a.SetHasSplicingPoint(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the HasSplicingPoint to true has failed.", *generated, *target) - } - target, _ = generatePacketAF(t, "0100") - generated, a = generatePacketAF(t, "0104") - a.SetHasSplicingPoint(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the HasSplicingPoint to false has failed.", *generated, *target) - } -} - -func TestAdaptationField(t *testing.T) { - p := createPacketEmptyPayload(t, "470000300102") - a, err := p.AdaptationField() - if err != nil { - t.Errorf("error getting adaptation field: %s", err.Error()) - } - if a == nil { - t.Errorf("no adaptation field was returned") - } - - p = createPacketEmptyPayload(t, "470000100002") - a, err = p.AdaptationField() - if err == nil { - t.Error("no error was returned in trying to access a nonexistent adaptation field.") - } - if a != nil { - t.Errorf("adaptation field does not exist but something was returned.") - } -} - -func TestAdaptationFieldFull(t *testing.T) { - generated, a := generatePacketAF(t, "B700") - target, _ := generatePacketAF(t, "B710000000007E01") - err := a.SetHasPCR(true) - if err != nil { - t.Errorf("failed to set pcr flag. Error: %s", err.Error()) - } - a.SetPCR(1) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the PCR to 1 has failed.", *generated, *target) - } - - target, _ = generatePacketAF(t, "B718000000007E01000000007E02") - a.SetHasOPCR(true) - a.SetOPCR(2) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the OPCR to 2 has failed.", *generated, *target) - } - - target, _ = generatePacketAF(t, "B71A000000007E01000000007E020188") - a.SetHasTransportPrivateData(true) - a.SetTransportPrivateData([]byte{0x88}) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Transport Private Data to 0x88 has failed.", *generated, *target) - } - - target, _ = generatePacketAF(t, "B71B000000007E01000000007E0201880100") - a.SetHasAdaptationFieldExtension(true) - a.SetAdaptationFieldExtension([]byte{0x00}) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Adaptation Field Extension to 0x00 has failed.", *generated, *target) - } - - target, _ = generatePacketAF(t, "B71B000000007E01000000007E020266660100") - a.SetTransportPrivateData([]byte{0x66, 0x66}) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Transport Private Data to 0x6666 has failed.", *generated, *target) - } - - target, _ = generatePacketAF(t, "B713000000007E010266660100") - a.SetHasOPCR(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nremoving has failed.", *generated, *target) - } - - target, _ = generatePacketAF(t, "B717000000007E01510266660100") - a.SetHasSplicingPoint(true) - a.SetSpliceCountdown(0x51) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Transport Private Data to 0x6666 has failed.", *generated, *target) - } - - a.SetHasOPCR(true) - a.SetOPCR(2) - - hasPCR, err := a.HasPCR() - if err != nil { - t.Errorf("failed to get HasPCR. error: %s", err.Error()) - } - if hasPCR != true { - t.Errorf("generated HasPCR (%t) does not match expected HasPCR (%t)", hasPCR, true) - } - pcr, err := a.PCR() - if err != nil { - t.Errorf("failed to get PCR. error: %s", err.Error()) - } - if pcr != 1 { - t.Errorf("generated PCR (%d) does not match expected PCR (%d)", pcr, 1) - } - - hasOPCR, err := a.HasOPCR() - if err != nil { - t.Errorf("failed to get HasPCR. error: %s", err.Error()) - } - if hasOPCR != true { - t.Errorf("generated HasPCR (%t) does not match expected HasPCR (%t)", hasOPCR, true) - } - opcr, err := a.OPCR() - if err != nil { - t.Errorf("failed to get OPCR. error: %s", err.Error()) - } - if opcr != 2 { - t.Errorf("generated OPCR (%d) does not match expected OPCR (%d)", opcr, 2) - } - - hasSplicingPoint, err := a.HasSplicingPoint() - if err != nil { - t.Errorf("failed to get HasSplicingPoint. error: %s", err.Error()) - } - if hasSplicingPoint != true { - t.Errorf("generated hasSplicingPoint (%t) does not match expected hasSplicingPoint (%t)", hasSplicingPoint, true) - } - spliceCountdown, err := a.SpliceCountdown() - if err != nil { - t.Errorf("failed to get spliceCountdown. error: %s", err.Error()) - } - if spliceCountdown != 0x51 { - t.Errorf("generated spliceCountdown (0x%X) does not match expected spliceCountdown (0x%X)", spliceCountdown, 0x51) - } - - hasTPD, err := a.HasTransportPrivateData() - if err != nil { - t.Errorf("failed to get hasTPD. error: %s", err.Error()) - } - if hasTPD != true { - t.Errorf("generated HasTransportPrivateData (%t) does not match expected HasTransportPrivateData (%t)", hasTPD, true) - } - tpd, err := a.TransportPrivateData() - if err != nil { - t.Errorf("failed to get TransportPrivateData. error: %s", err.Error()) - } - if bytes.Equal(tpd, []byte{0x66, 0x66}) { - t.Errorf("generated TransportPrivateData (0x%X) does not match expected TransportPrivateData (0x%X)", tpd, []byte{0x66, 0x66}) - } - - hasAFE, err := a.HasAdaptationFieldExtension() - if err != nil { - t.Errorf("failed to get HasAdaptationFieldExtension. error: %s", err.Error()) - } - if hasAFE != true { - t.Errorf("generated HasAdaptationFieldExtension (%t) does not match expected HasAdaptationFieldExtension (%t)", hasAFE, true) - } - afe, err := a.AdaptationFieldExtension() - if err != nil { - t.Errorf("failed to get AdaptationFieldExtension. error: %s", err.Error()) - } - if bytes.Equal(afe, []byte{0x00}) { - t.Errorf("generated AdaptationFieldExtension (0x%X) does not match expected AdaptationFieldExtension (0x%X)", tpd, []byte{0x00}) - } -} diff --git a/v2/packet/create.go b/v2/packet/create.go deleted file mode 100644 index 6ce6478..0000000 --- a/v2/packet/create.go +++ /dev/null @@ -1,215 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "github.com/Comcast/gots/v2" -) - -var ( - required = []func(*Packet){setSyncByte} -) - -var ( - // TestPatPacket is a minimal PAT packet for testing. It contains a single program stream with no payload. - TestPatPacket = Packet{ - 0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xcb, 0x00, - 0x00, 0x00, 0x01, 0xe0, 0x64, 0x68, 0xd6, 0x84, 0x2e, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - TestPmtPacket = Packet{ - 0x47, 0x40, 0x64, 0x10, 0x00, 0x02, 0xb0, 0x2d, 0x00, 0x01, 0xcb, 0x00, - 0x00, 0xe0, 0x65, 0xf0, 0x06, 0x05, 0x04, 0x43, 0x55, 0x45, 0x49, 0x1b, - 0xe0, 0x65, 0xf0, 0x05, 0x0e, 0x03, 0x00, 0x04, 0xb0, 0x0f, 0xe0, 0x66, - 0xf0, 0x06, 0x0a, 0x04, 0x65, 0x6e, 0x67, 0x00, 0x86, 0xe0, 0x6e, 0xf0, - 0x00, 0x7f, 0xc9, 0xad, 0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} -) - -// Create creates a new packet with the provided information. This is primarily used for testing. If you are creating packets for use other than testing, you should reconsider your usage of this package. - -// Example usage -// pkt, _ = SetCC( -// Create(pid, -// WithHasPayloadFlag, -// WithContinuousAF, -// WithPUSI), -// cc) -func Create(pid int, options ...func(*Packet)) *Packet { - var pkt Packet - setPid(&pkt, pid) - for _, option := range options { - option(&pkt) - } - for _, option := range required { - option(&pkt) - } - return &pkt -} - -// CreateTestPacket creates a test packet with the given PID, continuity counter, payload unit start indicator and payload flag -// This is a convenience function for often used packet creatio options functions -func CreateTestPacket(pid int, cc uint8, pusi, hasPay bool) *Packet { - var pkt *Packet - if hasPay && pusi { - pkt = SetCC( - Create(pid, - WithHasPayloadFlag, - WithContinuousAF, - WithPUSI), - cc) - } else if hasPay { - pkt = SetCC( - Create(pid, - WithHasPayloadFlag, - WithContinuousAF), - cc) - - } else { - pkt = SetCC(Create(pid, WithContinuousAF), cc) - } - return pkt -} - -// CreateDCPacket creates a new packet with a discontinuous adapataion field and the given PID and CC -func CreateDCPacket(pid int, cc uint8) *Packet { - pkt := SetCC(Create(pid, WithDiscontinuousAF, WithHasPayloadFlag), cc) - return pkt -} - -// CreatePacketWithPayload creates a new packet with the given PID, CC and payload -func CreatePacketWithPayload(pid int, cc uint8, pay []byte) *Packet { - pkt := SetCC( - Create( - pid, - WithHasPayloadFlag, - WithContinuousAF, - func(pkt *Packet) { - SetPayload(pkt, pay) - }, - ), - cc, - ) - return pkt -} - -func setPid(pkt *Packet, pid int) { - pkt[1] = byte(pid >> 8 & 0x1f) - pkt[2] = byte(pid & 0xff) -} - -// WithHasPayloadFlag is an option function for creating a packet with a payload flag -func WithHasPayloadFlag(pkt *Packet) { - pkt[3] |= 0x10 -} - -// WithHasAdaptationFieldFlag is an option function for creating a packet with an adaptation field -func WithHasAdaptationFieldFlag(pkt *Packet) { - pkt[3] |= 0x20 -} - -// WithAFPrivateDataFlag is an option function for creating a packet with a adaptation field private data flag -func WithAFPrivateDataFlag(pkt *Packet) { - pkt[5] |= 0x02 -} - -// WithPUSI is an option function for creating a packet with the payload unit start indicator flag set -func WithPUSI(pkt *Packet) { - pkt[1] |= 0x40 -} - -// WithContinuousAF is an option function for creating a packet with a continuous adaptation field -func WithContinuousAF(pkt *Packet) { - pkt[5] |= 0x7f -} - -// WithDisconinuousAF is an option function for creating a packet with a discontinuous adaptation field -func WithDiscontinuousAF(pkt *Packet) { - pkt[5] |= 0x80 -} - -// WithPES is an option function for creating a packet with a PES header -func WithPES(pkt *Packet, pts uint64) { - size := PacketSize - 4 - pay := make([]byte, size, size) - //packet_start_code_prefix - pay[0] = 0x00 - pay[1] = 0x00 - pay[2] = 0x01 - // stream id 184 is STREAM_ID_ALL_VIDEO_STREAMS - pay[3] = byte(184) - // Packet Length - pay[4] = 0x0 - //pay[5] = ??length - // Data alignment indicator - pay[6] = 0x40 - // PTS DTS indicator 2 is PTS_DTS_INDICATOR_ONLY_PTS - pay[7] = 0x80 - // Header len - pay[8] = 14 - gots.InsertPTS(pay[9:14], pts) - SetPayload(pkt, pay) - WithHasPayloadFlag(pkt) -} - -// SetPayload sets the payload of a given packet -func SetPayload(pkt *Packet, pay []byte) int { - start := payloadStart(pkt) - i := start - j := 0 - for i < PacketSize && j < len(pay) { - pkt[i] = pay[j] - i++ - j++ - } - return i - start -} - -// Required parts of a packet -func setSyncByte(pkt *Packet) { - pkt[0] = SyncByte -} diff --git a/v2/packet/doc.go b/v2/packet/doc.go deleted file mode 100644 index 28d00a8..0000000 --- a/v2/packet/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Package packet is used for reading and manipulating packets in an MPEG transport stream -package packet diff --git a/v2/packet/io.go b/v2/packet/io.go deleted file mode 100644 index f1b964e..0000000 --- a/v2/packet/io.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "encoding/binary" - "io" - - "github.com/Comcast/gots/v2" -) - -// Peeker wraps the Peek method. -type Peeker interface { - // Peek returns the next n bytes without advancing the reader. - Peek(n int) ([]byte, error) -} - -// PeekScanner is an extended io.ByteScanner with peek capacity. -type PeekScanner interface { - io.ByteScanner - Peeker -} - -// Sync finds the offset of the next packet header and advances the reader -// to the packet start. It returns the offset of the sync relative to the -// original reader position. -// -// Sync uses IsSynced to determine whether a position is at packet header. -func Sync(r PeekScanner) (off int64, err error) { - for { - b, err := r.ReadByte() - if err != nil { - if err == io.EOF { - return off, gots.ErrSyncByteNotFound - } - return off, err - } - if b != SyncByte { - off++ - continue - } - - err = r.UnreadByte() - if err != nil { - return off, err - } - ok, err := IsSynced(r) - if ok { - return off, nil - } - if err != nil { - if err == io.EOF { - return off, gots.ErrSyncByteNotFound - } - return off, err - } - - // Advance again. This is a consequence of not - // duplicating IsSynced for 3 and 4 byte reads. - _, err = r.ReadByte() - // These errors should never happen since we - // have already read this byte above. - if err != nil { - if err == io.EOF { - return off, gots.ErrSyncByteNotFound - } - return off, err - } - } -} - -// IsSynced returns whether r is synced to a packet boundary. -// -// IsSynced checks whether the first byte is the MPEG-TS sync byte, -// the PID is not within the reserved range of 0x4-0xf and that -// the AFC is not the reserved value 0x0. -func IsSynced(r Peeker) (ok bool, err error) { - b, err := r.Peek(4) - if err != nil { - return false, err - } - // Check that the first byte is the sync byte. - if b[0] != SyncByte { - return false, nil - } - - const ( - pidMask = 0x1fff << 8 - afcMask = 0x3 << 4 - ) - header := binary.BigEndian.Uint32(b) - - // Check that the AFC is not zero (reserved). - afc := header & afcMask - if afc == 0x0 { - return false, nil - } - - // Check that the PID is not 0x4-0xf (reserved). - pid := (header & pidMask) >> 8 - return pid < 0x4 || 0xf < pid, nil -} diff --git a/v2/packet/io_test.go b/v2/packet/io_test.go deleted file mode 100644 index 13a2038..0000000 --- a/v2/packet/io_test.go +++ /dev/null @@ -1,138 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "bufio" - "bytes" - "encoding/hex" - "testing" -) - -func TestSyncForSmoke(t *testing.T) { - bs, _ := hex.DecodeString("474000100000b00d0001c100000001e256f803e71bfffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ff4742") - r := bufio.NewReader(bytes.NewReader(bs)) - - offset, err := Sync(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if offset != 0 { - t.Error("Incorrect offset returned for next sync marker") - } - synced, err := IsSynced(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if !synced { - t.Error("Unexpectedly unsynced") - } -} - -func TestSyncNonZeroOffset(t *testing.T) { - bs, _ := hex.DecodeString("ffffff474000100000b00d0001c100000001e256f803e71bfffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ff4742") - r := bufio.NewReader(bytes.NewReader(bs)) - - synced, err := IsSynced(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if synced { - t.Error("Unexpectedly synced") - } - offset, err := Sync(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if offset != 3 { - t.Error("Incorrect offset returned for next sync marker") - } - synced, err = IsSynced(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if !synced { - t.Error("Unexpectedly unsynced") - } -} - -func TestSyncNotFound(t *testing.T) { - // no sync byte here - bs, err := hex.DecodeString("ff4000100000b00d0001c100000001e256f803e71bffffffff") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - r := bufio.NewReader(bytes.NewReader(bs)) - - synced, err := IsSynced(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if synced { - t.Error("Unexpectedly synced") - } - _, err = Sync(r) - if err == nil { - t.Errorf("Expected there to be an error, but there was not") - } -} - -func TestSyncReaderPosAtPacketStart(t *testing.T) { - bs, _ := hex.DecodeString("ffffff474000100000b00d0001c100000001e256f803e71bfffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ff4742") - r := bufio.NewReader(bytes.NewReader(bs)) - - _, err := Sync(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - bytes := make([]byte, 3) - _, err = r.Read(bytes) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - wantHex := "474000" - gotHex := hex.EncodeToString(bytes) - if gotHex != wantHex { - t.Errorf("Reader not left in correct spot. Wanted next read %v, got %v", wantHex, gotHex) - } -} diff --git a/v2/packet/modify.go b/v2/packet/modify.go deleted file mode 100644 index 49ebb35..0000000 --- a/v2/packet/modify.go +++ /dev/null @@ -1,326 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "github.com/Comcast/gots/v2" -) - -// flags that are reserved and should not be used. -const ( - invalidTransportScramblingControlFlag TransportScramblingControlOptions = 1 // 01 - invalidAdaptationFieldControlFlag AdaptationFieldControlOptions = 0 // 00 -) - -// New creates a new packet with a Null ID, sync byte, and with the adaptation -// field control set to payload only. -func New() *Packet { - //Default packet is the Null packet - return &Packet{ - 0: 0x47, // Sync byte - 1: 0x1f, 2: 0xff, // pkt.SetPID(NullPacketPid) - 3: 0x10, // pkt.SetAdaptationFieldControl(PayloadFlag) - } -} - -// FromBytes creates a ts packet from a slice of bytes 188 in length. -// If the bytes provided have errors or the slice is not 188 in length, -// then an error vill be returned along with a nill slice. -func FromBytes(bytes []byte) (*Packet, error) { - if len(bytes) != PacketSize { - return nil, gots.ErrInvalidPacketLength - } - var pkt Packet - copy(pkt[:], bytes) - err := pkt.CheckErrors() - return &pkt, err -} - -// SetTransportErrorIndicator sets the Transport Error Indicator flag. -func (p *Packet) SetTransportErrorIndicator(value bool) { - p.setBit(1, 0x80, value) -} - -// TransportErrorIndicator returns the Transport Error Indicator -func (p *Packet) TransportErrorIndicator() bool { - return p.getBit(1, 0x80) -} - -// SetPayloadUnitStartIndicator sets the Payload unit start indicator (PUSI) flag -// PUSI is a flag that indicates the start of PES data or PSI -// (Program-Specific Information) such as AT, CAT, PMT or NIT. The PUSI -// flag is contained in the second bit of the second byte of the Packet. -func (p *Packet) SetPayloadUnitStartIndicator(value bool) { - p.setBit(1, 0x40, value) -} - -// PayloadUnitStartIndicator returns the Payload unit start indicator (PUSI) flag -// PUSI is a flag that indicates the start of PES data or PSI -// (Program-Specific Information) such as AT, CAT, PMT or NIT. The PUSI -// flag is contained in the second bit of the second byte of the Packet. -func (p *Packet) PayloadUnitStartIndicator() bool { - return p.getBit(1, 0x40) -} - -// SetTransportPriority sets the Transport Priority flag -func (p *Packet) SetTransportPriority(value bool) { - p.setBit(1, 0x20, value) -} - -// TransportPriority returns the Transport Priority flag -func (p *Packet) TransportPriority() bool { - return p.getBit(1, 0x20) -} - -// SetPID sets the program ID -func (p *Packet) SetPID(pid int) { - p[1] = p[1]&^byte(0x1f) | byte(pid>>8)&byte(0x1f) - p[2] = byte(pid) -} - -// PID Returns the program ID -func (p *Packet) PID() int { - return int(p[1]&0x1f)<<8 | int(p[2]) -} - -// SetTransportScramblingControl sets the Transport Scrambling Control flag. -func (p *Packet) SetTransportScramblingControl(value TransportScramblingControlOptions) { - p[3] = p[3]&^byte(0xC0) | byte(value)<<6 -} - -// TransportScramblingControl returns the Transport Scrambling Control flag. -func (p *Packet) TransportScramblingControl() TransportScramblingControlOptions { - return TransportScramblingControlOptions((p[3] & 0xC0) >> 6) -} - -// SetAdaptationFieldControl sets the Adaptation Field Control flag. -func (p *Packet) SetAdaptationFieldControl(value AdaptationFieldControlOptions) error { - hasAFBefore := p.HasAdaptationField() - p[3] = p[3]&^byte(0x30) | byte(value)<<4 - hasAFAfter := p.HasAdaptationField() - // if it didnt have an AF but now has one. Init the AF - if !hasAFBefore && hasAFAfter { - initAdaptationField(p) - } - - if value == PayloadAndAdaptationFieldFlag { - af, _ := p.AdaptationField() - if af.Length() == 183 { - if af.stuffingStart() < PacketSize { - af.setLength(182) - af.stuffAF() - } else { - return gots.ErrAdaptationFieldTooLarge - } - } - } - - return nil -} - -// AdaptationFieldControl returns the Adaptation Field Control. -func (p *Packet) AdaptationFieldControl() AdaptationFieldControlOptions { - return AdaptationFieldControlOptions((p[3] & 0x30) >> 4) -} - -// HasPayload returns true if the adaptation field control specifies that there is a payload. -func (p *Packet) HasPayload() bool { - return p.getBit(3, 0x10) -} - -// HasPayload returns true if the adaptation field control specifies that there is an adaptation field. -func (p *Packet) HasAdaptationField() bool { - return p.getBit(3, 0x20) -} - -// SetContinuityCounter sets the continuity counter. -// The continuity counter should be an integer between 0 and 15. -// If the number is out of this range then it will discard the extra bits. -// The effect is the same as modulus by 16. -func (p *Packet) SetContinuityCounter(value int) { - // if value is greater than 15 it will overflow and start at 0 again. - p[3] = p[3]&^byte(0x0F) | byte(value&0x0F) -} - -// ContinuityCounter returns the continuity counter. -// The continuity counter is an integer between 0 and 15. -func (p *Packet) ContinuityCounter() int { - return int(p[3] & 0x0F) -} - -// SetContinuityCounter sets the continuity counter to 0. -func (p *Packet) ZeroContinuityCounter() { - p.SetContinuityCounter(0) -} - -// IncContinuityCounter increments the continuity counter. -// The continuity counter is an integer between 0 and 15. -// If the number is out of this range (overflow) -// after incrementing then it will discard the extra bits. -// The effect is the same as modulus by 16. -func (p *Packet) IncContinuityCounter() { - cc := p.ContinuityCounter() - cc += 1 - p.SetContinuityCounter(cc) -} - -// IsNull returns true if the packet PID is equal to 8191, the null packet pid. -func (p *Packet) IsNull() bool { - return p.PID() == NullPacketPid -} - -// IsNull returns true if the packet PID is equal to 0, the PAT packet pid. -func (p *Packet) IsPAT() bool { - return p.PID() == 0 -} - -// AdaptationField returns the AdaptationField of the packet. -// If the packet does not have an adaptation field then a nil -// AdaptationField is returned. -func (p *Packet) AdaptationField() (*AdaptationField, error) { - hasAF := p.HasAdaptationField() - if hasAF { - return (*AdaptationField)(p), nil - } - return nil, gots.ErrNoAdaptationField -} - -// SetAdaptationField copies the AdaptationField into a packet. -// If the packet does not have an adaptation field then an error is returned -// AdaptationField must fit in the same size as the existing adaptation field -// and its stuffing bytes. -func (p *Packet) SetAdaptationField(af *AdaptationField) error { - if !p.HasAdaptationField() { - return gots.ErrNoAdaptationField - } - oldAF, _ := p.AdaptationField() - if oldAF.stuffingEnd() < af.stuffingStart() { - return gots.ErrAdaptationFieldTooLarge - } - copy(oldAF[5:oldAF.stuffingEnd()], af[5:af.stuffingStart()]) // Copy without length. - oldAF.stuffAF() - return nil -} - -func (p *Packet) payloadStart() int { - if p.HasAdaptationField() { - return 4 + 1 + (*AdaptationField)(p).Length() - } - return 4 // packet header bytes -} - -// stuffingStart returns where the stuffing begins, this is also the first byte where the payload can begin. -// if there is no payload then it is stuffed until the very end -func (p *Packet) stuffingStart() int { - af, err := p.AdaptationField() - if err != nil { - return 4 - } - if af.Length() == 0 { - return 5 - } - return af.stuffingStart() -} - -func (p *Packet) freeSpace() int { - return PacketSize - p.stuffingStart() -} - -// Payload returns a slice to a copy of the payload bytes in the packet. -func (p *Packet) Payload() ([]byte, error) { - afc := p.AdaptationFieldControl() - if afc == AdaptationFieldFlag { - return nil, gots.ErrNoPayload - } - offset := p.payloadStart() - payload := make([]byte, PacketSize-offset) - copy(payload, p[offset:]) - return payload, nil -} - -// SetPayload sets the payload of the packet. If the payload cannot fit in the -// packet an integer will be returned that is the number of bytes that were -// able to fit in the packet. -func (p *Packet) SetPayload(data []byte) (int, error) { - afc := p.AdaptationFieldControl() - if afc == AdaptationFieldFlag { - return 0, gots.ErrNoPayload - } - freeSpace := p.freeSpace() - if freeSpace > len(data) { - p.SetAdaptationFieldControl(PayloadAndAdaptationFieldFlag) - af, _ := p.AdaptationField() - - af.setLength(PacketSize - (len(data) + 4 + 1)) // header length + adaptation field length - af.stuffAF() - } else { - af, _ := p.AdaptationField() - if af != nil { - af.setLength(PacketSize - (freeSpace + 4 + 1)) // header length + adaptation field length - } - } - - offset := p.payloadStart() - return copy(p[offset:], data), nil -} - -// Equal returns true if the bytes of the two packets are equal -func (p *Packet) Equals(r *Packet) bool { - return Equal(p, r) -} - -// CheckErrors checks the packet for errors -func (p *Packet) CheckErrors() error { - if p.syncByte() != SyncByte { - return gots.ErrBadSyncByte - } - if p.TransportScramblingControl() == invalidTransportScramblingControlFlag { - return gots.ErrInvalidTSCFlag - } - if p.AdaptationFieldControl() == invalidAdaptationFieldControlFlag { - return gots.ErrInvalidAFCFlag - } - return nil -} - -// syncByte returns the Sync byte. -func (p *Packet) syncByte() byte { - return p[0] -} - -// setBit sets a bit in a packet. If packet is nil, or slice has a bad -// length, it does nothing. -func (p *Packet) setBit(index int, mask byte, value bool) { - if value { - p[index] |= mask - } else { - p[index] &= ^mask - } -} - -// getBit returns true if a bit in a packet is set to 1. -func (p *Packet) getBit(index int, mask byte) bool { - return p[index]&mask != 0 -} diff --git a/v2/packet/modify_test.go b/v2/packet/modify_test.go deleted file mode 100644 index 678a6bf..0000000 --- a/v2/packet/modify_test.go +++ /dev/null @@ -1,594 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "bytes" - "encoding/hex" - "testing" -) - -const ( - testPacket = "47000010" + - "000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000" + - "000000000000000000000000000000000000000000000000" + - "00000000000000000000000000000000" - - testPacket2 = "471FFF30" + - "530246999999999999999999999999999999999999999999" + - "999999999999999999999999999999999999999999999999" + - "999999999999999999999999999999999999999999999999" + - "99FFFFFFFFFFFFFFFFFFFFFF777777777777777777777777" + - "777777777777777777777777777777777777777777777777" + - "777777777777777777777777777777777777777777777777" + - "777777777777777777777777777777777777777777777777" + - "77777777777777777777777777777777" -) - -func createPacketEmptyPayload(t *testing.T, header string) *Packet { - headerBytes, _ := hex.DecodeString(header) - bodyBytes := make([]byte, 188-len(headerBytes)) - packetBytes := append(headerBytes, bodyBytes...) - - p, err := FromBytes(packetBytes) - if err != nil { - t.Error("packet error checking failed") - } - return p -} - -func createPacketEmptyAdaptationField(t *testing.T, header string) *Packet { - headerBytes, _ := hex.DecodeString(header) - AFBytes := make([]byte, 188) - AFBytes[4] = 183 - AFBytes[5] = 0 - for i := 6; i < len(AFBytes); i++ { - AFBytes[i] = 0xFF - } - AFBytes = AFBytes[len(headerBytes):188] - packetBytes := append(headerBytes, AFBytes...) - - p, err := FromBytes(packetBytes) - if err != nil { - t.Error("packet error checking failed") - } - return p -} - -func TestFromBytes(t *testing.T) { - bytes, _ := hex.DecodeString(testPacket) - p, err := FromBytes(bytes) - if err != nil { - t.Error("eacket error checking failed") - } - if (len(bytes) != 188) && (len(p) != 188) { - t.Error("packets are not 188 bytes") - return - } - for i := range bytes { - if bytes[i] != p[i] { - t.Error("packet generated with FromBytes did not copy bytes correctly.") - return - } - } -} - -func TestNewPacket(t *testing.T) { - target := createPacketEmptyPayload(t, "471FFF10") - generated := New() - if err := generated.CheckErrors(); err != nil { - t.Error("Default packet has errors.") - } - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nCreating a new packet failed.", generated, target) - } -} - -func TestSetTransportErrorIndicator(t *testing.T) { - generated := New() - - target := createPacketEmptyPayload(t, "479FFF10") - generated.SetTransportErrorIndicator(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the transport error indicator to true has failed.", generated, target) - } - - target = createPacketEmptyPayload(t, "471FFF10") - generated.SetTransportErrorIndicator(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the transport error indicator to false has failed.", generated, target) - } -} - -func TestTransportErrorIndicator(t *testing.T) { - pkt := createPacketEmptyPayload(t, "479FFF10") - tei := pkt.TransportErrorIndicator() - if tei != true { - t.Error("TransportErrorIndicator does not match expected.") - } -} - -func TestSetPayloadUnitStartIndicator(t *testing.T) { - generated := New() - - target := createPacketEmptyPayload(t, "475FFF10") - generated.SetPayloadUnitStartIndicator(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the PUSI to true has failed.", generated, target) - } - - target = createPacketEmptyPayload(t, "471FFF10") - generated.SetPayloadUnitStartIndicator(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the PUSI to false has failed.", generated, target) - } -} - -func TestPayloadUnitStartIndicator(t *testing.T) { - pkt := createPacketEmptyPayload(t, "475FFF10") - pusi := pkt.PayloadUnitStartIndicator() - if pusi != true { - t.Error("PayloadUnitStartIndicator does not match expected.") - } -} - -func TestSetTP(t *testing.T) { - generated := New() - - target := createPacketEmptyPayload(t, "473FFF10") - generated.SetTransportPriority(true) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the transport priority to true has failed.", generated, target) - } - - target = createPacketEmptyPayload(t, "471FFF10") - generated.SetTransportPriority(false) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the transport priority to false has failed.", generated, target) - } -} - -func TestTP(t *testing.T) { - pkt := createPacketEmptyPayload(t, "473FFF10") - tp := pkt.TransportPriority() - if tp != true { - t.Error("TransportPriority does not match expected.") - } -} - -func TestSetPID(t *testing.T) { - generated := createPacketEmptyPayload(t, "47e09010") - - target := createPacketEmptyPayload(t, "47f76A10") - generated.SetPID(0x176A) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the PID to 0x176A has failed.", generated, target) - } - - generated = New() - - target = createPacketEmptyPayload(t, "47000010") - generated.SetPID(0x0000) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the PID to 0x0000 has failed.", generated, target) - } - - target = createPacketEmptyPayload(t, "471fec10") - generated.SetPID(0x1fec) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the PID to 0x1fec has failed.", generated, target) - } -} - -func TestPID(t *testing.T) { - pkt := createPacketEmptyPayload(t, "47344410") - pid := pkt.PID() - if pid != 0x1444 { - t.Errorf("reads different PID than expected. expected: %d", 0x1444) - } -} - -func TestSetTransportScramblingControl(t *testing.T) { - generated := New() - - target := createPacketEmptyPayload(t, "471FFFD0") - generated.SetTransportScramblingControl(ScrambleOddKeyFlag) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Transport Scrambling Control to ScrambleOddKeyFlag has failed.", generated, target) - } - - target = createPacketEmptyPayload(t, "471FFF90") - generated.SetTransportScramblingControl(ScrambleEvenKeyFlag) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Transport Scrambling Control to ScrambleEvenKeyFlag has failed.", generated, target) - } - - target = createPacketEmptyPayload(t, "471FFF10") - generated.SetTransportScramblingControl(NoScrambleFlag) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Transport Scrambling Control to NoScrambleFlag has failed.", generated, target) - } -} - -func TestTransportScramblingControl(t *testing.T) { - pkt := createPacketEmptyPayload(t, "471FFF90") - tsc := pkt.TransportScramblingControl() - if tsc != ScrambleEvenKeyFlag { - t.Errorf("reads different transport scrambling control than expected. expected: ScrambleEvenKeyFlag") - } - - pkt = createPacketEmptyPayload(t, "471FFFD0") - tsc = pkt.TransportScramblingControl() - if tsc != ScrambleOddKeyFlag { - t.Errorf("reads different transport scrambling control than expected. expected: ScrambleOddKeyFlag") - } - - pkt = createPacketEmptyPayload(t, "471FFF10") - tsc = pkt.TransportScramblingControl() - if tsc != NoScrambleFlag { - t.Errorf("reads different transport scrambling control than expected. expected: NoScrambleFlag") - } -} - -func TestSetAdaptationFieldControl(t *testing.T) { - generated := New() - - target := createPacketEmptyPayload(t, "471FFF10") - generated.SetAdaptationFieldControl(PayloadFlag) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Adaptation Field Control to PayloadFlag has failed.", generated, target) - } - - target = createPacketEmptyAdaptationField(t, "471FFF30B6") - generated.SetAdaptationFieldControl(PayloadAndAdaptationFieldFlag) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Adaptation Field Control to PayloadAndAdaptationFieldFlag has failed.", generated, target) - } - - target = createPacketEmptyAdaptationField(t, "471FFF20") - generated.SetAdaptationFieldControl(PayloadFlag) - generated.SetAdaptationFieldControl(AdaptationFieldFlag) - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Adaptation Field Control to AdaptationFieldFlag has failed.", generated, target) - } -} - -func TestAdaptationFieldControl(t *testing.T) { - pkt := createPacketEmptyPayload(t, "471FFF9000") - asc := pkt.AdaptationFieldControl() - if asc != PayloadFlag { - t.Errorf("reads different adaptation field control than expected. expected: PayloadFlag") - } - pkt = createPacketEmptyAdaptationField(t, "471FFFB001") - asc = pkt.AdaptationFieldControl() - if asc != PayloadAndAdaptationFieldFlag { - t.Errorf("reads different adaptation field control than expected. expected: PayloadAndAdaptationFieldFlag") - } - pkt = createPacketEmptyAdaptationField(t, "471FFFA001") - asc = pkt.AdaptationFieldControl() - if asc != AdaptationFieldFlag { - t.Errorf("reads different adaptation field control than expected. expected: AdaptationFieldFlag") - } -} - -func TestSetContinuityCounter(t *testing.T) { - target := createPacketEmptyPayload(t, "471FFF1f") - generated := New() - - generated.SetContinuityCounter(15) - - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the Continuity Counter to 15 has failed.", generated, target) - } -} - -func TestContinuityCounterModify(t *testing.T) { - pkt := createPacketEmptyPayload(t, "471FFF19") - cc := pkt.ContinuityCounter() - if cc != 9 { - t.Errorf("Reads different continuity counter than expected. ecpected: 9") - } -} - -func TestIncContinuityCounter(t *testing.T) { - target := createPacketEmptyPayload(t, "471FFF10") - generated := New() - - generated.SetContinuityCounter(0xFE) // cc = 14 - generated.IncContinuityCounter() // cc = 15 - generated.IncContinuityCounter() // cc = 0, overflow - - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nContinuity Counter did not rollover as expected.", generated, target) - } -} - -func TestIsPAT(t *testing.T) { - pkt := createPacketEmptyPayload(t, "47000010") - isPAT := pkt.IsPAT() - if !isPAT { - t.Errorf("packet should be a PAT") - } -} - -func TestIsNull(t *testing.T) { - pkt := createPacketEmptyPayload(t, "471FFF10") - isNull := pkt.IsNull() - if !isNull { - t.Errorf("packet should be Null") - } -} - -func TestHasAdaptationField(t *testing.T) { - pkt := createPacketEmptyPayload(t, "471FFF100100") - hasAF := pkt.HasAdaptationField() - if hasAF { - t.Errorf("Packet should not have Adaptation Field (AdaptationFieldControl = 01).") - } - pkt = createPacketEmptyPayload(t, "471FFF200100") - hasAF = pkt.HasAdaptationField() - if !hasAF { - t.Errorf("Packet should have Adaptation Field (AdaptationFieldControl = 10).") - } - pkt = createPacketEmptyPayload(t, "471FFF300100") - hasAF = pkt.HasAdaptationField() - if !hasAF { - t.Errorf("Packet should have Adaptation Field (AdaptationFieldControl = 11).") - } -} - -func TestHasPayload(t *testing.T) { - pkt := createPacketEmptyPayload(t, "471FFF10") - hasPayload := pkt.HasPayload() - if !hasPayload { - t.Errorf("Packet should have Payload (AdaptationFieldControl = 01).") - } - pkt = createPacketEmptyPayload(t, "471FFF20") - hasPayload = pkt.HasPayload() - if hasPayload { - t.Errorf("Packet should not have Payload (AdaptationFieldControl = 10).") - } - pkt = createPacketEmptyPayload(t, "471FFF30") - hasPayload = pkt.HasPayload() - if !hasPayload { - t.Errorf("Packet should have Payload (AdaptationFieldControl = 11).") - } -} - -func TestHeaderBasic(t *testing.T) { - target := createPacketEmptyPayload(t, "47EFA098") - generated := New() - - generated.SetContinuityCounter(7) - generated.IncContinuityCounter() - generated.SetPID(4000) - generated.SetTransportErrorIndicator(true) - generated.SetPayloadUnitStartIndicator(true) - generated.SetTransportPriority(true) - generated.SetAdaptationFieldControl(PayloadFlag) - generated.SetTransportScramblingControl(ScrambleEvenKeyFlag) - - if !Equal(generated, target) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nFields did not set successfully", generated, target) - } - - if generated.ContinuityCounter() != 8 { - t.Errorf("Reads different ContinuityCounter than expected.") - } - if generated.PID() != 4000 { - t.Errorf("Reads different PID than expected.") - } - if generated.TransportErrorIndicator() != true { - t.Errorf("Reads different TransportErrorIndicator than expected.") - } - if generated.PayloadUnitStartIndicator() != true { - t.Errorf("Reads different PayloadUnitStartIndicator than expected.") - } - if generated.TransportPriority() != true { - t.Errorf("Reads different TP than expected.") - } - if generated.AdaptationFieldControl() != PayloadFlag { - t.Errorf("Reads different AdaptationFieldControl than expected.") - } - if generated.TransportScramblingControl() != ScrambleEvenKeyFlag { - t.Errorf("Reads different TransportScramblingControl than expected.") - } -} - -func TestSetPayload(t *testing.T) { - data, _ := hex.DecodeString(testPacket2) - target, _ := FromBytes(data) - payload := []byte{} - tpd := []byte{} - for i := 0; i < 188; i++ { - payload = append(payload, 0x77) - } - for i := 0; i < 188; i++ { - tpd = append(tpd, 0x99) - } - - copyAF := NewAdaptationField() - err := copyAF.SetHasTransportPrivateData(true) - if err != nil { - t.Error(err.Error()) - return - } - err = copyAF.SetTransportPrivateData(tpd[:70]) - if err != nil { - t.Error(err.Error()) - return - } - p := New() - err = p.SetAdaptationFieldControl(AdaptationFieldFlag) - if err != nil { - t.Error(err.Error()) - return - } - err = p.SetAdaptationFieldControl(PayloadAndAdaptationFieldFlag) - if err != nil { - t.Error(err.Error()) - return - } - af, _ := p.AdaptationField() - err = af.SetHasTransportPrivateData(true) - if err != nil { - t.Error(err.Error()) - return - } - err = af.SetTransportPrivateData(tpd[:100]) - if err != nil { - t.Error(err.Error()) - return - } - _, err = p.SetPayload(payload[:70]) - if err != nil { - t.Error(err.Error()) - return - } - err = p.SetAdaptationField(copyAF) - if err != nil { - t.Error(err.Error()) - return - } - _, err = p.SetPayload(payload[:100]) - if err != nil { - t.Error(err.Error()) - return - } - if !Equal(target, p) { - t.Errorf("crafted packet:\n%X \ndoes not match expected packet:\n%X\nSetting the payload failed.", p, target) - } - payloadInPacket, _ := p.Payload() - if !bytes.Equal(payload[:100], payloadInPacket) { - t.Errorf("payload in packet is incorrect.") - } -} - -func BenchmarkNewStyleAllFields(b *testing.B) { - for n := 0; n < b.N; n++ { - // create everything - p := New() - p.SetContinuityCounter(7) - p.IncContinuityCounter() - p.SetPID(4000) - p.SetTransportErrorIndicator(false) - p.SetPayloadUnitStartIndicator(true) - p.SetTransportPriority(false) - p.SetAdaptationFieldControl(AdaptationFieldFlag) - p.SetTransportScramblingControl(ScrambleEvenKeyFlag) - - a, _ := p.AdaptationField() - a.SetHasPCR(true) - a.SetHasOPCR(true) - a.SetHasSplicingPoint(true) - a.SetHasTransportPrivateData(true) - a.SetHasAdaptationFieldExtension(true) - a.SetElementaryStreamPriority(true) - a.SetRandomAccess(true) - a.SetHasAdaptationFieldExtension(true) - - // read everything - p.ContinuityCounter() - p.HasAdaptationField() - p.HasPayload() - p.IsNull() - p.IsPAT() - p.IsPAT() - p.PID() - p.PayloadUnitStartIndicator() - p.TransportErrorIndicator() - p.TransportPriority() - p.TransportScramblingControl() - - a, _ = p.AdaptationField() - a.Length() - a.Discontinuity() - a.ElementaryStreamPriority() - a.HasAdaptationFieldExtension() - a.HasOPCR() - a.HasPCR() - a.HasSplicingPoint() - a.HasTransportPrivateData() - a.RandomAccess() - } -} - -func BenchmarkNewStyleCreate(b *testing.B) { - for n := 0; n < b.N; n++ { - pkt := New() - pkt.SetPID(13) - pkt.SetAdaptationFieldControl(PayloadFlag) - pkt.SetPayloadUnitStartIndicator(true) - pkt.SetContinuityCounter(7) - } -} - -func BenchmarkOldStyleCreate(b *testing.B) { - for n := 0; n < b.N; n++ { - SetCC( - Create( - 13, - WithHasPayloadFlag, - WithPUSI), - 7) - } -} - -func BenchmarkNewStyleRead(b *testing.B) { - pkt := New() - pkt.SetPID(13) - pkt.SetAdaptationFieldControl(PayloadFlag) - pkt.SetPayloadUnitStartIndicator(true) - pkt.CheckErrors() // no errors possible - - b.ResetTimer() - for n := 0; n < b.N; n++ { - pkt.ContinuityCounter() - pkt.PayloadUnitStartIndicator() - pkt.AdaptationFieldControl() - pkt.PID() - } -} - -func BenchmarkOldStyleRead(b *testing.B) { - pkt := New() - pkt.SetPID(13) - pkt.SetAdaptationFieldControl(PayloadFlag) - pkt.SetPayloadUnitStartIndicator(true) - pkt.CheckErrors() // no errors possible - - b.ResetTimer() - for n := 0; n < b.N; n++ { - ContinuityCounter(pkt) - PayloadUnitStartIndicator(pkt) - ContainsPayload(pkt) - Pid(pkt) - } -} diff --git a/v2/packet/packet.go b/v2/packet/packet.go deleted file mode 100644 index 9cc012f..0000000 --- a/v2/packet/packet.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "github.com/Comcast/gots/v2" -) - -const ( - // PacketSize is the expected size of a packet in bytes - PacketSize = 188 - // SyncByte is the expected value of the sync byte - SyncByte = 71 // 0x47 (0100 0111) - // NullPacketPid is the pid reserved for null packets - NullPacketPid = 8191 // 0x1FFF -) - -// TransportScramblingControlOptions is a set of constants for -// selecting the transport scrambling control. -type TransportScramblingControlOptions byte - -const ( - NoScrambleFlag TransportScramblingControlOptions = 0 // 00 - ScrambleEvenKeyFlag TransportScramblingControlOptions = 2 // 10 - ScrambleOddKeyFlag TransportScramblingControlOptions = 3 // 11 -) - -// Packet is the basic unit in a transport stream. -type Packet [PacketSize]byte - -// PayloadUnitStartIndicator (PUSI) is a flag that indicates the start of PES data -// or PSI (Program-Specific Information) such as AT, CAT, PMT or NIT. The PUSI -// flag is contained in the second bit of the second byte of the Packet. -func PayloadUnitStartIndicator(packet *Packet) bool { - return packet[1]&0x040 != 0 -} - -// PID is the Packet Identifier. Each table or elementary stream in the -// transport stream is identified by a PID. The PID is contained in the 13 -// bits that span the last 5 bits of second byte and all bits in the byte. -func Pid(packet *Packet) int { - return int(packet[1]&0x1f)<<8 | int(packet[2]) -} - -// ContainsPayload is a flag that indicates the packet has a payload. The flag is -// contained in the 3rd bit of the 4th byte of the Packet. -func ContainsPayload(packet *Packet) bool { - return packet[3]&0x10 != 0 -} - -// ContainsAdaptationField is a flag that indicates the packet has an adaptation field. -func ContainsAdaptationField(packet *Packet) bool { - return packet[3]&0x20 != 0 -} - -// ContinuityCounter is a 4-bit sequence number of payload packets. Incremented -// only when a payload is present (see ContainsPayload() above). -func ContinuityCounter(packet *Packet) uint8 { - return packet[3] & uint8(0x0f) -} - -// IsNull returns true if the provided packet is a Null packet -// (i.e., PID == 0x1fff (8191)). -func IsNull(packet *Packet) bool { - return Pid(packet) == NullPacketPid -} - -// IsPat returns true if the provided packet is a PAT -func IsPat(packet *Packet) bool { - return Pid(packet) == 0 -} - -// Returns the index of the first byte of Payload data in packetBytes. -func payloadStart(packet *Packet) int { - var dataOffset = int(4) // packet header bytes - if ContainsAdaptationField(packet) { - afLength := int(packet[4]) - dataOffset += 1 + afLength - } - - return dataOffset -} - -// Payload returns a slice containing the packet payload. If the packet -// does not have a payload, an empty byte slice is returned -func Payload(packet *Packet) ([]byte, error) { - if !ContainsPayload(packet) { - return nil, gots.ErrNoPayload - } - start := payloadStart(packet) - if start > len(packet) { - return nil, gots.ErrInvalidPacketLength - } - pay := packet[start:] - return pay, nil -} - -// IncrementCC creates a new packet where the new packet has -// a continuity counter that is increased by one -func IncrementCC(packet *Packet) *Packet { - var newPacket Packet - copy(newPacket[:], packet[:]) - ccByte := newPacket[3] - newCC := increment4BitInt(ccByte) - newCCByte := (ccByte & byte(0xf0)) | newCC - newPacket[3] = newCCByte - return &newPacket -} - -// ZeroCC creates a new packet where the new packet has -// a continuity counter that zero -func ZeroCC(packet *Packet) *Packet { - var newPacket Packet - copy(newPacket[:], packet[:]) - ccByte := newPacket[3] - newCCByte := ccByte & byte(0xf0) - newPacket[3] = newCCByte - return &newPacket -} -func increment4BitInt(cc uint8) uint8 { - return (cc + 1) & 0x0f -} - -// SetCC creates a new packet where the new packet has -// the continuity counter provided -func SetCC(packet *Packet, newCC uint8) *Packet { - var newPacket Packet - copy(newPacket[:], packet[:]) - ccByte := newPacket[3] - newCCByte := (ccByte & byte(0xf0)) | newCC - newPacket[3] = newCCByte - return &newPacket -} - -// PESHeader returns a byte slice containing the PES header if the Packet contains one, -// otherwise returns an error -func PESHeader(packet *Packet) ([]byte, error) { - if PayloadUnitStartIndicator(packet) { - pay, err := Payload(packet) - if err != nil { - return nil, err - } - if len(pay) > 3 && pay[0] == 0 && pay[1] == 0 && pay[2] == 1 { - return pay, nil - } - } - return nil, gots.ErrNoPayload -} - -// Header Returns a slice containing the Packer Header. -func Header(packet *Packet) []byte { - start := payloadStart(packet) - return packet[:start] -} - -// Equal returns true if the bytes of the two packets are equal -func Equal(a, b *Packet) bool { - if a == b { - return true - } - if a == nil || b == nil { - return false - } - return *a == *b -} - -// CopyPackets returns a copy of the given packets with new memory -func CopyPackets(packets []*Packet) []*Packet { - newPackets := make([]*Packet, len(packets)) - for i, pkt := range packets { - pktCopy := &Packet{} - copy(pktCopy[:], pkt[:]) - newPackets[i] = pktCopy - } - return newPackets -} diff --git a/v2/packet/packet_test.go b/v2/packet/packet_test.go deleted file mode 100644 index 1a81cfb..0000000 --- a/v2/packet/packet_test.go +++ /dev/null @@ -1,349 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "bytes" - "encoding/hex" - "testing" -) - -func parseHexString(h string) *Packet { - b, err := hex.DecodeString(h) - if err != nil { - panic("bad test: " + h) - } - pkt := new(Packet) - if copy(pkt[:], b) != PacketSize { - panic("bad test (wrong length): " + h) - } - return pkt -} - -func TestPayloadUnitStartIndicatorTrue(t *testing.T) { - packet := parseHexString( - "474000130000b00d0001c700000001e0642273423bffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - expected := true - if pusi := PayloadUnitStartIndicator(packet); pusi != expected { - t.Errorf("PayloadUnitStartIndicator() = %t, want %t", pusi, expected) - } -} - -func TestPayloadUnitStartIndicatorFalse(t *testing.T) { - packet := parseHexString( - "4700673b7000ffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffd55e98825c522faa6d37de" + - "cb2e84e04544cd54b9ffb497e788f2b308d1a71d4f2bce4c18c563b92ecd954b" + - "558e7ca55796ca56ed4020812c21f1013ff5497f875897f58ffeeb1c") - expected := false - if pusi := PayloadUnitStartIndicator(packet); pusi != expected { - t.Errorf("PayloadUnitStartIndicator() = %t, want %t", pusi, expected) - } -} - -func TestPid(t *testing.T) { - packet := parseHexString( - "47406618000001c000f280800523fae5b8a3fff94c801d4010210994fd959f4b" + - "6108806a912e4b972d025c92429595817016dca64a18e7fc5c271bb40a0f9150" + - "3c0057776bdd66c0e9ab2ba7614de80ee468cc6e860846241710cfda6dabc569" + - "79279065d30a93c8d5584d12b87b35938e18f2868f149f3dec38cae665db77bd" + - "0ba9b7a659363d7347d22f835b4e53f6472f01be53d7df28ea7f1764972f5549" + - "34096bd6bf42eabe1dff1c59e0cc55a716b6a40618b3305b45779c31") - expected := 102 - if pid := Pid(packet); pid != expected { - t.Errorf("Pid() = %d, want %d", pid, expected) - } -} - -func TestPidGreaterThan255(t *testing.T) { - packet := parseHexString( - "4701221B000001c000f280800523fae5b8a3fff94c801d4010210994fd959f4b" + - "6108806a912e4b972d025c92429595817016dca64a18e7fc5c271bb40a0f9150" + - "3c0057776bdd66c0e9ab2ba7614de80ee468cc6e860846241710cfda6dabc569" + - "79279065d30a93c8d5584d12b87b35938e18f2868f149f3dec38cae665db77bd" + - "0ba9b7a659363d7347d22f835b4e53f6472f01be53d7df28ea7f1764972f5549" + - "34096bd6bf42eabe1dff1c59e0cc55a716b6a40618b3305b45779c31") - expected := 290 - if pid := Pid(packet); pid != expected { - t.Errorf("Pid() = %d, want %d", pid, expected) - } -} - -func TestContainsPayloadTrue(t *testing.T) { - packet := parseHexString( - "47406618000001c000f280800523fae5b8a3fff94c801d4010210994fd959f4b" + - "6108806a912e4b972d025c92429595817016dca64a18e7fc5c271bb40a0f9150" + - "3c0057776bdd66c0e9ab2ba7614de80ee468cc6e860846241710cfda6dabc569" + - "79279065d30a93c8d5584d12b87b35938e18f2868f149f3dec38cae665db77bd" + - "0ba9b7a659363d7347d22f835b4e53f6472f01be53d7df28ea7f1764972f5549" + - "34096bd6bf42eabe1dff1c59e0cc55a716b6a40618b3305b45779c31") - expected := true - if containsPayload := ContainsPayload(packet); containsPayload != expected { - t.Errorf("ContainsPayload() = %t, want %tv", containsPayload, expected) - } -} - -func TestContainsPayloadFalse(t *testing.T) { - packet := parseHexString( - "47006523b7103f5c99597ef7ffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - expected := false - if containsPayload := ContainsPayload(packet); containsPayload != expected { - t.Errorf("ContainsPayload() = %t, want %t", containsPayload, expected) - } -} - -func TestContinuityCounter(t *testing.T) { - packet := parseHexString( - "47006518dc0eff960f094176e794721d00cfedc13c1b039abf71e0f16bfeef88" + - "de1d1901a576793da53551cfc53363e00be1417c08383ce8bc51efda4c4a465c" + - "9aee27f76997169968829cf3343253c16243f7c21602cb2161767fda0485d4de" + - "87bf26954b148497393886b854288985a50059dd5cbfdf61b3f701793cd3bdf0" + - "43e5d197998e05e2a50f590f6923d45490c81750d5f603643f974a2bde5f4812" + - "749dd96f61281aca2cb496c01f01e3152fcba48c2ec78314ab21da3b") - expected := uint8(8) - if cc := ContinuityCounter(packet); cc != expected { - t.Errorf("ContinuityCounter() = %d, want %d", cc, expected) - } -} - -func TestZeroLenthAdaptationField(t *testing.T) { - packet := parseHexString( - "4701e1320034fcabf65d866a87eca0195db5ce1dcb6e0f75ba45a351722714db" + - "a013cea9665e9e1866b13431429454a37cb5663ea00353624c5d1f84c9463651" + - "634497dd837080b99ddf4bb26242f18d22ecd74dde47cd84041e5df3f0c57c40" + - "e1c6782394c5dbb02a1896ca6712dc232a53958f47596c570c70f90e44303188" + - "89ace8b8b378a515b088341942220c44578c157ee4313d123db73ec2a2726a29" + - "9ab1852b9314ae15fad86177607b75be718f0c07d22400845160d980") - - expected := true - if hasadapt := ContainsAdaptationField(packet); hasadapt != expected { - t.Errorf("ContainsAdaptationField() = %v, want %v", hasadapt, expected) - } -} - -func TestPayloadWhenPacketHasNoAdaptationField(t *testing.T) { - packet := parseHexString( - "47006518dc0eff960f094176e794721d00cfedc13c1b039abf71e0f16bfeef88" + - "de1d1901a576793da53551cfc53363e00be1417c08383ce8bc51efda4c4a465c" + - "9aee27f76997169968829cf3343253c16243f7c21602cb2161767fda0485d4de" + - "87bf26954b148497393886b854288985a50059dd5cbfdf61b3f701793cd3bdf0" + - "43e5d197998e05e2a50f590f6923d45490c81750d5f603643f974a2bde5f4812" + - "749dd96f61281aca2cb496c01f01e3152fcba48c2ec78314ab21da3b") - - expected, _ := hex.DecodeString( - "dc0eff960f094176e794721d00cfedc13c1b039abf71e0f16bfeef88de1d1901" + - "a576793da53551cfc53363e00be1417c08383ce8bc51efda4c4a465c9aee27f7" + - "6997169968829cf3343253c16243f7c21602cb2161767fda0485d4de87bf2695" + - "4b148497393886b854288985a50059dd5cbfdf61b3f701793cd3bdf043e5d197" + - "998e05e2a50f590f6923d45490c81750d5f603643f974a2bde5f4812749dd96f" + - "61281aca2cb496c01f01e3152fcba48c2ec78314ab21da3b") - - if payload, err := Payload(packet); !(bytes.Equal(payload, expected)) || err != nil { - t.Errorf("Payload() = %x, want %x err=%v", payload, expected, err) - } -} - -func TestPayloadWhenPacketHasAdaptationField(t *testing.T) { - packet := parseHexString( - "4740653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - - expected, _ := hex.DecodeString( - "000001e0000084c00a33faf9760713faf900b900000001091000000001274d40" + - "1f9a6281004b602d1000003e90000ea60e8601d400057e4bbcb8280000000128ee38" + - "8000000001060007818a378085f8c00104007820100601c40411b500314741393403" + - "c2fffd2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f" + - "78053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - - if payload, err := Payload(packet); !(bytes.Equal(payload, expected)) || err != nil { - t.Errorf("Payload() = %x, want %x err=%v", payload, expected, err) - } -} - -func TestIncrementCC(t *testing.T) { - packet := parseHexString( - "4700673b7000ffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffd55e98825c522faa6d37de" + - "cb2e84e04544cd54b9ffb497e788f2b308d1a71d4f2bce4c18c563b92ecd954b" + - "558e7ca55796ca56ed4020812c21f1013ff5497f875897f58ffeeb1c") - packet[3] = byte(0x00) - newPacket := IncrementCC(packet) - - expected := uint8(1) - if expected != newPacket[3] { - t.Errorf("CC= %x, want %x", newPacket[3], expected) - } -} - -func TestIncrementCCFunc(t *testing.T) { - for i := byte(0); i < 16; i++ { - if i == 15 && increment4BitInt(i) != 0 { - t.Errorf("IncrementingCC from 15 to rollover did not cause a 0") - } - if i == 15 { - continue - } - res := increment4BitInt(i) - if res != i+1 { - t.Errorf("IncrementingCC from %d expected %d was %d", i, i+1, res) - } - } -} - -func TestContainsAdaptationField(t *testing.T) { - packet := parseHexString( - "4700663a7700ffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0b82aaa" + - "bd5a13a6b27a23c4e556bd78ccdb05c72a3a5cac278d76d89aae3de5241728ea" + - "ea79344f6ff95e63bd6060c9462c42b33a89b3fcff000480003e71c0") - if exists := ContainsAdaptationField(packet); !exists { - t.Error("Did not correctly find presence of adaptation field") - } -} - -func TestEqualsNilPacket(t *testing.T) { - packet := parseHexString( - "4740653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - if Equal(packet, nil) { - t.Error("Nil packet should not be equal to a non-nil packet") - } -} - -func TestEqualsIdenticalPackets(t *testing.T) { - packet := parseHexString( - "4740653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - same := *packet - if !Equal(packet, &same) { - t.Errorf("Identical packets are different p1%v p2%v", packet, &same) - } -} - -func TestEqualsHeadersNotEqual(t *testing.T) { - packet1 := parseHexString( - "4740653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - - // Same as above, but with the MPEG-TS headers TEI bit flipped. - packet2 := parseHexString( - "4780653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - - if Equal(packet1, packet2) { - t.Errorf("Packets should be different\n p1%v\n p2%v", packet1, packet2) - } -} - -func TestNullPacketIsNull(t *testing.T) { - p := parseHexString( - "471fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - if isNull := IsNull(p); isNull == false { - pid := Pid(p) - t.Errorf("Packets with PID == %d should be null. PID was %v", NullPacketPid, pid) - } -} - -func TestNonNullPacketIsNotNull(t *testing.T) { - packet1 := parseHexString( - "4740653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - - if isNull := IsNull(packet1); isNull == true { - t.Errorf("Packets with PID != %d should not be null.", NullPacketPid) - } -} - -func TestIsPat(t *testing.T) { - pat := parseHexString( - "4740001f0000b00d0031e100000001e064bfcd282fffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - if isPat := IsPat(pat); isPat == false { - t.Error("PAT packet should be counted as a PAT") - } - - notPat := parseHexString( - "4740653214723f5d09c67ec90ca90ad800d6ae02c11e66772d000001e0000084" + - "c00a33faf9760713faf900b900000001091000000001274d401f9a6281004b60" + - "2d1000003e90000ea60e8601d400057e4bbcb8280000000128ee388000000001" + - "060007818a378085f8c00104007820100601c40411b500314741393403c2fffd" + - "2980fc8080ff800000000125b80100017fb2c69de69e51f57c4a1b8623115f78" + - "053598e7f47c066bf03c90c6233c0405369fd5f8e20957e40437f784") - - if isPat2 := IsPat(notPat); isPat2 == true { - t.Error("Non PAT Packet shouldn't be counted as a PAT") - } -} diff --git a/v2/packet/packetwriter.go b/v2/packet/packetwriter.go deleted file mode 100644 index bc6e100..0000000 --- a/v2/packet/packetwriter.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package packet - -import ( - "io" - - "github.com/Comcast/gots/v2" -) - -// PacketWriter is subject to all rules governing implementations of io.Writer -// -// Additionally PacketWriter implementations must not modify or retain packet -// pointers, even temporarily. -type PacketWriter interface { - WritePacket(p *Packet) (n int, err error) -} - -// PacketWriteCloser is subject to all rules governing implementations of -// io.Writer -// -// Additionally PacketWriter implementations must not modify or retain packet -// array pointers, even temporarily. -type PacketWriteCloser interface { - PacketWriter - io.Closer -} - -// Writer is the interface that groups PacketWriter and io.Writer methods -type Writer interface { - PacketWriter - io.Writer -} - -// WriteCloser is the interface that groups PacketWriteCloser and io.Writer methods -type WriteCloser interface { - PacketWriteCloser - io.Writer -} - -type packetWriter struct { - PacketWriteCloser - pkt Packet -} - -func (pw *packetWriter) Write(p []byte) (n int, err error) { - if len(p)%PacketSize != 0 { - return 0, gots.ErrInvalidPacketLength - } - - for i := 0; i < len(p); i += PacketSize { - copy(pw.pkt[:], p[i:]) - if m, err := pw.WritePacket(&pw.pkt); err == nil { - n += m - } else { - return n + m, err - } - } - - if len(p) > n { - err = io.ErrShortWrite - } - return -} - -func (pw *packetWriter) ReadFrom(r io.Reader) (n int64, err error) { - buf := pw.pkt[:] - for { - nr, er := r.Read(buf) - if nr == PacketSize { - nw, ew := pw.WritePacket(&pw.pkt) - if nw > 0 { - n += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } else if nr > 0 && nr != PacketSize { - err = gots.ErrInvalidPacketLength - } - if er != nil { - if er != io.EOF { - err = er - } - break - } - } - return n, err -} - -// IOWriter returns a Writer with a default implementation of Write method -// wrapping the provided PacketWriter w. -func IOWriter(w PacketWriter) Writer { - return &packetWriter{PacketWriteCloser: NopCloser(w)} -} - -// IOWriteCloser returns a WriteCloser with a default implementation of Write -// method wrapping the provided PacketWriteCloser w. -func IOWriteCloser(w PacketWriteCloser) WriteCloser { - return &packetWriter{PacketWriteCloser: w} -} - -type nopCloser struct { - PacketWriter -} - -func (nopCloser) Close() error { return nil } - -// NopCloser returns a PacketWriteCloser with a no-op Close method wrapping -// the provided PacketWriter r. -func NopCloser(r PacketWriter) PacketWriteCloser { - return nopCloser{r} -} - -// The PacketWriterFunc type is an adapter to allow the use of -// ordinary functions as PacketWriters. If f is a function -// with the appropriate signature, PacketWriterFunc(f) is a -// PacketWriter that calls f. -type PacketWriterFunc func(*Packet) (int, error) - -// WritePacket calls f(p). -func (f PacketWriterFunc) WritePacket(p *Packet) (n int, err error) { - return f(p) -} diff --git a/v2/pcr.go b/v2/pcr.go deleted file mode 100644 index d1d0899..0000000 --- a/v2/pcr.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package gots - -// ExtractPCR extracts a PCR time -// PCR is the Program Clock Reference. -// First 33 bits are PCR base. -// Next 6 bits are reserved. -// Final 9 bits are PCR extension. -func ExtractPCR(bytes []byte) uint64 { - var a, b, c, d, e, f uint64 - a = uint64(bytes[0]) - b = uint64(bytes[1]) - c = uint64(bytes[2]) - d = uint64(bytes[3]) - e = uint64(bytes[4]) - f = uint64(bytes[5]) - pcrBase := (a << 25) | (b << 17) | (c << 9) | (d << 1) | (e >> 7) - pcrExt := ((e & 0x1) << 8) | f - return pcrBase*300 + pcrExt -} - -// InsertPCR insterts a given pcr time into a byte slice. -func InsertPCR(b []byte, pcr uint64) { - pcrBase := pcr / 300 - pcrExt := (pcr - pcrBase*300) & 0x1ff - b[0] = byte(pcrBase >> 25) - b[1] = byte(pcrBase >> 17) - b[2] = byte(pcrBase >> 9) - b[3] = byte(pcrBase >> 1) - b[4] = byte((pcrBase << 7) | (pcrExt >> 8) | 0x7e) - b[5] = byte(pcrExt & 0xff) -} diff --git a/v2/pcr_test.go b/v2/pcr_test.go deleted file mode 100644 index 688e535..0000000 --- a/v2/pcr_test.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package gots - -import ( - "testing" -) - -func TestExtractPCR(t *testing.T) { - b := []byte{168, 149, 227, 4, 0, 126} - pcr := ExtractPCR(b) - if pcr != 1697037160926 { - t.Errorf("ExtractPCR returned %v", pcr) - } -} - -func TestInsertPCR(t *testing.T) { - b := make([]byte, 6) - - var pcr uint64 = 1697037160926 - InsertPCR(b, pcr) - if ExtractPCR(b) != pcr { - t.Errorf("Insert PCR test 1 failed: %v", b) - } - - var pcr2 uint64 = 2576980377599 - InsertPCR(b, pcr2) - if ExtractPCR(b) != pcr2 { - t.Errorf("Insert PCR test 2 failed: %v (%v)", b, ExtractPCR(b)) - } -} diff --git a/v2/pes/doc.go b/v2/pes/doc.go deleted file mode 100644 index dffc29c..0000000 --- a/v2/pes/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Package pes contains interfaces and operations for packetized elementary stream headers. -package pes diff --git a/v2/pes/pes.go b/v2/pes/pes.go deleted file mode 100644 index 1215d3b..0000000 --- a/v2/pes/pes.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package pes - -import "github.com/Comcast/gots/v2/packet" - -// AlignedPUSI checks for a PUSI with aligned flag set and returns a bool -// indicating a match when true, as well as the bytes for the PES data -func AlignedPUSI(pkt *packet.Packet) ([]byte, bool) { - if !pkt.PayloadUnitStartIndicator() { - } else if pesHeaderBytes, err := packet.PESHeader(pkt); err != nil { - } else if pesHeader, err := NewPESHeader(pesHeaderBytes); err != nil { - } else if pesHeader != nil && pesHeader.DataAligned() { - return pesHeader.Data(), true - } - return nil, false -} diff --git a/v2/pes/pesheader.go b/v2/pes/pesheader.go deleted file mode 100644 index c7f561c..0000000 --- a/v2/pes/pesheader.go +++ /dev/null @@ -1,293 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package pes - -import ( - "errors" - "fmt" - - "github.com/Comcast/gots/v2" -) - -// stream_id possibilities -const ( - STREAM_ID_ALL_AUDIO_STREAMS uint8 = 184 - STREAM_ID_ALL_VIDEO_STREAMS = 185 - STREAM_ID_PROGRAM_STREAM_MAP = 188 - STREAM_ID_PRIVATE_STREAM_1 = 189 - STREAM_ID_PADDNG_STREAM = 190 - STREAM_ID_PRIVATE_STREAM_2 = 191 - STREAM_ID_ECM_STREAM = 240 - STREAM_ID_EMM_STREAM = 241 - STREAM_ID_DSM_CC_STREAM = 242 - STREAM_ID_ISO_IEC_13552_STREAM = 243 - STREAM_ID_ITU_T_H222_1_TYPE_A = 244 - STREAM_ID_ITU_T_H222_1_TYPE_B = 245 - STREAM_ID_ITU_T_H222_1_TYPE_C = 246 - STREAM_ID_ITU_T_H222_1_TYPE_D = 247 - STREAM_ID_ITU_T_H222_1_TYPE_E = 248 - STREAM_ID_ANCILLARY_STREAM = 249 - STREAM_ID_MPEG_4_SL_PACKETIZED_STREAM = 250 - STREAM_ID_MPEG_4_FLEXMUX_STREAM = 251 - STREAM_ID_METADATA_STREAM = 252 - STREAM_ID_EXTENDED_STREAM_ID = 253 - STREAM_ID_RESERVED = 254 - STREAM_ID_PROGRAM_STREAM_DIRECTORY = 255 -) - -// PESHeader represents operations available on a packetized elementary stream header. -type PESHeader interface { - // HasPTS returns true if the header has a PTS time - HasPTS() bool - //PTS return the PTS time in the header - PTS() uint64 - // HasDTS returns true if the header has a DTS time - HasDTS() bool - //DTS return the DTS time in the header - DTS() uint64 - // Data returns the PES data - Data() []byte - // StreamId returns the stream id - StreamId() uint8 - // DataAligned returns true if the data_alignment_indicator is set - DataAligned() bool - // PacketStartCodePrefix returns the packet_start_code_prefix. Note that this is a 24 bit value. - PacketStartCodePrefix() uint32 -} - -/* - * ============================================================ - * NAME | # BITS - * ============================================================ - * PACKET START CODE PREFIX (0X000001) | 24 - * STREAM ID | 8 - * ex: AUDIO STREAMS (0XC0-0XDF) | - * ex: VIDEO STREAMS (0XE0-0XEF) | - * PES PACKET LENGTH | 16 - * (CAN BE 0; NOT SPECIFIED FOR VIDEO IN MPEGTS) | - * OPTIONAL PES HEADER | VAR - * STUFFING BYTES | VAR - * DATA | - * | - * +--- OPTIONAL PES HEADER | - * | MARKER BITS (BINARY(10) OR HEX(0X2) | 2 - * | SCRAMBLING CONTROL (00 IMPLIES NOT SCRAMBLED) | 2 - * | PRIORITY | 1 - * | DATA ALIGNMENT INDICATOR | 1 - * | 1 INDICATES THAT THE PES PACKET HEADER IS | - * | IMMEDIATELY FOLLOWED BY THE VIDEO START CODE | - * | OR AUDIO SYNCWORD | - * | COPYRIGHT (1 IMPLIES COPYRIGHTED) | 1 - * | ORIGINAL OR COPY (1 IMPLIES ORIGINAL) | 1 - * | PTS DTS INDICATOR (11=BOTH, 10=ONLY PTS) | 2 - * | ESCR FLAG | 1 - * | ES RATE FLAG | 1 - * | DSM TRICK MODE FLAG | 1 - * | ADDITIONAL COPY INFO FLAG | 1 - * | CRC FLAG | 1 - * | EXTENSION FLAG | 1 - * | PES HEADER LENGTH (REMAINDER LENGTH OF HEADER) | 8 - * | | - * | +--- OPTIONAL FIELDS (IF PTS_DTS_FLAG = 10) | - * | | MARKER BITS '0010' | 4 - * | | PTS 32-30 | 3 - * | | MARKER BIT '1' | 1 - * | | PTS 29-15 | 15 - * | | MARKER BIT '1' | 1 - * | | PTS 14-0 | 15 - * | | MARKER BIT '1' | 1 - * | +--- OPTIONAL FIELDS (IF PTS_DTS_FLAG = 10) | - * | | - * | +--- OPTIONAL FIELDS (IF PTS_DTS_FLAG = 11) | - * | | MARKER BITS '0011' | 4 - * | | PTS 32-30 | 3 - * | | MARKER BIT '1' | 1 - * | | PTS 29-15 | 15 - * | | MARKER BIT '1' | 1 - * | | PTS 14-0 | 15 - * | | MARKER BIT '1' | 1 - * | | MARKER BITS '0001' | 4 - * | | DTS 32-30 | 3 - * | | MARKER BIT '1' | 1 - * | | DTS 29-15 | 15 - * | | MARKER BIT '1' | 1 - * | | DTS 14-0 | 15 - * | | MARKER_BIT '1' | 1 - * | +--- OPTIONAL FIELDS (IF PTS_DTS_FLAG = 10) | - * | | - * | OPTIONAL FIELDS (PRESENCE IS SET BY FLAG ABOVE) | VAR - * | STUFFING BYTES (0xFF) | VAR - * +--- OPTIONAL PES HEADER | - * ============================================================ - */ -type pESHeader struct { - packetStartCodePrefix uint32 - dataAlignment bool - streamId uint8 - pesPacketLength uint16 - ptsDtsIndicator uint8 - pts uint64 - dts uint64 - data []byte -} - -// ExtractTime extracts a PTS time -func ExtractTime(bytes []byte) uint64 { - var a, b, c, d, e uint64 - a = uint64((bytes[0] >> 1) & 0x07) - b = uint64(bytes[1]) - c = uint64((bytes[2] >> 1) & 0x7f) - d = uint64(bytes[3]) - e = uint64((bytes[4] >> 1) & 0x7f) - return (a << 30) | (b << 22) | (c << 15) | (d << 7) | e -} - -// NewPESHeader creates a new PES header with the provided bytes. -// pesBytes is the packet payload that contains the PES data -func NewPESHeader(pesBytes []byte) (PESHeader, error) { - pes := new(pESHeader) - var err error - - if CheckLength(pesBytes, "PES", 7) { - - pes.packetStartCodePrefix = uint32(pesBytes[0])<<16 | uint32(pesBytes[1])<<8 | uint32(pesBytes[2]) - - pes.streamId = uint8(pesBytes[3]) - - pes.pesPacketLength = uint16(pesBytes[4])<<8 | uint16(pesBytes[5]) - pes.dataAlignment = pesBytes[6]&0x04 != 0 - dataStartIndex := 6 - - if pes.optionalFieldsExist() && CheckLength(pesBytes, "Optional Fields", 9) { - - ptsDtsIndicator := (uint8(pesBytes[7]) & 0xc0 >> 6) - - pesHeaderLength := pesBytes[8] - dataStartIndex = 9 + int(pesHeaderLength) - - pes.ptsDtsIndicator = ptsDtsIndicator - - if ptsDtsIndicator != gots.PTS_DTS_INDICATOR_NONE && - CheckLength(pesBytes, "PTS", 14) { - - pes.pts = gots.ExtractTime(pesBytes[9:14]) - - if pes.ptsDtsIndicator == gots.PTS_DTS_INDICATOR_BOTH && - CheckLength(pesBytes, "DTS", 19) { - - pes.dts = gots.ExtractTime(pesBytes[14:19]) - } - } - - } - - if len(pesBytes) > dataStartIndex { - pes.data = pesBytes[dataStartIndex:] - } - } else { - err = errors.New("Invalid length for PES header. Too short to parse") - } - - return pes, err -} - -func (pes *pESHeader) optionalFieldsExist() bool { - if pes.streamId == STREAM_ID_PADDNG_STREAM || - pes.streamId == STREAM_ID_PRIVATE_STREAM_2 || - pes.streamId == STREAM_ID_ECM_STREAM || - pes.streamId == STREAM_ID_EMM_STREAM || - pes.streamId == STREAM_ID_DSM_CC_STREAM || - pes.streamId == STREAM_ID_ITU_T_H222_1_TYPE_E || - pes.streamId == STREAM_ID_PROGRAM_STREAM_DIRECTORY { - return false - } - return true -} - -func (pes *pESHeader) PacketStartCodePrefix() uint32 { - return pes.packetStartCodePrefix -} - -func (pes *pESHeader) StreamId() uint8 { - return pes.streamId -} - -func (pes *pESHeader) PTS() uint64 { - return pes.pts -} - -func (pes *pESHeader) DTS() uint64 { - return pes.dts -} - -func (pes *pESHeader) Data() []byte { - return pes.data -} - -func (pes *pESHeader) HasPTS() bool { - return (pes.ptsDtsIndicator & gots.PTS_DTS_INDICATOR_ONLY_PTS) != 0 -} - -func (pes *pESHeader) HasDTS() bool { - return pes.ptsDtsIndicator == gots.PTS_DTS_INDICATOR_BOTH -} - -func (pes *pESHeader) Format() string { - var f = fmt.Sprintf( - "PES\n"+ - "---\n"+ - "Packet Start Code Prefix: %X \n"+ - "Stream Id: %X \n"+ - "PES Packet Length: %d\n", - pes.packetStartCodePrefix, - pes.streamId, - pes.pesPacketLength) - - if pes.optionalFieldsExist() { - ptsDtsIndicator := pes.ptsDtsIndicator - f += fmt.Sprintf("PTS DTS Indicator: %b\n", pes.ptsDtsIndicator) - if ptsDtsIndicator == gots.PTS_DTS_INDICATOR_BOTH || ptsDtsIndicator == gots.PTS_DTS_INDICATOR_ONLY_PTS { - f += fmt.Sprintf("PTS: %d\n", pes.pts) - if ptsDtsIndicator == gots.PTS_DTS_INDICATOR_BOTH { - f += fmt.Sprintf("DTS: %d\n", pes.dts) - } - } - - } - - return f -} - -func (pes *pESHeader) DataAligned() bool { - return pes.dataAlignment -} - -// CheckLength the length of the byte array to avoid index out of bound panics -func CheckLength(byteArray []byte, name string, min int) bool { - if len(byteArray) < min { - return false - } - return true -} diff --git a/v2/pes/pesheader_test.go b/v2/pes/pesheader_test.go deleted file mode 100644 index 105f301..0000000 --- a/v2/pes/pesheader_test.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package pes - -import ( - "encoding/hex" - "testing" - - "github.com/Comcast/gots/v2/packet" -) - -func parseHexString(h string) *packet.Packet { - b, err := hex.DecodeString(h) - if err != nil { - panic("bad test: " + h) - } - pkt := new(packet.Packet) - if copy(pkt[:], b) != packet.PacketSize { - panic("bad test (wrong length): " + h) - } - return pkt -} - -func TestPESHeader(t *testing.T) { - pkt := parseHexString( - "4740661a000001c006ff80800521dee9ca57fff94c801d2000210995341d9d43" + - "61089848180b0884626048901425ddc09249220129d2fce728111c987e67ecb7" + - "4284af5099181d8cd095b841b0c7539ad6c06260536e137615560052369fc984" + - "0b3af532418b3924a6b28d6208a6a9e3d22d533ec89646246c734a696407a95e" + - "3bf230404a4ad0000201038cf0c6a2e32abda45b7effe9f79280a137ed120fd3" + - "bd8252e07cddadbe6d2b60084208500e06f6ceb6acf2c43011c5938b") - pesBytes, err := packet.PESHeader(pkt) - if err != nil { - t.Errorf("Expected that this packet is a PES") - } - pes, err := NewPESHeader(pesBytes) - if err != nil { - t.Error(err) - } - - expected := uint64(934962475) - if pes.PTS() != expected { - t.Errorf("Invalid pts. Expected: %d, Actual: %d", expected, pes.PTS()) - } - if pes.StreamId() != 0xc0 { - t.Errorf("Invalid stream id. Expected: %d, Actual: %d", 0xc0, pes.StreamId()) - } - if pes.PacketStartCodePrefix() != uint32(0x000001) { - t.Errorf("Invalid start code prefix. Expected: %d, Actual: %d", uint32(0x000001), pes.PacketStartCodePrefix()) - } -} - -func TestPESHeader2(t *testing.T) { - pkt := parseHexString( - "4740651C000001E0000084C00A39EFF33A7519EFF30B89000000010950000000" + - "01060104001A20100411B500314741393403C2FFFD8080FC942FFF8000000001" + - "21A81C29145C6FEB86EB239E2EE231302CF5163D32D183B7822FE37E7FB84549" + - "DC1D08780834029F139BDD36E9BBC25B18AE4DE5F508036AEDB9E8A321B93288" + - "4EEF1482E6C77B31E92ADF3BC0D275E5D40864FD3A9806ABC74B98B0E3255EC1" + - "B1C157068EF46E15ED82E7D7C1C0538C4B5B7AF39AEC09386939FE1C") - - pesBytes, err := packet.PESHeader(pkt) - if err != nil { - t.Errorf("Expected that this packet is a PES") - } - pes, err := NewPESHeader(pesBytes) - if err != nil { - t.Error(err) - } - - expected := uint64(5301378362) - if pes.PTS() != expected { - t.Errorf("Invalid pts. Expected: %d, Actual: %d", expected, pes.PTS()) - } - if pes.StreamId() != 0xE0 { - t.Errorf("Invalid stream id. Expected: %d, Actual: %d", 0xe0, pes.StreamId()) - } - if pes.DataAligned() != true { - t.Error("PES header read incorrect data alignment flag") - } -} - -func TestNewPESHeaderMissingBytes(t *testing.T) { - // Actual data from Cisco Transcoder (AMC channel). Below packet was causing - // index out of bounds exception. It has the PES prefix code but we were not - // checking to see if it's a PUSI to begin with - pkt := parseHexString( - "47006531b300ffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff0000010b") - _, err := packet.PESHeader(pkt) - if err == nil { - t.Errorf("Expected that this packet is not a PES since PUSI is 0") - } -} - -func TestPESHeaderTS(t *testing.T) { - pkt := parseHexString( - "4752a31c000001e0000080c00a210005bf21210005a7ab000001000697fffb80" + - "000001b5844ffb9400000001b24741393403d4fffc8080fd8fdffa0000fa0000" + - "fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa00" + - "00fa0000fa0000fa0000fa0000fa0000ff000001014a24afffa4e8b836d7eeee" + - "4dafded260dab9688b2a0d89bed7fd3ad106c1b6bfe5a24a20d89b572ca92544" + - "389b572ca7b441b176bffebd06c5daffe8bd06c9b5fbb8364da6ffad") - - pesBytes, err := packet.PESHeader(pkt) - if err != nil { - t.Errorf("Expected that this packet is a PES") - } - pes, err := NewPESHeader(pesBytes) - if err != nil { - t.Error(err) - } - - expectedPTS := uint64(90000) - if pes.PTS() != expectedPTS { - t.Errorf("Invalid pts. Expected: %d, Actual: %d", expectedPTS, pes.PTS()) - } - - expectedDTS := uint64(86997) - if !pes.HasDTS() { - t.Errorf("Invalid dts indicator.") - } - if pes.DTS() != expectedDTS { - t.Errorf("Invalid dts. Expected: %d, Actual: %d", expectedDTS, pes.DTS()) - } -} diff --git a/v2/psi/doc.go b/v2/psi/doc.go deleted file mode 100644 index 8506c5d..0000000 --- a/v2/psi/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Package psi provides mechanisms for collecting and querying program specific information in an MPEG transport stream. -package psi diff --git a/v2/psi/pat.go b/v2/psi/pat.go deleted file mode 100644 index 2a3e14b..0000000 --- a/v2/psi/pat.go +++ /dev/null @@ -1,156 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "errors" - "io" - - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/packet" -) - -const ( - // PatPid is the PID of a PAT. By definition this value is zero. - PatPid = 0 -) - -// PAT interface represents operations on a Program Association Table. Currently only single program transport streams (SPTS)are supported -type PAT interface { - NumPrograms() int - ProgramMap() map[int]int - SPTSpmtPID() (int, error) -} - -// The Program Association Table (PAT) lists the programs available in transport -// stream. Currently it is assumed that it will always be processing a Single -// Program Transport Stream (SPTS), and thus will only ever receive a PAT that -// contains a single programNumber to PID mapping. -type pat []byte - -// NewPAT constructs a new PAT from the provided bytes. -// patBytes should be concatenated packet payload contents. -// If a 188 byte slice is passed in, NewPAT tries to help and -// treats it as TS packet and builds a PAT from the packet payload. -func NewPAT(patBytes []byte) (PAT, error) { - if len(patBytes) < 13 { - return nil, gots.ErrInvalidPATLength - } - - if len(patBytes) == 188 { - var pkt packet.Packet - copy(pkt[:], patBytes) - var err error - patBytes, err = packet.Payload(&pkt) - if err != nil { - return nil, err - } - } - - return pat(patBytes), nil -} - -// NumPrograms returns the number of programs in this PAT -func (pat pat) NumPrograms() int { - sectionLength := int(SectionLength(pat)) - if len(pat[:]) < sectionLength { - sectionLength = len(pat[:]) - } - numPrograms := int((sectionLength - - 2 - // Transport Stream ID - 1 - // Reserved|VersionNumber|CurrentNextIndicator - 1 - // Section Number - 1 - // Last Section Number - 4) / // CRC32 - 4) // Divided by 4 bytes per program - return numPrograms -} - -// ProgramMap returns a map of program numbers and PIDs of the PMTs -func (pat pat) ProgramMap() map[int]int { - m := make(map[int]int) - - counter := 8 // skip table id et al - - for i := 0; i < pat.NumPrograms(); i++ { - pn := (int(pat[counter+1]) << 8) | int(pat[counter+2]) - - // ignore the top three (reserved) bits - pid := int(pat[counter+3])&0x1f<<8 | int(pat[counter+4]) - - // A value of 0 is reserved for a NIT packet identifier. - if pn > 0 { - m[pn] = pid - } - - counter += 4 - } - - return m -} - -// SPTSpmtPID returns the PMT PID if and only if this pat is for a single program transport stream. If this pat is for a multiprogram transport stream, an error is returned. -func (pat pat) SPTSpmtPID() (int, error) { - if pat.NumPrograms() > 1 { - return 0, errors.New("Not a single program transport stream") - } - for _, pid := range pat.ProgramMap() { - return pid, nil - } - return 0, errors.New("No programs in transport stream") -} - -// ReadPAT extracts a PAT from a reader of a TS stream. It will read until a -// PAT packet is found or EOF is reached. -// It returns a new PAT object parsed from the packet, if found, and otherwise -// returns an error. -func ReadPAT(r io.Reader) (PAT, error) { - var pkt packet.Packet - var pat PAT - for pat == nil { - if _, err := io.ReadFull(r, pkt[:]); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - break - } - return nil, err - } - isPat := packet.IsPat(&pkt) - - if isPat { - pay, err := packet.Payload(&pkt) - if err != nil { - return nil, err - } - cp := make([]byte, len(pay)) - copy(cp, pay) - pat, err := NewPAT(cp) - if err != nil { - return nil, err - } - return pat, nil - } - } - return nil, gots.ErrPATNotFound -} diff --git a/v2/psi/pat_test.go b/v2/psi/pat_test.go deleted file mode 100644 index cdc033c..0000000 --- a/v2/psi/pat_test.go +++ /dev/null @@ -1,138 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "bytes" - "encoding/hex" - "reflect" - "testing" -) - -var testData = []struct { - patBytes string - wantStreams int - wantProgramNumber int - wantProgramMapPID int - wantProgramMap map[int]int -}{ - {"0000b00d0000c100000001e064dee0f320", 1, 1, 100, map[int]int{1: 100}}, - {"0000b0150000c100000001e0640002e0c80003e12ce8f16345", 3, 0, 8192, map[int]int{1: 100, 2: 200, 3: 300}}, -} - -func TestNumPrograms(t *testing.T) { - for _, test := range testData { - pat_bytes, _ := hex.DecodeString(test.patBytes) - - pat, err := NewPAT(pat_bytes) - if err != nil { - t.Errorf("Can't parse PAT table %v", err) - } - - gotStreams := pat.NumPrograms() - if test.wantStreams != gotStreams { - t.Errorf("Invalid number of streams! got %v, want %v", gotStreams, test.wantStreams) - } - } -} -func TestProgramMap(t *testing.T) { - for _, test := range testData { - pat_bytes, _ := hex.DecodeString(test.patBytes) - - pat, err := NewPAT(pat_bytes) - if err != nil { - t.Errorf("Can't parse PAT table %v", err) - } - - gotMap := pat.ProgramMap() - if !reflect.DeepEqual(test.wantProgramMap, gotMap) { - t.Errorf("Wrong Program Map! got %v, want %v", gotMap, test.wantProgramMap) - } - } -} - -func TestSPTSpmtPID(t *testing.T) { - for _, test := range testData { - pat_bytes, _ := hex.DecodeString(test.patBytes) - - pat, err := NewPAT(pat_bytes) - if err != nil { - t.Errorf("Can't parse PAT table %v", err) - } - - gotPMTpid, err := pat.SPTSpmtPID() - if test.wantStreams > 1 { - if err == nil { - t.Fatal("Expected error for MPTS") - } - continue - } else { - if err != nil { - t.Fatal(err) - } - } - - if test.wantProgramMapPID != gotPMTpid { - t.Errorf("Wrong Program Map PID got %v, want %v", gotPMTpid, test.wantProgramMapPID) - } - } -} - -func TestReadPATForSmoke(t *testing.T) { - // requires full packets so cannot use test data above - bs, _ := hex.DecodeString("474000100000b00d0001c100000001e256f803e71bfffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ff474256100002b0300001c10000e131f0060504435545491be121f0042a027e1" + - "f86e225f00f52012a9700e9080c001f41850fa041ee3f6580ffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffff") - r := bytes.NewReader(bs) - pat, err := ReadPAT(r) - if err != nil { - t.Errorf("Unexpected error reading PAT: %v", err) - } - // sanity check (tests integration a bit) - gotMap := pat.ProgramMap() - wantMap := map[int]int{1: 598} - if !reflect.DeepEqual(wantMap, gotMap) { - t.Errorf("PAT read is invalid, did not have expected program map") - } -} - -func TestReadPATIncomplete(t *testing.T) { - bs, _ := hex.DecodeString("47400") // incomplete PAT packet - r := bytes.NewReader(bs) - - _, err := ReadPAT(r) - if err == nil { - t.Errorf("Expected to get error reading PAT, but did not") - } -} diff --git a/v2/psi/pmt.go b/v2/psi/pmt.go deleted file mode 100644 index 7f3847c..0000000 --- a/v2/psi/pmt.go +++ /dev/null @@ -1,486 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/packet" -) - -const PidNotFound int = 1<<16 - 1 - -const ( - programInfoLengthOffset = 10 // includes PSIHeaderLen - pmtEsDescriptorStaticLen uint16 = 5 -) - -// Unaccounted bytes before the end of the SectionLength field -const ( - // Pointerfield(1) + table id(1) + flags(.5) + section length (2.5) - PSIHeaderLen uint16 = 4 - CrcLen uint16 = 4 -) - -// PMT is a Program Map Table. -type PMT interface { - Pids() []int - VersionNumber() uint8 - CurrentNextIndicator() bool - ElementaryStreams() []PmtElementaryStream - RemoveElementaryStreams(pids []int) - IsPidForStreamWherePresentationLagsEbp(pid int) bool - String() string - PIDExists(pid int) bool -} - -type pmt struct { - pids []int - elementaryStreams []PmtElementaryStream - versionNumber uint8 - currentNextIndicator bool -} - -// PmtAccumulatorDoneFunc is a doneFunc that can be used for packet accumulation -// to create a PMT -func PmtAccumulatorDoneFunc(b []byte) (bool, error) { - if len(b) < 1 { - return false, nil - } - - start := 1 + int(PointerField(b)) - if len(b) < start { - return false, nil - } - - sectionBytes := b[start:] - for len(sectionBytes) > 2 && sectionBytes[0] != 0xFF { - tableLength := sectionLength(sectionBytes) - if len(sectionBytes) < int(tableLength)+3 { - return false, nil - } - sectionBytes = sectionBytes[3+tableLength:] - } - - return true, nil -} - -// NewPMT Creates a new PMT from the given bytes. -// pmtBytes should be concatenated packet payload contents. -func NewPMT(pmtBytes []byte) (PMT, error) { - pmt := &pmt{} - err := pmt.parseTables(pmtBytes) - if err != nil { - return nil, err - } - return pmt, nil -} - -func (p *pmt) parseTables(pmtBytes []byte) error { - sectionBytes := pmtBytes[1+PointerField(pmtBytes):] - - for len(sectionBytes) > 2 && sectionBytes[0] != 0xFF { - tableLength := sectionLength(sectionBytes) - - if tableID(sectionBytes) == 0x2 { - err := p.parsePMTSection(sectionBytes[0 : 3+tableLength]) - if err != nil { - return err - } - } - sectionBytes = sectionBytes[3+tableLength:] - } - - return nil -} - -func (p *pmt) parsePMTSection(pmtBytes []byte) error { - var pids []int - var elementaryStreams []PmtElementaryStream - sectionLength := sectionLength(pmtBytes) - - if len(pmtBytes) <= programInfoLengthOffset+1 { - return gots.ErrPMTParse - } - - var err error - p.versionNumber, p.currentNextIndicator, err = tableVersionAndCNI(pmtBytes) - if err != nil { - return err - } - - programInfoLength := uint16(pmtBytes[programInfoLengthOffset]&0x0f)<<8 | - uint16(pmtBytes[programInfoLengthOffset+1]) - - // start at the stream descriptors, parse until the CRC - for offset := programInfoLengthOffset + 2 + programInfoLength; offset < PSIHeaderLen+sectionLength-pmtEsDescriptorStaticLen-CrcLen; { - elementaryStreamType := uint8(pmtBytes[offset]) - elementaryPid := int(pmtBytes[offset+1]&0x1f)<<8 | int(pmtBytes[offset+2]) - pids = append(pids, elementaryPid) - infoLength := uint16(pmtBytes[offset+3]&0x0f)<<8 | uint16(pmtBytes[offset+4]) - - // Move past the es descriptor static data - offset += pmtEsDescriptorStaticLen - var descriptors []PmtDescriptor - if infoLength != 0 && int(infoLength+offset) < len(pmtBytes) { - var descriptorOffset uint16 - for descriptorOffset < infoLength { - tag := uint8(pmtBytes[offset+descriptorOffset]) - descriptorOffset++ - descriptorLength := uint16(pmtBytes[offset+descriptorOffset]) - descriptorOffset++ - startPos := offset + descriptorOffset - endPos := int(offset + descriptorOffset + descriptorLength) - if endPos < len(pmtBytes) { - data := pmtBytes[startPos:endPos] - descriptors = append(descriptors, NewPmtDescriptor(tag, data)) - } else { - return gots.ErrParsePMTDescriptor - } - descriptorOffset += descriptorLength - } - offset += infoLength - } - es := NewPmtElementaryStream(elementaryStreamType, elementaryPid, descriptors) - elementaryStreams = append(elementaryStreams, es) - } - - p.pids = pids - p.elementaryStreams = elementaryStreams - return nil -} - -// Pids returns a slice of Pids -func (p *pmt) Pids() []int { - return p.pids -} - -// VersionNumber returns the version number of the PMT -func (p *pmt) VersionNumber() uint8 { - return p.versionNumber -} - -// CurrentNextIndicator provides a bool for if this PMT is in use yet -func (p *pmt) CurrentNextIndicator() bool { - return p.currentNextIndicator -} - -// ElementaryStreams returns a slice of PMT Elementary Streams -func (p *pmt) ElementaryStreams() []PmtElementaryStream { - return p.elementaryStreams -} - -// RemoveElementaryStreams removes elementary streams in the pmt of the given pids -func (p *pmt) RemoveElementaryStreams(removePids []int) { - for _, pid := range removePids { - for j, s := range p.elementaryStreams { - if pid == s.ElementaryPid() { - p.elementaryStreams = append(p.elementaryStreams[:j], p.elementaryStreams[j+1:]...) - break - } - } - } - - var filteredPids []int - - for _, es := range p.elementaryStreams { - filteredPids = append(filteredPids, es.ElementaryPid()) - } - - p.pids = filteredPids -} - -func (p *pmt) IsPidForStreamWherePresentationLagsEbp(pid int) bool { - for _, s := range p.elementaryStreams { - if pid == s.ElementaryPid() { - return s.IsStreamWherePresentationLagsEbp() - } - } - return false -} - -func (p *pmt) String() string { - var buf bytes.Buffer - buf.WriteString("PMT[") - i := 0 - for _, es := range p.elementaryStreams { - buf.WriteString(fmt.Sprintf("%v", es)) - i++ - if i < len(p.elementaryStreams) { - buf.WriteString(",") - } - } - buf.WriteString("]") - - return buf.String() -} - -func (p *pmt) PIDExists(pid int) bool { - for _, pmtPid := range p.Pids() { - if pmtPid == pid { - return true - } - } - return false -} - -func ExtractCRC(payload []byte) (uint32, error) { - if len(payload) < 4 { - return 0, gots.ErrShortPayload - } - - sectionLength := SectionLength(payload) - - if !CanBuildPMT(payload, sectionLength) { - return 0, gots.ErrPMTParse - } - - end := PSIHeaderLen + sectionLength - - // The CRC is the last 4-bytes of the PSI Table. - data := payload[end-4 : end] - return binary.BigEndian.Uint32(data), nil -} - -func CanBuildPMT(payload []byte, sectionLength uint16) bool { - if len(payload) < int(sectionLength) { - return false - } - return true -} - -// FilterPMTPacketsToPids filters the PMT contents of the provided packet to the PIDs provided and returns a new packet(s). -// For example: if the provided PMT has PIDs 101, 102, and 103 and the provided PIDs are 101 and 102, -// the new PMT will have only descriptors for PID 101 and 102. The descriptor for PID 103 will be stripped from the new PMT packet. -// Returns packets and nil error if all pids are present in the PMT. -// Returns packets and non-nil error if some pids are present in the PMT. -// Returns nil packets and non-nil error if none of the pids are present in the PMT. -func FilterPMTPacketsToPids(packets []*packet.Packet, pids []int) ([]*packet.Packet, error) { - // make sure we have packets - if len(packets) == 0 { - return nil, nil - } - - if len(pids) == 0 { - return packets, nil - } - - // Mush the payloads of all PMT packets into one []byte - var pmtByteBuffer bytes.Buffer - for i := 0; i < len(packets); i++ { - pay, err := packet.Payload(packets[i]) - if err != nil { - return nil, gots.ErrNoPayload - } - pmtByteBuffer.Write(pay) - } - - pmtPayload := pmtByteBuffer.Bytes() - - // Determine if any of the given PIDs aren't in the PMT. - unfilteredPMT, _ := NewPMT(pmtPayload) - - pmtPid := packet.Pid(packets[0]) - var missingPids []int - for _, pid := range pids { - // Ignore PAT and PMT PIDS if they are included. - if !unfilteredPMT.PIDExists(pid) && pid != PatPid && pid != pmtPid { - missingPids = append(missingPids, pid) - } - } - - // Return an error if any of the given PIDs is not present in the PMT. - var returnError error - if len(missingPids) > 0 { - returnError = fmt.Errorf(gots.ErrPIDNotInPMT.Error(), missingPids) - } - - // Return nil packets and an error if none of the PIDs being filtered exist in the PMT. - if len(missingPids) == len(pids) { - return nil, returnError - } - - // include +1 to account for the PointerField field itself - pointerField := PointerField(pmtPayload) + 1 - - var filteredPMT bytes.Buffer - - // Copy everything from the pointerfield offset and move the pmtPayload slice to the start of that - filteredPMT.Write(pmtPayload[:pointerField]) - pmtPayload = pmtPayload[pointerField:] - // Copy the first 12 bytes of the PMT packet. Only section_length will change. - filteredPMT.Write(pmtPayload[:programInfoLengthOffset+2]) - - // Get the section length - sectionLength := sectionLength(pmtPayload) - - // Get program info length - programInfoLength := uint16(pmtPayload[programInfoLengthOffset]&0x0f)<<8 | uint16(pmtPayload[programInfoLengthOffset+1]) - if programInfoLength != 0 { - filteredPMT.Write(pmtPayload[programInfoLengthOffset+2 : programInfoLengthOffset+2+programInfoLength]) - } - - for offset := programInfoLengthOffset + 2 + programInfoLength; offset < PSIHeaderLen+sectionLength-pmtEsDescriptorStaticLen-CrcLen; { - elementaryPid := int(pmtPayload[offset+1]&0x1f)<<8 | int(pmtPayload[offset+2]) - infoLength := uint16(pmtPayload[offset+3]&0x0f)<<8 | uint16(pmtPayload[offset+4]) - - // This is an ES PID we want to keep - if pidIn(pids, elementaryPid) { - // write out the whole es info - filteredPMT.Write(pmtPayload[offset : offset+pmtEsDescriptorStaticLen+infoLength]) - } - offset += pmtEsDescriptorStaticLen + infoLength - } - - // Create the new section length - fPMT := filteredPMT.Bytes() - // section_length is the length of data (including the CRC) in bytes following the section length field (ISO13818: 2.4.4.9) - // This will be the length of our buffer - (Bytes preceding section_length) + CRC - // Bytes preceding = 4 + PointerField value and the CRC = 4, so it turns out to be the length of the buffer - PointerField field - // -1 because we previously added 1 for the pointerfield field itself - newSectionLength := uint16(len(fPMT)) - uint16(pointerField-1) - sectionLengthBytes := make([]byte, 2) - binary.BigEndian.PutUint16(sectionLengthBytes, newSectionLength) - fPMT[pointerField+1] = (fPMT[pointerField+1] & 0xf0) | sectionLengthBytes[0] - fPMT[pointerField+2] = sectionLengthBytes[1] - - // Recalculate the CRC - fPMT = append(fPMT, gots.ComputeCRC(fPMT[pointerField:])...) - - var filteredPMTPackets []*packet.Packet - for _, pkt := range packets { - var pktBuf bytes.Buffer - header := packet.Header(pkt) - pktBuf.Write(header) - if len(fPMT) > 0 { - toWrite := safeSlice(fPMT, 0, packet.PacketSize-len(header)) - // truncate fPMT to the remaining bytes - if len(toWrite) < len(fPMT) { - fPMT = fPMT[len(toWrite):] - } else { - fPMT = nil - } - pktBuf.Write(toWrite) - } else { - // all done - break - } - filteredPMTPackets = append(filteredPMTPackets, padPacket(&pktBuf)) - } - return filteredPMTPackets, returnError -} - -// IsPMT returns true if the provided packet is a PMT -// defined by the PAT provided. Returns ErrNilPAT if pat -// is nil, or any error encountered in parsing the PID -// of pkt. -func IsPMT(pkt *packet.Packet, pat PAT) (bool, error) { - if pat == nil { - return false, gots.ErrNilPAT - } - - pmtMap := pat.ProgramMap() - pid := packet.Pid(pkt) - - for _, mapPID := range pmtMap { - if pid == mapPID { - return true, nil - } - } - - return false, nil -} - -func safeSlice(byteArray []byte, start, end int) []byte { - if end < len(byteArray) { - return byteArray[start:end] - } - return byteArray[start:len(byteArray)] -} - -func padPacket(buf *bytes.Buffer) *packet.Packet { - var pkt packet.Packet - for i := copy(pkt[:], buf.Bytes()); i < packet.PacketSize; i++ { - pkt[i] = 0xff - } - return &pkt -} - -func pidIn(pids []int, target int) bool { - for _, pid := range pids { - if pid == target { - return true - } - } - - return false -} - -// ReadPMT extracts a PMT from a reader of a TS stream. It will read until PMT -// packet(s) are found or EOF is reached. -// It returns a new PMT object parsed from the packet(s), if found, and -// otherwise returns an error. -func ReadPMT(r io.Reader, pid int) (PMT, error) { - var pkt = &packet.Packet{} - var err error - var pmt PMT - - pmtAcc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - done := false - - for !done { - if _, err := io.ReadFull(r, pkt[:]); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - return nil, gots.ErrPMTNotFound - } - return nil, err - } - currPid := pkt.PID() - if currPid != pid { - continue - } - _, err = pmtAcc.WritePacket(pkt) - if err == gots.ErrAccumulatorDone { - pmt, err = NewPMT(pmtAcc.Bytes()) - if err != nil { - return nil, err - } - if len(pmt.Pids()) == 0 { - done = false - pmtAcc = packet.NewAccumulator(PmtAccumulatorDoneFunc) - continue - } - done = true - } else if err != nil { - return nil, err - } - - } - return pmt, nil -} diff --git a/v2/psi/pmt_test.go b/v2/psi/pmt_test.go deleted file mode 100644 index 4f5061f..0000000 --- a/v2/psi/pmt_test.go +++ /dev/null @@ -1,934 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "bytes" - "encoding/hex" - "fmt" - "testing" - - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/packet" -) - -func parseHexString(h string) *packet.Packet { - b, err := hex.DecodeString(h) - if err != nil { - panic("bad test: " + h) - } - pkt := new(packet.Packet) - if copy(pkt[:], b) != packet.PacketSize { - panic("bad test (wrong length): " + h) - } - return pkt -} - -type testPmtElementaryStream struct { - elementaryPid int - streamType uint8 - presentationLagsEbp bool -} - -func (es *testPmtElementaryStream) ElementaryPid() int { - return es.elementaryPid -} - -func (es *testPmtElementaryStream) StreamType() uint8 { - return es.streamType -} - -func (es *testPmtElementaryStream) StreamTypeDescription() string { - return "Test Stream Description" -} - -func (es *testPmtElementaryStream) IsAudioContent() bool { - return es.presentationLagsEbp -} - -func (es *testPmtElementaryStream) IsVideoContent() bool { - return !es.presentationLagsEbp -} - -func (es *testPmtElementaryStream) IsSCTE35Content() bool { - return false -} - -func (es *testPmtElementaryStream) IsID3Content() bool { - return false -} - -func (es *testPmtElementaryStream) IsPrivateContent() bool { - return false -} - -func (es *testPmtElementaryStream) IsStreamWherePresentationLagsEbp() bool { - return es.presentationLagsEbp -} - -func (es *testPmtElementaryStream) Descriptors() []PmtDescriptor { - return make([]PmtDescriptor, 0) -} - -func (es *testPmtElementaryStream) MaxBitRate() uint64 { - return 0 -} - -func (es *testPmtElementaryStream) IsTTMLSubtitling() bool { - return false -} - -func TestParseTable(t *testing.T) { - byteArray, _ := hex.DecodeString("0002b02d0001cb0000e065f0060504435545491b" + - "e065f0050e030004b00fe066f0060a04656e670086e06ef0" + - "007fc9ad32ffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffff") - - pmt := pmt{} - err := pmt.parseTables(byteArray) - if err != nil { - t.Errorf("Can't parse PMT table %v", err) - } - - want := []int{101, 102, 110} - got := pmt.Pids() - if len(want) != len(got) { - t.Errorf("ES count does not match want:%v got:%v", len(want), len(got)) - } - - for i, pid := range got { - if want[i] != pid { - t.Errorf("PID does not match Want:%v Got:%v", want[i], pid) - } - } -} - -func TestParseMultipleTables(t *testing.T) { - byteArray, _ := hex.DecodeString("00c00015000297000b0000000000000100000000006ed1581a" + - "02b02d0001cb0000e065f0060504435545491b" + - "e065f0050e030004b00fe066f0060a04656e670086e06ef0" + - "007fc9ad32ffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffff") - - pmt := pmt{} - err := pmt.parseTables(byteArray) - if err != nil { - t.Errorf("Can't parse PMT table %v", err) - } - - want := []int{101, 102, 110} - got := pmt.Pids() - if len(want) != len(got) { - t.Errorf("ES count does not match want:%v got:%v", len(want), len(got)) - } - - for i, pid := range got { - if want[i] != pid { - t.Errorf("PID does not match Want:%v Got:%v", want[i], pid) - } - } -} - -func TestBuildPMT(t *testing.T) { - pkt := parseHexString("474064100002b02d0001cb0000e065f0060504435545491b" + - "e065f0050e030004b00fe066f0060a04656e670086e06ef0" + - "007fc9ad32ffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffff") - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - - _, err := acc.WritePacket(pkt) - if err != gots.ErrAccumulatorDone { - t.Errorf("Single packet PMT expected. This means your doneFunc is probably bad.") - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - want := []int{101, 102, 110} - got := pmt.Pids() - if len(want) != len(got) { - t.Errorf("PID lengths do not match Expected %d: Got %d", len(want), len(got)) - } - for i, pid := range got { - if want[i] != pid { - t.Errorf("PIDs incorrect in PMT Want %d: Got %d", want[i], pid) - } - } -} - -func TestPIDExists(t *testing.T) { - pkt := parseHexString("474064100002b02d0001cb0000e065f0060504435545491b" + - "e065f0050e030004b00fe066f0060a04656e670086e06ef0" + - "007fc9ad32ffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffff") - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(pkt) - if err != gots.ErrAccumulatorDone { - t.Errorf("Single packet PMT expected. This means your doneFunc is probably bad.") - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - if !pmt.PIDExists(101) { - t.Error("Expected true for PID 101 to exist in PMT.") - } - - if pmt.PIDExists(199) { - t.Error("Expected false for PID 199 to exist in PMT.") - } -} - -func TestBuildPMT_ExpectsAnotherPacket(t *testing.T) { - pkt := parseHexString( - "4740271A0002B0BA0001F70000E065F00C0F04534150530504435545491BE065" + - "F03028046400283F2A0FFF7F00000001000001C2000003E99F0E03C039219700" + - "E90710830A41850241860701656E677EFFFF0FE066F0160A04656E67000E03C0" + - "00F09700E90710830A408502400FE067F0160A04737061000E03C000F09700E9" + - "0710830A4085024087E068F0160A04656E67000E03C001E09700E90710830A40" + - "85024087E069F0160A04737061000E03C000F09700E90710830A4085") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(pkt) - if err == gots.ErrAccumulatorDone { - t.Errorf("Expected Error because not enough packets are present to create PMT") - } -} -func TestBuildPMT_LargePointerFieldGood(t *testing.T) { - pkt := parseHexString("474064108700000000000000000000000000000000000000" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "ffffffffffffffffffffffffffffffffffffffff02b02d00" + - "01cb0000e065f0060504435545491be065f0050e030004b0" + - "0fe066f0060a04656e670086e06ef0007fc9ad32") - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(pkt) - - if err != gots.ErrAccumulatorDone { - t.Errorf("Single packet PMT expected. This means your doneFunc is probably bad.") - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - want := []int{101, 102, 110} - got := pmt.Pids() - if len(want) != len(got) { - t.Errorf("PID lengths do not match Expected %d: Got %d", len(want), len(got)) - } - for i, pid := range got { - if want[i] != pid { - t.Errorf("PIDs incorrect in PMT Want %d: Got %d", want[i], pid) - } - } -} -func TestBuildPMT_LargePointerFieldExpectsAnotherPacket(t *testing.T) { - pkt := parseHexString("474064108800000000000000000000000000000000000000" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "0102030405060708090a0b0c0d0e0f101112131415161718" + - "ffffffffffffffffffffffffffffffffffffffffff02b02d" + - "0001cb0000e065f0060504435545491be065f0050e030004" + - "b00fe066f0060a04656e670086e06ef0007fc9ad") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(pkt) - if err == gots.ErrAccumulatorDone { - t.Errorf("Expected Error because not enough packets are present to create PMT") - } -} -func TestBuildMultiPacketPMT(t *testing.T) { - firstPacketBytes := parseHexString("474064100002b0ba0001c10000e065f00b0504435545490e03c03dd01be065f016970028046400283fe907108302808502800e03c0392087e066f0219700050445414333cc03c0c2100a04656e6700e907108302808502800e03c000f087e067f0219700050445414333cc03c0c4100a0473706100e907108302808502800e03c001e00fe068f01697000a04656e6700e907108302808502800e03c000f00fe069f01697000a0473706100e907108302808502800e03c000f086e0dc") - secondPacketBytes := parseHexString("47006411f0002b59bc22ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - acc.WritePacket(firstPacketBytes) - _, err := acc.WritePacket(secondPacketBytes) - if err != gots.ErrAccumulatorDone { - t.Fatal("PMT should have been done after 2 packets and it is not") - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - wantedPids := []int{101, 102, 103, 104, 105, 220} - if len(wantedPids) != len(pmt.Pids()) { - t.Errorf("PID length do not match expected %d Got %d", len(wantedPids), len(pmt.Pids())) - t.FailNow() - } - for i, pid := range wantedPids { - if pid != pmt.Pids()[i] { - t.Errorf("Pids do not match expected %d, Got %d", pid, pmt.Pids()[i]) - } - } -} - -func TestBuildMultiPacketPMT2(t *testing.T) { - firstPacket := parseHexString("4741E03001000002B1790001C10000E1E1F00B0504435545490E03C038F31BE1E1F016970028046400293FE907108302808502800E03C024DF0FE1EEF01697000A04656E6700E907108302808502800E03C001700FE1EFF01697000A0473706100E907108302808502800E03C001700FE1F0F01697000A04706F7200E907108302808502800E03C0017087E1E2F0219700050445414333CC03C0C4100A04656E6700E907108302808502800E03C002C287E1E3F02197000504454143") - secondPacket := parseHexString("4701E031010033CC03C0C2100A0473706100E907108302808502800E03C0017E87E1E4F0219700050445414333CC03C0D2100A04656E6700E907108302808502800E03C0017E81E1E8F0259700050441432D33810706380FFF1F013F0A04656E6700E907108302808502800E03C0054A81E1E9F0259700050441432D338107062005FF1F013F0A0473706100E907108302808502800E03C001EA81E1EAF0259700050441432D338107062045FF00013F0A04656E6703E90710830280") - thirdPacket := parseHexString("4701E03201008502800E03C001EA86E1F4F00096A58F55FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(firstPacket) - if err == gots.ErrAccumulatorDone { - t.Error("Added first packet of multi-packet and already indicating done. That's not right.") - t.FailNow() - } - - _, err = acc.WritePacket(secondPacket) - if err == gots.ErrAccumulatorDone { - t.Error("Added second packet of multi-packet and already indicating done. That's not right.") - t.FailNow() - } - - _, err = acc.WritePacket(thirdPacket) - if err != gots.ErrAccumulatorDone { - t.Error("Added third and final packet of multi-packet but indicating not done. That's not right.") - t.FailNow() - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - wantedPids := []int{481, 482, 483, 484, 488, 489, 490, 494, 495, 496, 500} - if len(wantedPids) != len(pmt.Pids()) { - t.Errorf("PID length do not match expected %d Got %d", len(wantedPids), len(pmt.Pids())) - t.FailNow() - } - for _, wpid := range wantedPids { - found := false - for _, pid := range pmt.Pids() { - if wpid == pid { - found = true - break - } - } - if !found { - t.Errorf("PIDs not found %d", wpid) - } - } -} - -func TestElementaryStreams(t *testing.T) { - pids := []int{101, 102, 103} - want := []PmtElementaryStream{ - &testPmtElementaryStream{101, 27, true}, - &testPmtElementaryStream{102, 15, true}, - &testPmtElementaryStream{103, 15, true}, - } - pmt := &pmt{pids: pids, elementaryStreams: want} - - got := pmt.ElementaryStreams() - - for i, es := range want { - if es.ElementaryPid() != got[i].ElementaryPid() { - t.Errorf("PIDs do not match Want %d: Got %d", es.ElementaryPid(), got[i].ElementaryPid()) - } - } - pid := int(102) - if !pmt.IsPidForStreamWherePresentationLagsEbp(pid) { - t.Errorf("PID %d: presentation should lag EBP", pid) - } -} -func TestIsPidForStreamWherePresentationLagsEbp(t *testing.T) { - pids := []int{101, 102, 103} - streams := []PmtElementaryStream{&testPmtElementaryStream{102, 15, true}} - pmt := &pmt{pids: pids, elementaryStreams: streams} - if !pmt.IsPidForStreamWherePresentationLagsEbp(102) { - t.Errorf("Expected Presentation to lag EBP") - } -} - -func TestIsNotPidForStreamWherePresentationLagsEbp(t *testing.T) { - pids := []int{101, 102, 103} - streams := []PmtElementaryStream{&testPmtElementaryStream{102, 15, false}} - pmt := &pmt{pids: pids, elementaryStreams: streams} - - if pmt.IsPidForStreamWherePresentationLagsEbp(102) { - t.Errorf("Expected Presentation to NOT lag EBP") - } -} - -func TestStringFormat(t *testing.T) { - bytes := []byte{ - 0x47, 0x40, 0x64, 0x10, 0x00, 0x02, 0xb0, 0x2d, 0x00, 0x01, 0xcb, 0x00, - 0x00, 0xe0, 0x65, 0xf0, 0x06, 0x05, 0x04, 0x43, 0x55, 0x45, 0x49, 0x1b, - 0xe0, 0x65, 0xf0, 0x05, 0x0e, 0x03, 0x00, 0x04, 0xb0, 0x0f, 0xe0, 0x66, - 0xf0, 0x06, 0x0a, 0x04, 0x65, 0x6e, 0x67, 0x00, 0x86, 0xe0, 0x6e, 0xf0, - 0x00, 0x7f, 0xc9, 0xad, 0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - pkt := &packet.Packet{} - - for i := 0; i < len(bytes)/packet.PacketSize; i++ { - copy(pkt[:], bytes[i*packet.PacketSize:(i+1)*packet.PacketSize]) - acc.WritePacket(pkt) - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - want := "PMT[ElementaryStream[pid=101,streamType=27,descriptor0='Maximum Bit-Rate (1200)'],ElementaryStream[pid=102,streamType=15,descriptor0='ISO 639 Language (code=eng, audioType=0x00)'],ElementaryStream[pid=110,streamType=134]]" - got := fmt.Sprintf("%v", pmt) - if want != got { - t.Errorf("String format for PMT failed. Want: %s: Got %s", want, got) - } -} - -func TestFilterPMTPacketsToPids_SinglePacketPMT(t *testing.T) { - bytes := packet.Packet{ - 0x47, 0x40, 0x64, 0x10, 0x00, 0x02, 0xb0, 0x2d, 0x00, 0x01, 0xcb, 0x00, - 0x00, 0xe0, 0x65, 0xf0, 0x06, 0x05, 0x04, 0x43, 0x55, 0x45, 0x49, 0x1b, - 0xe0, 0x65, 0xf0, 0x05, 0x0e, 0x03, 0x00, 0x04, 0xb0, 0x0f, 0xe0, 0x66, - 0xf0, 0x06, 0x0a, 0x04, 0x65, 0x6e, 0x67, 0x00, 0x86, 0xe0, 0x6e, 0xf0, - 0x00, 0x7f, 0xc9, 0xad, 0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - pkt := &packet.Packet{} - - for i := 0; i < len(bytes)/packet.PacketSize; i++ { - copy(pkt[:], bytes[i*packet.PacketSize:(i+1)*packet.PacketSize]) - acc.WritePacket(pkt) - } - - unfilteredPmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - pids := unfilteredPmt.Pids() - pids = pids[:len(pids)-1] - - filteredPmtPackets, err := FilterPMTPacketsToPids([]*packet.Packet{&bytes}, pids) - if err != nil { - t.Errorf("Expected nil error, got %s", err.Error()) - } - - acc = packet.NewAccumulator(PmtAccumulatorDoneFunc) - for _, p := range filteredPmtPackets { - acc.WritePacket(p) - } - filteredPmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - for i, pid := range filteredPmt.Pids() { - if pids[i] != pid { - t.Errorf("PIDs do not match Expected:%d Got %d", pids[i], pid) - } - } -} - -func TestFilterPMTPacketsToPids_MultiPacketPMT(t *testing.T) { - firstPacketBytes := parseHexString("474064100002b0ba0001c10000e065f00b0504435545490e03c03dd01be065f016970028046400283fe907108302808502800e03c0392087e066f0219700050445414333cc03c0c2100a04656e6700e907108302808502800e03c000f087e067f0219700050445414333cc03c0c4100a0473706100e907108302808502800e03c001e00fe068f01697000a04656e6700e907108302808502800e03c000f00fe069f01697000a0473706100e907108302808502800e03c000f086e0dc") - secondPacketBytes := parseHexString("47006411f0002b59bc22ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - wantedPids := []int{101, 102, 103, 104, 105, 220} - - filteredPids := wantedPids[:len(wantedPids)-1] - filteredPMTPackets, err := FilterPMTPacketsToPids([]*packet.Packet{firstPacketBytes, secondPacketBytes}, filteredPids) - if err != nil { - t.Errorf("Expected nil error, got %s", err.Error()) - } - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - for _, p := range filteredPMTPackets { - acc.WritePacket(p) - } - - wantedPids = []int{101, 102, 103, 104, 105} - - filteredPMT, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - if len(wantedPids) != len(filteredPMT.Pids()) { - t.Errorf("PID Length do not match wanted:%d got %d", len(wantedPids), len(filteredPMT.Pids())) - } - for i, pid := range filteredPMT.Pids() { - if wantedPids[i] != pid { - t.Errorf("PIDs do not match Expected:%d Got %d", wantedPids[i], pid) - } - } -} - -func TestFilterPMTPacketsToPids_PIDNotFound(t *testing.T) { - // PMT contains PIDs 101-105. - pmtPacketBytes := parseHexString("4740641D0002B0940001DF0000E065F0050E03C015581BE065F0150E03C0109D2A027E1F9700E9080C001F418503E84187E066F01A0E03C00122050445414333CC07E0C2B0E8656E670A04656E670087E067F01A0E03C00122050445414333CC07E0C2B0E8656E670A04656E67000FE068F0100E03C001262B030102010A04656E67000FE069F0100E03C001262B030102010A04656E67002E9B5B71FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(pmtPacketBytes) - if err != gots.ErrAccumulatorDone { - t.Errorf("Single packet PMT expected. This means your doneFunc is probably bad.") - } - - // Test when some of the PIDs to be filtered exist in the PMT. - pmtPkts, err := FilterPMTPacketsToPids([]*packet.Packet{pmtPacketBytes}, []int{105, 106}) - if err.Error() != "PID(s) [106] not found in PMT." { - t.Errorf("Expected missing PID error string, got %s", err.Error()) - } - - if pmtPkts == nil { - t.Error("Expected non-nil packets since some of the filtered PIDs exist in the PMT.") - } - - // Test when none of the PIDs to be filtered exist in the PMT. - pmtPkts, err = FilterPMTPacketsToPids([]*packet.Packet{pmtPacketBytes}, []int{106, 107}) - if err.Error() != "PID(s) [106 107] not found in PMT." { - t.Errorf("Expected missing PID error string, got %s", err.Error()) - } - - if pmtPkts != nil { - t.Error("Expected nil packets since none of the filtered PIDs exist in the PMT.") - } -} - -func TestPMTIsIFrameStreamPositive(t *testing.T) { - firstPacket := parseHexString("4741E03001000002B02D0001C10000E1E1F0050E03C003531BE1E1F016970028044D401F3FE907108301808501800E03C003175D027AA4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - _, err := acc.WritePacket(firstPacket) - if err != gots.ErrAccumulatorDone { - t.Error(err) - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - var isIFrameProfile bool - for _, es := range pmt.ElementaryStreams() { - for _, des := range es.Descriptors() { - isIFrameProfile = des.IsIFrameProfile() - if isIFrameProfile { - break - } - } - if isIFrameProfile { - break - } - } - if !isIFrameProfile { - t.Errorf("Positive I-Frame Stream failed. Supposed to be an I-Frame stream.") - } -} - -func TestPMTIsIFrameStreamNegative(t *testing.T) { - firstPacket := parseHexString("4741E03001000002B0FB0001C10000E1E1F00B0504435545490E03C02FD31BE1E1F016970028046400293FE907108302808502800E03C024DF0FE1E2F01697000A04656E6700E907108302808502800E03C001700FE1E3F01697000A0473706100E907108302808502800E03C001700FE1E4F01697000A04706F7200E907108302808502800E03C0017087E1E5F0219700050445414333CC03C0C4100A04656E6700E907108302808502800E03C002C287E1E6F02197000504454143") - secondPacket := parseHexString("4701E031010033CC03C0C2100A0473706100E907108302808502800E03C0017E87E1E7F0219700050445414333CC03C0D2100A04656E6700E907108302808502800E03C0017E86E1F4F00013E8BFD4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") - - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - acc.WritePacket(firstPacket) - acc.WritePacket(secondPacket) - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - - var isIFrameProfile bool - for _, es := range pmt.ElementaryStreams() { - for _, des := range es.Descriptors() { - isIFrameProfile = des.IsIFrameProfile() - if isIFrameProfile { - break - } - } - if isIFrameProfile { - break - } - } - if isIFrameProfile { - t.Errorf("Negative I-Frame Stream failed. Not supposed to be an I-Frame stream.") - } -} - -func TestIsPMT(t *testing.T) { - patPkt := parseHexString("4740003001000000b00d0001c100000001e1e02d507804ffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - patPayload, _ := packet.Payload(patPkt) - pat, _ := NewPAT(patPayload) - - if pat == nil { - t.Error("Couldn't load the PAT") - } - - pmt := parseHexString("4741e03001000002b0480001c10000e1e1f0050e03c004751be1e1f016970028" + - "044d401f3fe907108302808502800e03c003350fe1e2f01697000a04656e6700" + - "e907108302808502800e03c00104db121f57ffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - notPMT := parseHexString("4741e13117f200014307ff050fdf0d45425030c8dae4dd8000000000000001e0" + - "000084d00d31000bab4111000b93cb80054700000001091000000001674d401f" + - "ba202833f3e022000007d20001d4c1c040020f400041eb4d4601f18311200000" + - "000168ebef20000000010600068232993c76c08000000001060447b500314741" + - "393403d4fffc8080fd8080fa0000fa0000fa0000fa0000fa0000fa0000fa0000" + - "fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa0000fa") - - if isPMTExpectTrue, _ := IsPMT(pmt, pat); isPMTExpectTrue == false { - t.Error("PMT packet should be counted as a PMT") - } - - if isPMTExpectFalse, _ := IsPMT(notPMT, pat); isPMTExpectFalse == true { - t.Error("EBP packet should not be counted as a PMT") - } -} - -func TestIsPMTErrorConditions(t *testing.T) { - // Test nil PAT - - pmt := parseHexString("4741e03001000002b0480001c10000e1e1f0050e03c004751be1e1f016970028" + - "044d401f3fe907108302808502800e03c003350fe1e2f01697000a04656e6700" + - "e907108302808502800e03c00104db121f57ffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - isPMTExpectFalse, errExpectInvalidArg := IsPMT(pmt, nil) - if isPMTExpectFalse == true { - t.Error("nil PAT should return false for any PMT") - } - - if errExpectInvalidArg != gots.ErrNilPAT { - t.Error("Nil Pat should return nil pat error") - } - - patPkt := parseHexString("4740003001000000b00d0001c100000001e1e02d507804ffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - - patPayload, _ := packet.Payload(patPkt) - pat, _ := NewPAT(patPayload) - - if pat == nil { - t.Error("Couldn't load the PAT") - } -} - -func TestReadPMTForSmoke(t *testing.T) { - bs, _ := hex.DecodeString("474000100000b00d0001c100000001e256f803e71bfffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ff474256100002b0300001c10000e131f0060504435545491be121f0042a027e1" + - "f86e225f00f52012a9700e9080c001f41850fa041ee3f6580ffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffff") - r := bytes.NewReader(bs) - - pid := 598 - pmt, err := ReadPMT(r, pid) - if err != nil { - t.Fatalf("Unexpected error reading PMT: %v", err) - } - // sanity check (tests integration a bit) - if len(pmt.ElementaryStreams()) != 2 { - t.Errorf("PMT read is invalid, did not have expected number of streams") - } -} - -func TestReadPMTIncomplete(t *testing.T) { - bs, _ := hex.DecodeString("474000100000b00d0001c100000001e256f803e71bfffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ff4742") // incomplete PMT packet - r := bytes.NewReader(bs) - - pid := 598 - _, err := ReadPMT(r, pid) - if err == nil { - t.Errorf("Expected to get error reading PMT, but did not") - } -} - -func TestReadPMTSCTE(t *testing.T) { - bs, _ := hex.DecodeString("47403b1b00c0001500000100810000000000000100000000002f832c69ff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffff47403b1c0002b0b20001cb0000f13df01809044749e1" + - "0b050441432d330504454143330504435545491bf13df0102a027e1f9700" + - "e9080c001f418507d04181f13ef00f810706380fff1f003f0a04656e6700" + - "81f13ff00f8107061003ff1f003f0a047370610086f140f00f8a01009700" + - "e9080c001f418507d041c0f141f012050445545631a100e9080c001f4185" + - "07d041c0f142f013050445545631a20100e9080c001f418507d041c0f164" + - "f008bf06496e766964690bcfa64bffff") // two PMT packets, first with SCTE 0xc0 table only - r := bytes.NewReader(bs) - - pid := 59 - pmt, err := ReadPMT(r, pid) - if err != nil { - t.Errorf("Unexpected error reading PMT: %v", err) - return - } - // sanity check (tests integration a bit) - if len(pmt.ElementaryStreams()) != 7 { - t.Errorf("PMT read is invalid, did not have expected number of streams") - } -} - -func TestReadPMT_MultipleTables_MultiplePackets(t *testing.T) { - bs, _ := hex.DecodeString("47403b1e00c0001500000100610000000000000100000000" + - "0035e3e2d702b0b20001c50000eefdf01809044749e10b05" + - "0441432d330504454143330504435545491beefdf0102a02" + - "7e1f9700e9080c001f418507d04181eefef00f810706380f" + - "ff1f003f0a04656e670081eefff00f8107061003ff1f003f" + - "0a047370610086ef00f00f8a01009700e9080c001f418507" + - "d041c0ef01f012050445545631a100e9080c001f418507d0" + - "41c0ef02f013050445545631a20100e9080c001f47003b1f" + - "418507d041c0ef03f008bf06496e76696469a5cff3afffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffffffffffffffffffff" + - "ffffffffffffffffffffffffffffffff") // two tables (0xc0 and 0x2) combined across two packets - r := bytes.NewReader(bs) - - pid := 59 - pmt, err := ReadPMT(r, pid) - if err != nil { - t.Errorf("Unexpected error reading PMT: %v", err) - return - } - if len(pmt.ElementaryStreams()) != 7 { - t.Errorf("PMT read is invalid, did not have expected number of streams") - } -} - -func TestIsDolbyATMOS(t *testing.T) { - d, _ := hex.DecodeString("00ff") - desc := &pmtDescriptor{tag: uint8(204), data: d} - if desc.IsDolbyATMOS() { - t.Errorf("Expected not Dolby ATMOS") - } - - d, _ = hex.DecodeString("ffffc000000000000000000000000000000000000000000000") - desc = &pmtDescriptor{tag: uint8(204), data: d} - if desc.IsDolbyATMOS() { - t.Errorf("Expected not Dolby ATMOS") - } - - d, _ = hex.DecodeString("0000ff0001") - desc = &pmtDescriptor{tag: uint8(204), data: d} - if !desc.IsDolbyATMOS() { - t.Errorf("Expected Dolby ATMOS") - } - - d, _ = hex.DecodeString("ffffc000000000000000000000000000000000000000000001") - desc = &pmtDescriptor{tag: uint8(204), data: d} - if !desc.IsDolbyATMOS() { - t.Errorf("Expected Dolby ATMOS") - } - - desc = &pmtDescriptor{tag: uint8(2), data: d} - if desc.IsDolbyATMOS() { - t.Errorf("Expected not Dolby ATMOS") - } - - b1 := []byte{0x47, 0x41, 0xe0, 0x1f, 0x00, 0x02, 0xb0, 0xd7, 0x00, - 0x01, 0xc1, 0x00, 0x00, 0xe1, 0xe1, 0xf0, 0x0b, 0x05, 0x04, 0x47, - 0x41, 0x39, 0x34, 0x0e, 0x03, 0xc0, 0xbf, 0x0e, 0x24, 0xe1, 0xe1, - 0xf0, 0x25, 0x06, 0x01, 0x02, 0x38, 0x0f, 0x02, 0x20, 0x00, 0x00, - 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x9f, 0x1f, 0x1f, - 0x97, 0x00, 0x0e, 0x03, 0xc0, 0xb4, 0x70, 0xe9, 0x08, 0x0c, 0x00, - 0x0f, 0xa1, 0x83, 0x03, 0xe9, 0x41, 0x87, 0xe1, 0xe2, 0xf0, 0x26, - 0x97, 0x00, 0x0e, 0x03, 0xc0, 0x06, 0xe2, 0xe9, 0x08, 0x0c, 0x00, - 0x0f, 0xa1, 0x83, 0x03, 0xe9, 0x41, 0x05, 0x04, 0x41, 0x43, 0x2d, - 0x33, 0xcc, 0x07, 0x80, 0xc5, 0xa0, 0x65, 0x6e, 0x67, 0x01, 0x0a, - 0x04, 0x65, 0x6e, 0x67, 0x00, 0x87, 0xe1, 0xe3, 0xf0, 0x25, 0x97, - 0x00, 0x0e, 0x03, 0xc0, 0x01, 0x74, 0xe9, 0x08, 0x0c, 0x00, 0x0f, - 0xa1, 0x83, 0x03, 0xe9, 0x41, 0x05, 0x04, 0x41, 0x43, 0x2d, 0x33, - 0xcc, 0x06, 0x80, 0xc2, 0xa0, 0x73, 0x70, 0x61, 0x0a, 0x04, 0x73, - 0x70, 0x61, 0x00, 0x0f, 0xe1, 0xe8, 0xf0, 0x1b, 0x7c, 0x02, 0x59, - 0x00, 0x0a, 0x04, 0x65, 0x6e, 0x67, 0x00, 0x97, 0x00, 0x0e, 0x03, - 0xc0, 0x01, 0x25, 0xe9, 0x08, 0x0c, 0x00, 0x0f, 0xa1, 0x83, 0x03, - 0xe9, 0x41, 0x0f} - b2 := []byte{0x47, 0x01, 0xe0, 0x10, 0xe1, 0xe9, 0xf0, 0x1b, 0x7c, - 0x02, 0x59, 0x00, 0x0a, 0x04, 0x73, 0x70, 0x61, 0x00, 0x97, 0x00, - 0x0e, 0x03, 0xc0, 0x01, 0x25, 0xe9, 0x08, 0x0c, 0x00, 0x0f, 0xa1, - 0x83, 0x03, 0xe9, 0x41, 0x38, 0x63, 0x37, 0x8f, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff} - - var pkt = &packet.Packet{} - acc := packet.NewAccumulator(PmtAccumulatorDoneFunc) - for i := 0; i < len(b1)/packet.PacketSize; i++ { - copy(pkt[:], b1[i*packet.PacketSize:(i+1)*packet.PacketSize]) - acc.WritePacket(pkt) - } - for i := 0; i < len(b2)/packet.PacketSize; i++ { - copy(pkt[:], b2[i*packet.PacketSize:(i+1)*packet.PacketSize]) - acc.WritePacket(pkt) - } - - pmt, err := NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - var hasATMOS bool - for _, es := range pmt.ElementaryStreams() { - for _, des := range es.Descriptors() { - hasATMOS = des.IsDolbyATMOS() - if hasATMOS { - break - } - } - if hasATMOS { - break - } - } - if !hasATMOS { - t.Errorf("Positive Dolby ATMOS Stream failed. Supposed to be a Dolby ATMOS stream.") - } - - b := []byte{0x47, 0x41, 0xe0, 0x1f, 0x00, 0x02, 0xb0, 0x8d, 0x00, 0x01, - 0xc1, 0x00, 0x00, 0xe1, 0xe1, 0xf0, 0x0b, 0x05, 0x04, 0x47, 0x41, - 0x39, 0x34, 0x0e, 0x03, 0xc0, 0xb9, 0xe8, 0x24, 0xe1, 0xe1, 0xf0, - 0x25, 0x06, 0x01, 0x02, 0x38, 0x0f, 0x02, 0x20, 0x00, 0x00, 0x00, - 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x9f, 0x1f, 0x1f, 0x97, - 0x00, 0x0e, 0x03, 0xc0, 0xb4, 0x70, 0xe9, 0x08, 0x0c, 0x00, 0x0f, - 0xa1, 0x83, 0x03, 0xe9, 0x41, 0x87, 0xe1, 0xe2, 0xf0, 0x26, 0x97, - 0x00, 0x0e, 0x03, 0xc0, 0x04, 0x54, 0xe9, 0x08, 0x0c, 0x00, 0x0f, - 0xa1, 0x83, 0x03, 0xe9, 0x41, 0x05, 0x04, 0x41, 0x43, 0x2d, 0x33, - 0xcc, 0x07, 0x80, 0xf4, 0xa0, 0x65, 0x6e, 0x67, 0x01, 0x0a, 0x04, - 0x65, 0x6e, 0x67, 0x00, 0x0f, 0xe1, 0xe3, 0xf0, 0x1b, 0x7c, 0x02, - 0x59, 0x00, 0x0a, 0x04, 0x65, 0x6e, 0x67, 0x00, 0x97, 0x00, 0x0e, - 0x03, 0xc0, 0x01, 0x25, 0xe9, 0x08, 0x0c, 0x00, 0x0f, 0xa1, 0x83, - 0x03, 0xe9, 0x41, 0xc3, 0x50, 0x69, 0xb4, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff} - - acc = packet.NewAccumulator(PmtAccumulatorDoneFunc) - for i := 0; i < len(b)/packet.PacketSize; i++ { - copy(pkt[:], b[i*packet.PacketSize:(i+1)*packet.PacketSize]) - acc.WritePacket(pkt) - } - - pmt, err = NewPMT(acc.Bytes()) - if err != nil { - t.Error(err) - } - hasATMOS = false - for _, es := range pmt.ElementaryStreams() { - for _, des := range es.Descriptors() { - hasATMOS = des.IsDolbyATMOS() - if hasATMOS { - break - } - } - if hasATMOS { - break - } - } - if !hasATMOS { - t.Errorf("Positive Dolby ATMOS Stream failed. Supposed to be a Dolby ATMOS stream.") - } -} diff --git a/v2/psi/pmtdescriptor.go b/v2/psi/pmtdescriptor.go deleted file mode 100644 index a573faa..0000000 --- a/v2/psi/pmtdescriptor.go +++ /dev/null @@ -1,373 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "encoding/binary" - "encoding/hex" - "fmt" - "strconv" -) - -// Program Element Stream Descriptor Type. -const ( - VIDEO_STREAM uint8 = 2 // 0000 0010 (0x02) - AUDIO_STREAM uint8 = 3 // 0000 0011 (0x03) - REGISTRATION uint8 = 5 // 0000 1000 (0x05) - CONDITIONAL_ACCESS uint8 = 9 // 0000 1001 (0x09) - LANGUAGE uint8 = 10 // 0000 1010 (0x0A) - SYSTEM_CLOCK uint8 = 11 // 0000 1011 (0x0B) - DOLBY_DIGITAL uint8 = 12 // 0000 1100 (0x0C) - COPYRIGHT uint8 = 13 // 0000 1101 (0x0D) - MAXIMUM_BITRATE uint8 = 14 // 0000 1110 (0x0E) - AVC_VIDEO uint8 = 40 // 0010 1000 (0x28) - STREAM_IDENTIFIER uint8 = 82 // 0101 0010 (0x52) - EXTENSION uint8 = 127 // 0111 1111 (0x7F) - SCTE_ADAPTATION uint8 = 151 // 1001 0111 (0x97) - DOLBY_VISION uint8 = 176 // 1011 0000 (0xB0) - EBP uint8 = 233 // 1110 1001 (0xE9) - EC3 uint8 = 204 // 1100 1100 (0xCC) -) - -// ISO_639 Audio service type -const ( - AUDIO_UNDEFINED int = 0 // 0000 0000 (0x00) - AUDIO_CLEAN_EFFECTS int = 1 // 0000 0001 (0x01) - AUDIO_HEARING_IMPAIRED int = 2 // 0000 0010 (0x02) - AUDIO_DESCRIPTION int = 3 // 0000 0011 (0x03) - AUDIO_PRIMARY int = 128 // 1000 0000 (0x80) - AUDIO_NATIVE int = 129 // 1000 0001 (0x81) -) - -// Descriptor tag extension -const ( - TTML_DESC_TAG_EXTENSION uint8 = 32 // 0010 0000 (0x20) -) - -const ( - TTML_PURPOSE_SAME_LANG_DIALOGUE uint8 = 0 // 0000 0000 (0x00) - TTML_PURPOSE_OTHER_LANG_DIALOGUE uint8 = 1 // 0000 0001 (0x01) - TTML_PURPOSE_ALL_DIALOGUE uint8 = 2 // 0000 0010 (0x02) - TTML_PURPOSE_HARD_OF_HEARING uint8 = 16 // 0001 0000 (0x10) - TTML_PURPOSE_OTHER_LANG_DIALOGUE_WITH_HARD_OF_HEARING uint8 = 17 // 0001 0001 (0x11) - TTML_PURPOSE_ALL_DIALOGUE_WITH_HARD_OF_HEARING uint8 = 18 // 0001 0010 (0x12) - TTML_PURPOSE_AUDIO_DESCRIPTION uint8 = 48 // 0011 0000 (0x30) - TTML_PURPOSE_CONTENT_RELATED_COMMENTARY uint8 = 49 // 0011 0001 (0x31) -) - -// PmtDescriptor represents operations currently necessary on descriptors found in the PMT -type PmtDescriptor interface { - Tag() uint8 - Format() string - IsIso639LanguageDescriptor() bool - IsMaximumBitrateDescriptor() bool - IsIFrameProfile() bool - IsEBPDescriptor() bool - DecodeMaximumBitRate() uint32 - DecodeIso639LanguageCode() string - DecodeIso639AudioType() byte - IsDolbyATMOS() bool - IsDolbyVision() bool - DecodeDolbyVisionCodec(string) string - IsTTMLSubtitlingDescriptor() bool - DecodeTTMLIso639LanguageCode() string - DecodeTTMLSubtitlePurpose() uint8 - IsTTMLDescTagExtension() bool -} - -type pmtDescriptor struct { - tag uint8 - data []byte -} - -// NewPmtDescriptor creates a new PMTDescriptor with the provided tag and byte contents. -func NewPmtDescriptor(tag uint8, data []byte) PmtDescriptor { - descriptor := &pmtDescriptor{} - descriptor.tag = tag - descriptor.data = data - return descriptor -} - -func (descriptor *pmtDescriptor) Tag() uint8 { - return descriptor.tag -} - -func (descriptor *pmtDescriptor) String() string { - return descriptor.decode() -} - -func (descriptor *pmtDescriptor) Format() string { - return fmt.Sprintf("[tag=%b, decoded=%s]\n", descriptor.tag, descriptor.decode()) -} - -func (descriptor *pmtDescriptor) decode() string { - switch descriptor.tag { - case LANGUAGE: - return fmt.Sprintf("ISO 639 Language (code=%s, audioType=0x%s)", - descriptor.DecodeIso639LanguageCode(), hex.EncodeToString([]byte{descriptor.DecodeIso639AudioType()})) - case MAXIMUM_BITRATE: - return fmt.Sprintf("Maximum Bit-Rate (%d)", descriptor.DecodeMaximumBitRate()) - case VIDEO_STREAM: - return fmt.Sprintf("Video Stream (%d)", descriptor.tag) - case AUDIO_STREAM: - return fmt.Sprintf("Audio Stream (%d)", descriptor.tag) - case REGISTRATION: - return fmt.Sprintf("Registration (%d)", descriptor.tag) - case CONDITIONAL_ACCESS: - return fmt.Sprintf("Conditional Access (%d)", descriptor.tag) - case SYSTEM_CLOCK: - return fmt.Sprintf("System Clock (%d)", descriptor.tag) - case COPYRIGHT: - return fmt.Sprintf("Copyright (%d)", descriptor.tag) - case AVC_VIDEO: - return fmt.Sprintf("AVC Video (%d)", descriptor.tag) - case DOLBY_DIGITAL: - return fmt.Sprintf("Dolby Digital (%d)", descriptor.tag) - case SCTE_ADAPTATION: - return fmt.Sprintf("SCTE Adaptation (%d)", descriptor.tag) - case DOLBY_VISION: - return fmt.Sprintf("Dolby Vision (%d)", descriptor.tag) - case EBP: - return fmt.Sprintf("EBP (%d)", descriptor.tag) - case STREAM_IDENTIFIER: - return fmt.Sprintf("Stream Identifier (%d): %v", descriptor.tag, descriptor.data[0]) - case EXTENSION: - return fmt.Sprintf("TTML Subtitling (language code=%s)", descriptor.DecodeTTMLIso639LanguageCode()) - } - return "unknown tag (" + strconv.Itoa(int(descriptor.tag)) + ")" -} - -func (descriptor *pmtDescriptor) IsIso639LanguageDescriptor() bool { - return descriptor.tag == LANGUAGE -} - -func (descriptor *pmtDescriptor) IsMaximumBitrateDescriptor() bool { - return descriptor.tag == MAXIMUM_BITRATE -} - -func (descriptor *pmtDescriptor) IsEBPDescriptor() bool { - return descriptor.tag == EBP -} - -// Return the decoded Maximum_bitrate in units of 50 bytes per second -func (descriptor *pmtDescriptor) DecodeMaximumBitRate() uint32 { - if descriptor.IsMaximumBitrateDescriptor() { - return uint32(descriptor.data[0]&0x1f)<<16 | uint32(descriptor.data[1])<<8 | uint32(descriptor.data[2]) - } - return 0 -} - -func (descriptor *pmtDescriptor) DecodeIso639LanguageCode() string { - if LANGUAGE == descriptor.tag { - return string(descriptor.data[0:3]) - } - return "" -} - -func (descriptor *pmtDescriptor) DecodeIso639AudioType() byte { - if len(descriptor.data) >= 4 { - return descriptor.data[3] - } - return 0 -} - -func (descriptor *pmtDescriptor) IsTTMLDescTagExtension() bool { - return len(descriptor.data) >= 1 && uint8(descriptor.data[0]) == TTML_DESC_TAG_EXTENSION -} - -func (descriptor *pmtDescriptor) IsTTMLSubtitlingDescriptor() bool { - return descriptor.tag == EXTENSION -} - -func (descriptor *pmtDescriptor) DecodeTTMLIso639LanguageCode() string { - if descriptor.tag == EXTENSION { - if len(descriptor.data) >= 4 { - return string(descriptor.data[1:4]) - } - } - return "" -} - -func (descriptor *pmtDescriptor) DecodeTTMLSubtitlePurpose() uint8 { - if descriptor.tag == EXTENSION { - if len(descriptor.data) >= 5 { - return uint8(descriptor.data[4] >> 2) // First 6 bits of the 5th bytes - } - } - return 0xFF -} - -// IsIFrameProfile determines from the PMT if the profile is an I-Frame only track -// or not. An I-Frame only track is defined to be true if and only if the -// 'EBP_distance' is equal to '1'. The 'EBP_distance' is found in the PMT EBP -// descriptor as defined on page 16-17 of OC-SP-EBP-I01-130018.pdf. -// https://www.teamccp.com/confluence/download/attachments/59024185/OC-SP-EBP-I01-130118.pdf?version=1&modificationDate=1378666671000&api=v2 -func (descriptor *pmtDescriptor) IsIFrameProfile() bool { - if EBP == descriptor.tag && 0 < len(descriptor.data) { - - offset := 0 - num_partitions := uint8((descriptor.data[offset] & 0xF8) >> 3) - timescale_flag := 1 == uint8((descriptor.data[offset]&0x04)>>2) - offset++ - - var EBP_distance_width_minus_1 uint8 - if timescale_flag { - return false - } - - indx := uint8(0) - for indx < num_partitions { - indx++ - EBP_data_explicit_flag := 1 == uint8((descriptor.data[offset]&0x80)>>7) - representation_id_flag := 1 == uint8((descriptor.data[offset]&0x04)>>6) - - if EBP_data_explicit_flag { - offset++ - if 0 == EBP_distance_width_minus_1 { - EBP_distance := uint8(descriptor.data[offset]) - return 1 == EBP_distance - } else { - return false - } - } else { - offset += 2 - } - - if representation_id_flag { - offset += 8 - } - } - return false - } - return false -} - -// IsDolbyATMOS checks to see if the flag_ec3_extension_type_a flag is set -// The additional data added to the enhanced-AC3 descriptor in the additional_info_byte field section looks like this: -// flag_ec3_extension_type_reserved 7 bslbf -// flag_ec3_extension_type_a 1 bslbf -// A52-2015 Annex G Table G.1 -func (descriptor *pmtDescriptor) IsDolbyATMOS() bool { - - if descriptor.tag == EC3 && len(descriptor.data) >= 2 { - - // reserved 1 bit '1' - bsid_flag := 1 == uint8((descriptor.data[0]&0x40)>>6) // 1 bit - mainid_flag := 1 == uint8((descriptor.data[0]&0x20)>>5) // 1 bit - asvc_flag := 1 == uint8((descriptor.data[0]&0x10)>>4) // 1 bit - // mixinfoexists := 1 == uint8((descriptor.data[0]&0x08)>>3) // 1 bit - substream1_flag := 1 == uint8((descriptor.data[0]&0x04)>>2) // 1 bit - substream2_flag := 1 == uint8((descriptor.data[0]&0x02)>>1) // 1 bit - substream3_flag := 1 == uint8(descriptor.data[0]&0x01) // 1 bit - - // data[1] not needed: reserved 1, full_service_flag 1, audio_service_type 3, number_of_channels 3 - - language_flag := false - language_flag_2 := false - - start := uint8(2) - if bsid_flag { - language_flag = 1 == uint8((descriptor.data[start]&0x80)>>7) // 1 bit - language_flag_2 = 1 == uint8((descriptor.data[start]&0x40)>>6) // 1 bit - start++ - } - - if mainid_flag { - start++ - } - - if asvc_flag { - start++ - } - - if substream1_flag { - start++ - } - - if substream2_flag { - start++ - } - - if substream3_flag { - start++ - } - - if language_flag { - start += 3 - } - - if language_flag_2 { - start += 3 - } - - if substream1_flag { - start += 3 - } - - if substream2_flag { - start += 3 - } - - if substream3_flag { - start += 3 - } - - for i := start; i < uint8(len(descriptor.data)); i++ { - if 0x01 == descriptor.data[i] { - return true - } - } - } - return false -} - -// Check if the registration data is set to `DOVI` -// dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, Section 3.1 -func (descriptor *pmtDescriptor) IsDolbyVision() bool { - if descriptor.tag == REGISTRATION { - if len(descriptor.data) >= 4 { - return binary.BigEndian.Uint32(descriptor.data[:4]) == 0x444F5649 - } - } - return false -} - -// Parse the profile and level of dolby vision -// dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, Section 3.3 -func (descriptor *pmtDescriptor) DecodeDolbyVisionCodec(originalCodec string) string { - if descriptor.tag == DOLBY_VISION && len(descriptor.data) >= 4 { - num := binary.BigEndian.Uint16(descriptor.data[2:4]) - dv_profile := uint8((num & 0xFE00) >> 9) - dv_level := uint8((num & 0xFC) >> 3) - - // Hardcode to dvhe because this is MPEG2-TS which doesn't support h265 - // The codec strings of dvc1 vs dvhe is an mp4 specific thing - // dolby-vision-streams-within-the-mpeg-dash-format-v2.0 see Table 2, section 4.2.3 - baseCodec := "dvhe" - - return fmt.Sprintf("%s.%02d.%02d", baseCodec, dv_profile, dv_level) - } - return "" -} diff --git a/v2/psi/pmtelementarystream.go b/v2/psi/pmtelementarystream.go deleted file mode 100644 index e933553..0000000 --- a/v2/psi/pmtelementarystream.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "bytes" - "fmt" -) - -// PmtElementaryStream represents an elementary stream inside a PMT -type PmtElementaryStream interface { - PmtStreamType - ElementaryPid() int - Descriptors() []PmtDescriptor - MaxBitRate() uint64 - IsTTMLSubtitling() bool -} - -type pmtElementaryStream struct { - PmtStreamType - elementaryPid int - descriptors []PmtDescriptor -} - -const ( - // BitsPerByte is the number of bits in a byte - BitsPerByte = 8 - // MaxBitRateBytesPerSecond is the maximum bit rate per second in a profile - MaxBitRateBytesPerSecond = 50 -) - -// NewPmtElementaryStream creates a new PmtElementaryStream. -func NewPmtElementaryStream(streamType uint8, elementaryPid int, descriptors []PmtDescriptor) PmtElementaryStream { - es := &pmtElementaryStream{} - es.PmtStreamType = LookupPmtStreamType(streamType) - es.elementaryPid = elementaryPid - es.descriptors = descriptors - return es -} - -func (es *pmtElementaryStream) ElementaryPid() int { - return es.elementaryPid -} - -func (es *pmtElementaryStream) Descriptors() []PmtDescriptor { - return es.descriptors -} - -// MaxBitRate returns the value of the PmtElementaryStreams maximum bitrate in bits-per-second. -// See Section 2.6.27 of ISO-13818 for more information. -func (es *pmtElementaryStream) MaxBitRate() uint64 { - for _, desc := range es.descriptors { - if desc.IsMaximumBitrateDescriptor() { - return uint64(desc.DecodeMaximumBitRate()) * BitsPerByte * MaxBitRateBytesPerSecond - } - } - return 0 -} - -// IsTTMLSubtitling checks all the descriptors and returns true if there is a TTML descriptor -// with TTML tag extension found -func (es *pmtElementaryStream) IsTTMLSubtitling() bool { - for _, descriptor := range es.Descriptors() { - if descriptor.IsTTMLSubtitlingDescriptor() && descriptor.IsTTMLDescTagExtension() { - return true - } - } - - return false -} - -func (es *pmtElementaryStream) String() string { - descriptors := es.descriptors - var descriptorsBuf bytes.Buffer - if len(descriptors) > 0 { - descriptorsBuf.WriteString(",") - for i, descriptor := range descriptors { - descriptorsBuf.WriteString(fmt.Sprintf("descriptor%d='%v'", i, descriptor)) - i++ - if i < len(descriptors) { - descriptorsBuf.WriteString(",") - } - } - } - return fmt.Sprintf("ElementaryStream[pid=%d,%v%s]", es.elementaryPid, es.PmtStreamType, descriptorsBuf.String()) -} diff --git a/v2/psi/pmtstreamtype.go b/v2/psi/pmtstreamtype.go deleted file mode 100644 index f7e56e7..0000000 --- a/v2/psi/pmtstreamtype.go +++ /dev/null @@ -1,197 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import "fmt" - -// Stream type constants -const ( - PmtStreamTypeMpeg2VideoH262 uint8 = 2 // H262 - PmtStreamTypeMpeg4Video uint8 = 27 // H264 - PmtStreamTypeMpeg4VideoH264 uint8 = 27 // H264 - PmtStreamTypeMpeg4VideoH265 uint8 = 36 // H265 - - PmtStreamTypeAac uint8 = 15 // AAC - PmtStreamTypeAc3 uint8 = 129 // DD - PmtStreamTypeEc3 uint8 = 135 // DD+ - - PmtStreamTypeScte35 uint8 = 134 // SCTE-35 - - PmtStreamTypeID3 uint8 = 21 // Nielsen ID3 - - PmtStreamTypePrivateContent uint8 = 6 // Private Content -) - -// PmtStreamType is used to represent elementary steam type inside a PMT -type PmtStreamType interface { - StreamType() uint8 - StreamTypeDescription() string - IsStreamWherePresentationLagsEbp() bool - IsAudioContent() bool - IsVideoContent() bool - IsSCTE35Content() bool - IsID3Content() bool - IsPrivateContent() bool -} -type pmtStreamType struct { - code uint8 - description string - presentationLagsEbp bool -} - -func (st pmtStreamType) StreamType() uint8 { - return st.code -} - -func (st pmtStreamType) StreamTypeDescription() string { - return st.description -} - -func (st pmtStreamType) IsStreamWherePresentationLagsEbp() bool { - return st.presentationLagsEbp -} - -func (st pmtStreamType) IsAudioContent() bool { - return st.code == PmtStreamTypeAac || - st.code == PmtStreamTypeAc3 || - st.code == PmtStreamTypeEc3 -} - -func (st pmtStreamType) IsVideoContent() bool { - return st.code == PmtStreamTypeMpeg4VideoH264 || - st.code == PmtStreamTypeMpeg4VideoH265 || - st.code == PmtStreamTypeMpeg4Video || - st.code == PmtStreamTypeMpeg2VideoH262 -} - -func (st pmtStreamType) IsSCTE35Content() bool { - return st.code == PmtStreamTypeScte35 -} - -func (st pmtStreamType) IsID3Content() bool { - return st.code == PmtStreamTypeID3 -} - -func (st pmtStreamType) IsPrivateContent() bool { - return st.code == PmtStreamTypePrivateContent -} - -func (st pmtStreamType) String() string { - return fmt.Sprintf("streamType=%d", st.code) -} - -// LookupPmtStreamType returns the associated PmtStreamType of the provided code. If the code is not recognized, a PmtSteamType of "unknown" is returned. -func LookupPmtStreamType(code uint8) PmtStreamType { - - for _, t := range atscPmtStreamTypes { - - if code >= t.firstCode && code <= t.lastCode { - - return *newPmtStreamType(code, t.description, presentationLagsEbp(code)) - } - } - - return *newPmtStreamType(code, "unknown", presentationLagsEbp(code)) -} - -func newPmtStreamType(code uint8, description string, presentationLagsEbp bool) *pmtStreamType { - return &pmtStreamType{code, description, presentationLagsEbp} -} - -func presentationLagsEbp(code uint8) bool { - switch code { - case 3, 4, 15, 17, 129, 135, 136: - return true - } - return false -} - -type atscPmtStreamType struct { - firstCode uint8 - lastCode uint8 - description string -} - -// Code/Descriptions transcribed from ATSC Code Point Registry at -// http://www.atsc.org/cms/index.php/standards/other-technical-documents/78-atsc-code-point-registry -// As of 4/8/2014 -var atscPmtStreamTypes = []atscPmtStreamType{ - {0, 0, "ITU-T | ISO/IEC Reserved"}, - {1, 1, "ISO/IEC 11172 Video "}, - {2, 2, "ITU-T Rec. H.262 | ISO/IEC 13818-2 Video"}, - {3, 3, "ISO/IEC 11172 Audio"}, - {4, 4, "ISO/IEC 13818-3 Audio"}, - {5, 5, "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private sections"}, - {6, 6, "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data"}, - {7, 7, "ISO/IEC 13522 MHEG"}, - {8, 8, "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 DSM-CC"}, - {9, 9, "ITU-T Rec. H.222.0 | ISO/IEC 13818-1/11172-1 auxiliary"}, - {10, 10, "ISO/IEC 13818-6 Multi-protocol Encapsulation"}, - {11, 11, "ISO/IEC 13818-6 DSM-CC U-N Messages"}, - {12, 12, "ISO/IEC 13818-6 Stream Descriptors"}, - {13, 13, "ISO/IEC 13818-6 Sections (any type, including private data)"}, - {14, 14, "ISO/IEC 13818-1 auxiliary"}, - {15, 15, "ISO/IEC 13818-7 Audio (AAC) with ADTS transport"}, - {16, 16, "ISO/IEC 14496-2 Visual"}, - {17, 17, "ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3"}, - {18, 18, "ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets"}, - {19, 19, "ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC 14496_sections"}, - {20, 20, "ISO/IEC 13818-6 DSM-CC Synchronized Download Protocol"}, - {21, 21, "Metadata carried in PES packets"}, - {22, 22, "Metadata carried in metadata_sections "}, - {23, 23, "Metadata carried in ISO/IEC 13818-6 Data Carousel"}, - {24, 24, "Metadata carried in ISO/IEC 13818-6 Object Carousel"}, - {25, 25, "Metadata carried in ISO/IEC 13818-6 Synchronized Download Protocol"}, - {26, 26, "IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP)"}, - {27, 27, "AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10 Video"}, - {28, 127, "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved"}, - {36, 36, "HEVC video stream as defined in ITU-T Rec. H.265 | ISO/IEC 23008-2 Video"}, - {128, 128, "DigiCipher® II video | Identical to ITU-T Rec. H.262 | ISO/IEC 13818-2 Video"}, - {129, 129, "ATSC A/53 audio [2] | AC-3 audio"}, - {130, 130, "SCTE Standard Subtitle"}, - {131, 131, "SCTE Isochronous Data | Reserved"}, - {132, 132, "ATSC/SCTE reserved"}, - {133, 133, "ATSC Program Identifier , SCTE Reserved"}, - {134, 134, "SCTE 35 splice_information_table | [Cueing]"}, - {135, 135, "E-AC-3"}, - //{ 135, 159 , " SCTE Reserved ", false }, - {136, 136, "DTS HD Audio"}, - {137, 137, "ATSC Reserved"}, - {138, 143, "ATSC Reserved"}, - {144, 144, "DVB stream_type value for Time Slicing / MPE-FEC"}, - {145, 145, "IETF Unidirectional Link Encapsulation (ULE)"}, - {146, 148, "ATSC Reserved"}, - {149, 149, "ATSC Data Service Table, Network Resources Table"}, - {150, 159, "ATSC Reserved"}, - {160, 160, "SCTE [IP Data] | ATSC Reserved"}, - {161, 191, "ATSC Reserved"}, - {192, 192, "DCII (DigiCipher®) Text"}, - {193, 193, "ATSC Reserved"}, - {194, 194, "ATSC synchronous data stream | [Isochronous Data]"}, - {195, 195, "SCTE Asynchronous Data"}, - {196, 233, "ATSC User Private Program Elements"}, - {234, 234, "VC-1 Elementary Stream per RP227"}, - {235, 255, "ATSC User Private Program Elements"}, -} diff --git a/v2/psi/psi.go b/v2/psi/psi.go deleted file mode 100644 index 1fb23fb..0000000 --- a/v2/psi/psi.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "github.com/Comcast/gots/v2" -) - -// TableHeader struct represents operations available on all PSI -type TableHeader struct { - TableID uint8 - SectionSyntaxIndicator bool - PrivateIndicator bool - SectionLength uint16 -} - -func PointerField(psi []byte) uint8 { - return psi[0] -} - -// TableID returns the psi table header table id -func TableID(psi []byte) uint8 { - return tableID(psi[1+PointerField(psi):]) -} - -// SectionSyntaxIndicator returns true if the psi contains section syntax -func SectionSyntaxIndicator(psi []byte) bool { - return sectionSyntaxIndicator(psi[1+PointerField(psi):]) -} - -// PrivateIndicator returns true if the psi contains private data -func PrivateIndicator(psi []byte) bool { - return psi[2+PointerField(psi)]&0x40 != 0 -} - -// SectionLength returns the psi section length -func SectionLength(psi []byte) uint16 { - offset := int(1 + PointerField(psi)) - if offset >= len(psi) { - return 0 - } - return sectionLength(psi[offset:]) -} - -// tableID returns the table id from the header of a section -func tableID(psi []byte) uint8 { - return uint8(psi[0]) -} - -// tableVersionAndCNI returns the table version_number and current_next_indicator -func tableVersionAndCNI(psi []byte) (uint8, bool, error) { - if len(psi) < 6 { - return 0, false, gots.ErrShortPayload - } - - // extract the 3rd-7th bits for version and bit immediately following for - // current_next_indicator - return uint8(psi[5]&0x3E) >> 1, (psi[5] & 0x1) == 0x01, nil -} - -func sectionSyntaxIndicator(psi []byte) bool { - return psi[1]&0x80 != 0 -} - -// sectionLength returns the length of a single psi section -func sectionLength(psi []byte) uint16 { - return uint16(psi[1]&3)<<8 | uint16(psi[2]) -} - -// NewPointerField will return a new pointer field with stuffing as raw bytes. -// The pointer field specifies where the TableHeader should start. -// Everything in between the pointer field and table header should -// be bytes with the value 0xFF. -func NewPointerField(size int) []byte { - data := make([]byte, size+1) - data[0] = byte(size) - for i := 1; i < size+1; i++ { - data[i] = 0xFF - } - return data -} - -// PSIFromBytes returns the PSI struct from a byte slice -func TableHeaderFromBytes(data []byte) (TableHeader, error) { - th := TableHeader{} - - if len(data) < 3 { - return th, gots.ErrShortPayload - } - - th.TableID = data[0] - th.SectionSyntaxIndicator = data[1]&0x80 != 0 - th.PrivateIndicator = data[1]&0x40 != 0 - th.SectionLength = uint16(data[1]&0x03 /* 0000 0011 */)<<8 | uint16(data[2]) - - return th, nil -} - -// Data returns the byte representation of the PSI struct. -func (th TableHeader) Data() []byte { - data := make([]byte, 3) - - data[0] = th.TableID - if th.SectionSyntaxIndicator { - data[1] |= 0x80 - } - if th.PrivateIndicator { - data[1] |= 0x40 - } - - // set reserved bits to 11 - data[1] |= 0x30 // 0011 0000 - - data[1] |= byte(th.SectionLength>>8) & 0x03 // 0000 0011 - data[2] = byte(th.SectionLength) - - return data -} - -// NewPSI will create a PSI with default values of zero and false for everything -func NewTableHeader() TableHeader { - return TableHeader{} -} diff --git a/v2/psi/psi_test.go b/v2/psi/psi_test.go deleted file mode 100644 index 17915ef..0000000 --- a/v2/psi/psi_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package psi - -import ( - "bytes" - "testing" -) - -func TestDefaultPSIData(t *testing.T) { - th := NewTableHeader() - generated := th.Data() - target := []byte{0x00, 0x30, 0x00} - if !bytes.Equal(target, generated) { - t.Errorf("NewTableHeader does not produce expected Data. \nExpected: %X \n Got: %X ", target, generated) - } -} - -func TestPSIFromBytes(t *testing.T) { - target := []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x18, 0xB1, 0xFF} - th, err := TableHeaderFromBytes(target[5:]) - if err != nil { - t.FailNow() - } - - if th.TableID != 0x18 { - t.Errorf("TableHeaderFromBytes does not produce expected TableID. \nExpected: %X \n Got: %X ", 0x18, th.TableID) - } - if th.SectionLength != 0x1FF { - t.Errorf("TableHeaderFromBytes does not produce expected SectionLength. \nExpected: %X \n Got: %X ", 0x1FF, th.SectionLength) - } - if th.PrivateIndicator { - t.Errorf("TableHeaderFromBytes does not produce expected PrivateIndicator. \nExpected: %t \n Got: %t ", true, th.PrivateIndicator) - } - if !th.SectionSyntaxIndicator { - t.Errorf("TableHeaderFromBytes does not produce expected SectionSyntaxIndicator. \nExpected: %t \n Got: %t ", false, th.SectionSyntaxIndicator) - } - generated := append(NewPointerField(4), th.Data()...) - if !bytes.Equal(target, generated) { - t.Errorf("Data does not produce same bytes. \nExpected: %X \n Got: %X ", target, generated) - } -} - -func TestPSICreate(t *testing.T) { - target := []byte{0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x18, 0x70, 0xFF} - th := NewTableHeader() - - th.TableID = 0x18 - th.SectionLength = 0x0FF - th.PrivateIndicator = true - th.SectionSyntaxIndicator = false - generated := append(NewPointerField(5), th.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Pointer field and TableHeader do not produce expected bytes. \nExpected: %X \n Got: %X ", target, generated) - } -} diff --git a/v2/pts.go b/v2/pts.go deleted file mode 100644 index b47be3b..0000000 --- a/v2/pts.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package gots - -import "math" - -// PTS constants -const ( - PTS_DTS_INDICATOR_BOTH = 3 // 11 - PTS_DTS_INDICATOR_ONLY_PTS = 2 // 10 - PTS_DTS_INDICATOR_NONE = 0 // 00 - - // MaxPtsValue is the highest value the PTS can hold before it rolls over, since its a 33 bit timestamp. - MaxPtsValue = (1 << 33) - 1 // 2^33 - 1 = 8589934591 = 0x1FFFFFFFF - - // MaxPtsTicks is the length of the complete PTS timeline. - MaxPtsTicks = 1 << 33 // 2^33 = 8589934592 = 0x200000000 - - // Used as a sentinel values for algorithms working against PTS - PtsNegativeInfinity = PTS(math.MaxUint64 - 1) //18446744073709551614 - PtsPositiveInfinity = PTS(math.MaxUint64) //18446744073709551615 - PtsClockRate = 90000 - - // UpperPtsRolloverThreshold is the threshold for a rollover on the upper end, MaxPtsValue - 30 min - UpperPtsRolloverThreshold = 8427934591 - // LowerPtsRolloverThreshold is the threshold for a rollover on the lower end, 30 min - LowerPtsRolloverThreshold = 162000000 -) - -// PTS represents PTS time -type PTS uint64 - -// After checks if this PTS is after the other PTS -func (p PTS) After(other PTS) bool { - switch { - case other == PtsPositiveInfinity: - return false - case other == PtsNegativeInfinity: - return true - case p.RolledOver(other): - return true - case other.RolledOver(p): - return false - default: - return p > other - } -} - -// GreaterOrEqual returns true if the method reciever is >= the provided PTS -func (p PTS) GreaterOrEqual(other PTS) bool { - if p == other { - return true - } - - return p.After(other) -} - -// RolledOver checks if this PTS just rollover compared to the other PTS -func (p PTS) RolledOver(other PTS) bool { - if other == PtsNegativeInfinity || other == PtsPositiveInfinity { - return false - } - - if p < LowerPtsRolloverThreshold && other > UpperPtsRolloverThreshold { - return true - } - return false -} - -// DurationFrom returns the difference between the two pts times. This number is always positive. -func (p PTS) DurationFrom(from PTS) uint64 { - switch { - case p.RolledOver(from): - return uint64((MaxPtsTicks - from) + p) - case from.RolledOver(p): - return uint64((MaxPtsTicks - p) + from) - case p < from: - return uint64(from - p) - default: - return uint64(p - from) - } -} - -// Add adds the two PTS times together and returns a new PTS -func (p PTS) Add(x PTS) PTS { - return (p + x) & MaxPtsValue -} - -// ExtractTime extracts a PTS time -func ExtractTime(bytes []byte) uint64 { - var a, b, c, d, e uint64 - a = uint64((bytes[0] >> 1) & 0x07) - b = uint64(bytes[1]) - c = uint64((bytes[2] >> 1) & 0x7f) - d = uint64(bytes[3]) - e = uint64((bytes[4] >> 1) & 0x7f) - return (a << 30) | (b << 22) | (c << 15) | (d << 7) | e -} - -// InsertPTS insterts a given pts time into a byte slice and sets the -// marker bits. len(b) >= 5 -func InsertPTS(b []byte, pts uint64) { - b[0] = byte(pts >> 29 & 0x0f) // PTS[32..30] - b[1] = byte(pts >> 22 & 0xff) // PTS[29..22] - b[2] = byte(pts >> 14 & 0xff) // PTS[21..15] - b[3] = byte(pts >> 7 & 0xff) // PTS[14..8] - b[4] = byte(pts&0xff) << 1 // PTS[7..0] - - // Set the marker bits as appropriate - b[0] |= 0x21 - b[2] |= 0x01 - b[4] |= 0x01 -} diff --git a/v2/pts_test.go b/v2/pts_test.go deleted file mode 100644 index 487ebb9..0000000 --- a/v2/pts_test.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package gots - -import "testing" - -func TestPTSIsAfterWithoutRollover(t *testing.T) { - p := PTS(2) - other := PTS(1) - if !p.After(other) { - t.Errorf("PTS=%v, not After other=%v", p, other) - } -} - -func TestPTSIsAfterWithRollover(t *testing.T) { - p := PTS(1) - other := PTS(8589934591) // MaxPtsValue - if !p.After(other) { - t.Errorf("PTS=%v, not After other=%v", p, other) - } - if other.After(p) { - t.Errorf("PTS=%v, After other=%v", other, p) - } - -} - -func TestPTSIsBeforePositiveInfinity(t *testing.T) { - p := PTS(1) - other := PtsPositiveInfinity - if p.After(other) { - t.Errorf("PTS=%v, not before other=%v", p, other) - } -} - -func TestPTSIsAfterNegativeInfinity(t *testing.T) { - p := PTS(0) - other := PtsNegativeInfinity - if !p.After(other) { - t.Errorf("PTS=%v, not before other=%v", p, other) - } -} - -func TestPTSIsNotAfterWithoutRollover(t *testing.T) { - p := PTS(2) - other := PTS(3) - if p.After(other) { - t.Errorf("PTS=%v, not After other=%v", p, other) - } -} - -func TestPTSIsNotAfterWithRolloverOverThreshold(t *testing.T) { - p := PTS(162000001) - other := PTS(8589934591) - if p.After(other) { - t.Errorf("PTS=%v, not After other=%v", p, other) - } -} - -func TestPTSRolledOver(t *testing.T) { - p := PTS(1) - other := PTS(8589934591) // MaxPtsValue - if !p.RolledOver(other) { - t.Errorf("PTS=%v, not After other=%v", p, other) - } -} - -func TestPTSDurationFrom(t *testing.T) { - if 5 != PTS(10).DurationFrom(PTS(5)) { - t.Error("Expected duration of 5") - } - - if 16 != PTS(5).DurationFrom(PTS(MaxPtsValue-10)) { - t.Error("Expected duration of 16") - } -} - -func TestPTSGreaterOrEqual(t *testing.T) { - if PTS(8589904323).GreaterOrEqual(PTS(146909)) { - t.Error("Greater or equal failed rollover") - } - - if !PTS(146909).GreaterOrEqual(PTS(8589904323)) { - t.Error("Greater or equal failed rollover") - } - - if !PTS(8589904323).GreaterOrEqual(PTS(8589904323)) { - t.Error("Greater or equal failed rollover") - } -} - -func TestAdd(t *testing.T) { - if PTS(6674924900) != PTS(7594224547).Add(PTS(7670634945)) { - t.Error("PTS addition 1 test failed") - } - if PTS(2000) != PTS(1500).Add(PTS(500)) { - t.Error("PTS addition 2 test failed") - } - // both of these values cannot fit in 33 bits - if PTS(0x187654321) != PTS(0x2E00000000).Add(PTS(0x5587654321)) { - t.Error("PTS addition 3 test failed") - } -} - -func TestInsertPTS(t *testing.T) { - var pts uint64 = 0x1DEADBEEF - b := make([]byte, 5) - InsertPTS(b, pts) - if ExtractTime(b) != 0x1DEADBEEF { - t.Error("Insert PTS test 1 failed") - } -} diff --git a/v2/scte35/descriptormodify.go b/v2/scte35/descriptormodify.go deleted file mode 100644 index 7dc0233..0000000 --- a/v2/scte35/descriptormodify.go +++ /dev/null @@ -1,273 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "github.com/Comcast/gots/v2" -) - -// SetUPIDType will set the type of the UPID -func (u *upidSt) SetUPIDType(value SegUPIDType) { - u.upidType = value -} - -// SetUPID set the actual UPID -func (u *upidSt) SetUPID(value []byte) { - u.upid = value -} - -// SetComponentTag sets the component tag, which is used for the identification of the component. -func (c *componentOffset) SetComponentTag(value byte) { - c.componentTag = value -} - -// SetPTSOffset sets the PTS offset of the component. -func (c *componentOffset) SetPTSOffset(value gots.PTS) { - c.ptsOffset = value -} - -// CreateSegmentationDescriptor creates and returns a default -// SegmentationDescriptor. -func CreateSegmentationDescriptor() SegmentationDescriptor { - return &segmentationDescriptor{} -} - -// Data returns the raw data bytes of the SegmentationDescriptor -func (d *segmentationDescriptor) Data() []byte { - var data, eventData []byte - data = make([]byte, 11) - data[0] = segDescTag - data[2] = byte(segDescID >> 24 & 0xFF) - data[3] = byte(segDescID >> 16 & 0xFF) - data[4] = byte(segDescID >> 8 & 0xFF) - data[5] = byte(segDescID & 0xFF) - data[6] = byte(d.eventID >> 24) - data[7] = byte(d.eventID >> 16) - data[8] = byte(d.eventID >> 8) - data[9] = byte(d.eventID) - data[10] = 0x7F // 0111 1111 reserved bits set to 1 - - if d.eventCancelIndicator { - data[10] |= 0x80 - } else { - eventData = make([]byte, 1) - - if d.deliveryNotRestricted { - eventData[0] |= 0x20 // 0010 0000 - eventData[0] |= 0x1F // 0001 1111 reserved fields all set to 1 - } else { - if d.webDeliveryAllowedFlag { - eventData[0] |= 0x10 // 0001 0000 - } - if d.noRegionalBlackoutFlag { - eventData[0] |= 0x08 // 0000 1000 - } - if d.archiveAllowedFlag { - eventData[0] |= 0x04 // 0000 0100 - } - eventData[0] |= byte(d.deviceRestrictions) & 0x03 // 0000 0011 - } - - if d.programSegmentationFlag { - eventData[0] |= 0x80 // 1000 0000 - } else { - componentsBytes := make([]byte, 1, 6*len(d.components)+1) - componentsBytes[0] = byte(len(d.components)) // set component count - for i := range d.components { - componentsBytes = append(componentsBytes, d.components[i].data()...) - } - eventData = append(eventData, componentsBytes...) - } - - if d.hasDuration { - eventData[0] |= 0x40 // 0100 0000 - durationBytes := make([]byte, 5) - durationBytes[0] = byte(d.duration >> 32) - durationBytes[1] = byte(d.duration >> 24) - durationBytes[2] = byte(d.duration >> 16) - durationBytes[3] = byte(d.duration >> 8) - durationBytes[4] = byte(d.duration) - eventData = append(eventData, durationBytes...) - } - UpidData := make([]byte, 2) - UpidData[0] = byte(d.upidType) - - if len(d.upid) > 0 { - UpidData = append(UpidData, d.upid...) - } else { - for i := range d.mid { - UpidData = append(UpidData, byte(d.mid[i].upidType)) - UpidData = append(UpidData, byte(d.mid[i].upidLen)) - UpidData = append(UpidData, d.mid[i].upid...) - } - } - UpidData[1] = byte(len(UpidData) - 2) - - eventData = append(eventData, UpidData...) - - segmentBytes := make([]byte, 3) - segmentBytes[0] = byte(d.typeID) - segmentBytes[1] = byte(d.segNum) - segmentBytes[2] = byte(d.segsExpected) - if ((d.typeID == 0x34) || (d.typeID == 0x36)) && d.hasSubSegments { - segmentBytes = append(segmentBytes, d.subSegNum, d.subSegsExpected) - } - eventData = append(eventData, segmentBytes...) - - data = append(data, eventData...) - - } - data[1] = byte(len(data) - 2) - return data -} - -// SetEventID sets the event id -func (d *segmentationDescriptor) SetEventID(value uint32) { - d.eventID = value -} - -// SetTypeID sets the type id -func (d *segmentationDescriptor) SetTypeID(value SegDescType) { - d.typeID = value - if d.typeID != 0x34 && d.typeID != 0x36 { - d.hasSubSegments = false - } -} - -// SetIsEventCanceled sets the the event cancel indicator -func (d *segmentationDescriptor) SetIsEventCanceled(value bool) { - d.eventCancelIndicator = value -} - -// SetHasDuration determines if a duration is present in the descriptor -func (d *segmentationDescriptor) SetHasDuration(value bool) { - d.hasDuration = value -} - -// SetDuration sets the duration of the descriptor, 40 bit unsigned integer, the rest of the bits will be truncated. -func (d *segmentationDescriptor) SetDuration(value gots.PTS) { - d.duration = value & 0xFFFFFFFFFF // keep 40 bits, truncate the rest. -} - -// SetUPIDType sets the type of upid, only works if UPIDType is not SegUPIDMID -func (d *segmentationDescriptor) SetUPIDType(value SegUPIDType) { - d.upidType = value - // only one can be set at a time - if d.upidType == SegUPIDMID { - d.upid = []byte{} - } else if d.upidType == SegUPIDNotUsed { - d.mid = []upidSt{} - d.upid = []byte{} - } else { - d.mid = []upidSt{} - } -} - -// SetUPID sets the upid of the descriptor -func (d *segmentationDescriptor) SetUPID(value []byte) { - // Check if this data can be set - if d.upidType == SegUPIDMID { - return - } - d.upid = value -} - -// SetSegmentNumber sets the segment number for this descriptor. -func (d *segmentationDescriptor) SetSegmentNumber(value uint8) { - d.segNum = value -} - -// SetSegmentsExpected sets the number of expected segments for this descriptor. -func (d *segmentationDescriptor) SetSegmentsExpected(value uint8) { - d.segsExpected = value -} - -// SetSubSegmentNumber sets the sub-segment number for this descriptor. -func (d *segmentationDescriptor) SetSubSegmentNumber(value uint8) { - d.subSegNum = value -} - -// SetSubSegmentsExpected sets the number of expected sub-segments for this descriptor. -func (d *segmentationDescriptor) SetSubSegmentsExpected(value uint8) { - d.subSegsExpected = value -} - -// SetHasProgramSegmentation if the descriptor has program segmentation -func (d *segmentationDescriptor) SetHasProgramSegmentation(value bool) { - d.programSegmentationFlag = value -} - -// SetIsDeliveryNotRestricted sets a flag that determines if the delivery is not restricted -func (d *segmentationDescriptor) SetIsDeliveryNotRestricted(value bool) { - d.deliveryNotRestricted = value -} - -// SetIsWebDeliveryAllowed sets a flag that determines if web delivery is allowed, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) SetIsWebDeliveryAllowed(value bool) { - d.webDeliveryAllowedFlag = value -} - -// SetIsArchiveAllowed sets a flag that determines if there are restrictions to storing/recording this segment, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) SetIsArchiveAllowed(value bool) { - d.archiveAllowedFlag = value -} - -// SetHasNoRegionalBlackout sets a flag that determines if there is no regional blackout, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) SetHasNoRegionalBlackout(value bool) { - d.noRegionalBlackoutFlag = value -} - -// SetDeviceRestrictions sets which device group the segment is restriced to, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) SetDeviceRestrictions(value DeviceRestrictions) { - d.deviceRestrictions = value -} - -// SetMID sets multiple UPIDs, only works if UPIDType is SegUPIDMID -func (d *segmentationDescriptor) SetMID(value []UPID) { - // Check if this data can be set - if d.upidType != SegUPIDMID { - return - } - d.mid = make([]upidSt, len(value)) - for i := range value { - d.mid[i].upidType = value[i].UPIDType() - d.mid[i].upid = value[i].UPID() - d.mid[i].upidLen = len(d.mid[i].upid) - } -} - -// SetComponents will set components' offsets -func (d *segmentationDescriptor) SetComponents(value []ComponentOffset) { - d.components = make([]componentOffset, len(value)) - for i := range value { - d.components[i].componentTag = value[i].ComponentTag() - d.components[i].ptsOffset = value[i].PTSOffset() - } -} - -// SetHasSubSegments sets the field that determines if this segmentation descriptor has sub subsegments. -func (d *segmentationDescriptor) SetHasSubSegments(value bool) { - d.hasSubSegments = value -} diff --git a/v2/scte35/doc.go b/v2/scte35/doc.go deleted file mode 100644 index 830cb50..0000000 --- a/v2/scte35/doc.go +++ /dev/null @@ -1,506 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Package scte35 is for handling scte35 splice signals -package scte35 - -import ( - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/psi" -) - -// SpliceCommandType is a type used to describe the types of splice commands. -type SpliceCommandType uint16 - -const ( - // SpliceNull is a Null Splice command type - SpliceNull SpliceCommandType = 0x00 - // SpliceSchedule is a splice schedule command type - SpliceSchedule = 0x04 - // SpliceInsert is a splice insert command type - SpliceInsert = 0x05 - // TimeSignal is a splice signal command type - TimeSignal = 0x06 - // BandwidthReservation is a command type that represents a reservation of bandwidth - BandwidthReservation = 0x07 - // PrivateCommand is a command type that represents private command data - PrivateCommand = 0xFF -) - -var SpliceCommandTypeNames = map[SpliceCommandType]string{ - SpliceNull: "SpliceNull", - SpliceSchedule: "SpliceSchedule", - SpliceInsert: "SpliceInsert", - TimeSignal: "TimeSignal", - BandwidthReservation: "BandwidthReservation", - PrivateCommand: "PrivateCommand", -} - -// DeviceRestrictions type is used to specify what group that the segment is restricted to. -type DeviceRestrictions byte - -const ( - RestrictGroup0 DeviceRestrictions = 0x00 - RestrictGroup1 DeviceRestrictions = 0x01 - RestrictGroup2 DeviceRestrictions = 0x02 - RestrictNone DeviceRestrictions = 0x03 // no restrictions -) - -var DeviceRestrictionsNames = map[DeviceRestrictions]string{ - RestrictGroup0: "RestrictGroup0", - RestrictGroup1: "RestrictGroup1", - RestrictGroup2: "RestrictGroup2", - RestrictNone: "RestrictNone", -} - -// SegDescType is the Segmentation Descriptor Type - not really needed for processing according -// to method below, but included here for backwards compatibility/porting -type SegDescType uint8 - -const ( - SegDescNotIndicated SegDescType = 0x00 - SegDescContentIdentification = 0x01 - SegDescProgramStart = 0x10 - SegDescProgramEnd = 0x11 - SegDescProgramEarlyTermination = 0x12 - SegDescProgramBreakaway = 0x13 - SegDescProgramResumption = 0x14 - SegDescProgramRunoverPlanned = 0x15 - SegDescProgramRunoverUnplanned = 0x16 - SegDescProgramOverlapStart = 0x17 - SegDescProgramBlackoutOverride = 0x18 - SegDescProgramStartInProgress = 0x19 - SegDescChapterStart = 0x20 - SegDescChapterEnd = 0x21 - SegDescBreakStart = 0x22 - SegDescBreakEnd = 0x23 - SegDescOpeningCreditStart = 0x24 - SegDescOpeningCreditEnd = 0x25 - SegDescClosingCreditStart = 0x26 - SegDescClosingCreditEnd = 0x27 - SegDescProviderAdvertisementStart = 0x30 - SegDescProviderAdvertisementEnd = 0x31 - SegDescDistributorAdvertisementStart = 0x32 - SegDescDistributorAdvertisementEnd = 0x33 - SegDescProviderPOStart = 0x34 - SegDescProviderPOEnd = 0x35 - SegDescDistributorPOStart = 0x36 - SegDescDistributorPOEnd = 0x37 - SegDescProviderPromoStart = 0x3C - SegDescProviderPromoEnd = 0x3D - SegDescUnscheduledEventStart = 0x40 - SegDescUnscheduledEventEnd = 0x41 - SegDescAlternateContentOpportunityStart = 0x42 - SegDescAlternateContentOpportunityEnd = 0x43 - SegDescProviderAdBlockStart = 0x44 - SegDescProviderAdBlockEnd = 0x45 - SegDescNetworkStart = 0x50 - SegDescNetworkEnd = 0x51 -) - -var SegDescTypeNames = map[SegDescType]string{ - SegDescNotIndicated: "SegDescNotIndicated", - SegDescContentIdentification: "SegDescContentIdentification", - SegDescProgramStart: "SegDescProgramStart", - SegDescProgramEnd: "SegDescProgramEnd", - SegDescProgramEarlyTermination: "SegDescProgramEarlyTermination", - SegDescProgramBreakaway: "SegDescProgramBreakaway", - SegDescProgramResumption: "SegDescProgramResumption", - SegDescProgramRunoverPlanned: "SegDescProgramRunoverPlanned", - SegDescProgramRunoverUnplanned: "SegDescProgramRunoverUnplanned", - SegDescProgramOverlapStart: "SegDescProgramOverlapStart", - SegDescProgramBlackoutOverride: "SegDescProgramBlackoutOverride", - SegDescProgramStartInProgress: "SegDescProgramStartInProgress", - SegDescChapterStart: "SegDescChapterStart", - SegDescChapterEnd: "SegDescChapterEnd", - SegDescBreakStart: "SegDescBreakStart", - SegDescBreakEnd: "SegDescBreakEnd", - SegDescProviderAdvertisementStart: "SegDescProviderAdvertisementStart", - SegDescProviderAdvertisementEnd: "SegDescProviderAdvertisementEnd", - SegDescOpeningCreditStart: "SegDescOpeningCreditStart", - SegDescOpeningCreditEnd: "SegDescOpeningCreditEnd", - SegDescClosingCreditStart: "SegDescClosingCreditStart", - SegDescClosingCreditEnd: "SegDescClosingCreditEnd", - SegDescDistributorAdvertisementStart: "SegDescDistributorAdvertisementStart", - SegDescDistributorAdvertisementEnd: "SegDescDistributorAdvertisementEnd", - SegDescProviderPOStart: "SegDescProviderPOStart", - SegDescProviderPOEnd: "SegDescProviderPOEnd", - SegDescDistributorPOStart: "SegDescDistributorPOStart", - SegDescDistributorPOEnd: "SegDescDistributorPOEnd", - SegDescProviderPromoStart: "SegDescProviderPromoStart", - SegDescProviderPromoEnd: "SegDescProviderPromoEnd", - SegDescUnscheduledEventStart: "SegDescUnscheduledEventStart", - SegDescUnscheduledEventEnd: "SegDescUnscheduledEventEnd", - SegDescAlternateContentOpportunityStart: "SegDescAlternateContentOpportunityStart", - SegDescAlternateContentOpportunityEnd: "SegDescAlternateContentOpportunityEnd", - SegDescProviderAdBlockStart: "SegDescProviderAdBlockStart", - SegDescProviderAdBlockEnd: "SegDescProviderAdBlockEnd", - SegDescNetworkStart: "SegDescNetworkStart", - SegDescNetworkEnd: "SegDescNetworkEnd", -} - -// SegUPIDType is the Segmentation UPID Types - Only type that really needs to be checked is -// SegUPIDURN for CSP -type SegUPIDType uint8 - -const ( - SegUPIDNotUsed SegUPIDType = 0x00 - SegUPIDUserDefined = 0x01 - SegUPIDISCI = 0x02 - SegUPIDAdID = 0x03 - SegUPIDUMID = 0x04 - SegUPIDISAN = 0x05 - SegUPIDVISAN = 0x06 - SegUPIDTID = 0x07 - SegUPIDTI = 0x08 - SegUPIDADI = 0x09 - SegUPIDEIDR = 0x0a - SegUPIDATSCID = 0x0b - SegUPIDMPU = 0x0c - SegUPIDMID = 0x0d - SegUPADSINFO = 0x0e - SegUPIDURN = 0x0f -) - -var SegUPIDTypeNames = map[SegUPIDType]string{ - SegUPIDNotUsed: "SegUPIDNotUsed", - SegUPIDUserDefined: "SegUPIDUserDefined", - SegUPIDISCI: "SegUPIDISCI", - SegUPIDAdID: "SegUPIDAdID", - SegUPIDUMID: "SegUPIDUMID", - SegUPIDISAN: "SegUPIDISAN", - SegUPIDVISAN: "SegUPIDVISAN", - SegUPIDTID: "SegUPIDTID", - SegUPIDTI: "SegUPIDTI", - SegUPIDADI: "SegUPIDADI", - SegUPIDEIDR: "SegUPIDEIDR", - SegUPIDATSCID: "SegUPIDATSCID", - SegUPIDMPU: "SegUPIDMPU", - SegUPIDMID: "SegUPIDMID", - SegUPADSINFO: "SegUPADSINFO", - SegUPIDURN: "SegUPIDURN", -} - -// SCTE35 represent operations available on a SCTE35 message. -type SCTE35 interface { - // HasPTS returns true if there is a pts time. - HasPTS() bool - // SetHasPTS sets if this SCTE35 message has a PTS. - SetHasPTS(flag bool) - // PTS returns the PTS time of the signal if it exists. Includes adjustment. - PTS() gots.PTS - // SetPTS sets the PTS time of the signal's command. There will be no PTS adjustment using this function. - // If HasPTS is false, then it will have no effect until it is set to true. Also this command has no - // effect with a null splice command. - SetPTS(pts gots.PTS) - // SetAdjustPTS will modify the pts adjustment field. The desired PTS value - // after adjustment should be passed, The adjustment value will be calculated - // during the call to Data(). - SetAdjustPTS(pts gots.PTS) - // Tier returns which authorization tier this message was assigned to. - // The tier value of 0XFFF is the default and will ignored. When using tier values, - // the SCTE35 message must fit entirely into the ts payload without being split up. - // The tier is a 12 bit unsigned integer. - Tier() uint16 - // SetTier sets which authorization tier this message was assigned to. - // The tier value of 0XFFF is the default and will ignored. When using tiers, - // the SCTE35 message must fit entirely into the ts payload without being split up. - // The tier is a 12 bit unsigned integer. - SetTier(tier uint16) - // Command returns the signal's splice command. - Command() SpliceCommandType - // CommandInfo returns an object describing fields of the signal's splice - // command structure. - CommandInfo() SpliceCommand - // SetCommandInfo sets the object describing fields of the signal's splice - // command structure - SetCommandInfo(commandInfo SpliceCommand) - // Descriptors returns a slice of the signals SegmentationDescriptors sorted - // by descriptor weight (least important signals first) - Descriptors() []SegmentationDescriptor - // SetDescriptors sets a slice of the signals SegmentationDescriptors they - // should be sorted by descriptor weight (least important signals first) - SetDescriptors(descriptors []SegmentationDescriptor) - // AlignmentStuffing returns how many stuffing bytes will be added to the SCTE35 - // message at the end. - AlignmentStuffing() uint - // SetAlignmentStuffing sets how many stuffing bytes will be added to the SCTE35 - // message at the end. - SetAlignmentStuffing(alignmentStuffing uint) - // UpdateData will encode the SCTE35 information to bytes and return it. - // UpdateData will make the next call to Data() return these new bytes. - UpdateData() []byte - // Data returns the raw data bytes of the scte signal. It will not change - // unless a call to UpdateData() is issued before this. - Data() []byte - // String returns a string representation of the SCTE35 message. - // String function is for debugging and testing. - String() string -} - -// SpliceCommand represent operations available on all SpliceCommands. -type SpliceCommand interface { - // CommandType returns the signal's splice command type value. - CommandType() SpliceCommandType - // HasPTS returns true if there is a pts time on the command. - HasPTS() bool - // PTS returns the PTS time of the command, not including adjustment. - PTS() gots.PTS - // SetHasPTS sets the flag that indicates if there is a pts time on the command. - SetHasPTS(value bool) - // SetPTS sets the PTS. - SetPTS(value gots.PTS) - // Data returns the bytes of this splice command. - Data() []byte -} - -// TimeSignalCommand is a type of SpliceCommand. -type TimeSignalCommand interface { - SpliceCommand -} - -// Component is an interface for components, a structure in SpliceInsertCommand. -type Component interface { - // ComponentTag returns the tag of the component. - ComponentTag() byte - // HasPTS returns if the component has a PTS. - HasPTS() bool - // PTS returns the PTS of the component. - PTS() gots.PTS - // SetComponentTag sets the component tag, which is used for the identification of the component. - SetComponentTag(value byte) - // SetHasPTS sets a flag that determines if the component has a PTS. - SetHasPTS(value bool) - // SetPTS sets the PTS of the component. - SetPTS(value gots.PTS) -} - -// ComponentOffset is an interface for componentOffset, a structure in SegmentationDescriptor. -type ComponentOffset interface { - // ComponentTag returns the tag of the component. - ComponentTag() byte - // PTSOffset returns the PTS offset of the component. - PTSOffset() gots.PTS - // SetComponentTag sets the component tag, which is used for the identification of the component. - SetComponentTag(value byte) - // SetPTSOffset sets the PTS offset of the component. - SetPTSOffset(value gots.PTS) -} - -// SpliceInsertCommand is a type of SpliceCommand. -type SpliceInsertCommand interface { - SpliceCommand - // EventID returns the event id - EventID() uint32 - // SetEventID sets the event id - SetEventID(value uint32) - // IsEventCanceled returns the event cancel indicator - IsEventCanceled() bool - // SetIsEventCanceled sets the the event cancel indicator - SetIsEventCanceled(value bool) - // IsOut returns the value of the out of network indicator - IsOut() bool - // SetIsOut sets the out of network indicator - SetIsOut(value bool) - // IsProgramSplice returns if the program_splice_flag is set - IsProgramSplice() bool - // SetIsProgramSplice sets the program splice flag - SetIsProgramSplice(value bool) - // HasDuration returns true if there is a duration - HasDuration() bool - // SetHasDuration sets the duration flag - SetHasDuration(value bool) - // SpliceImmediate returns if the splice_immediate_flag is set - SpliceImmediate() bool - // SetSpliceImmediate sets the splice immediate flag - SetSpliceImmediate(value bool) - // Components returns the components of the splice command - Components() []Component - // IsAutoReturn returns the boolean value of the auto return field - IsAutoReturn() bool - // SetIsAutoReturn sets the auto_return flag - SetIsAutoReturn(value bool) - // Duration returns the PTS duration of the command - Duration() gots.PTS - // SetDuration sets the PTS duration of the command - SetDuration(value gots.PTS) - // UniqueProgramId returns the unique_program_id field - UniqueProgramId() uint16 - // SetUniqueProgramId sets the unique program Id - SetUniqueProgramId(value uint16) - // AvailNum returns the avail_num field, index of this avail or zero if unused - AvailNum() uint8 - // SetAvailNum sets the avail_num field, zero if unused. otherwise index of the avail - SetAvailNum(value uint8) - // AvailsExpected returns avails_expected field, number of avails for program - AvailsExpected() uint8 - // SetAvailsExpected sets the expected number of avails - SetAvailsExpected(value uint8) -} - -// UPID describes the UPID, this is only used for MID. -type UPID interface { - // UPIDType returns the type of UPID stored - UPIDType() SegUPIDType - // UPID returns the actual UPID - UPID() []byte - // SetUPIDType will set the type of the UPID - SetUPIDType(value SegUPIDType) - // SetUPID set the actual UPID - SetUPID(value []byte) -} - -// SegmentationDescriptor describes the segmentation descriptor interface. -type SegmentationDescriptor interface { - // SCTE35 returns the SCTE35 signal this segmentation descriptor was found in. - SCTE35() SCTE35 - // EventID returns the event id - EventID() uint32 - // SetEventID sets the event id - SetEventID(value uint32) - // IsEventCanceled returns the event cancel indicator - IsEventCanceled() bool - // SetIsEventCanceled sets the the event cancel indicator - SetIsEventCanceled(value bool) - // HasProgramSegmentation returns if the descriptor has program segmentation - HasProgramSegmentation() bool - // SetHasProgramSegmentation if the descriptor has program segmentation - SetHasProgramSegmentation(value bool) - // HasDuration returns true if there is a duration associated with the descriptor - HasDuration() bool - // SetHasDuration determines if a duration is present in the descriptor - SetHasDuration(value bool) - // Duration returns the duration of the descriptor, 40 bit unsigned integer. - Duration() gots.PTS - // SetDuration sets the duration of the descriptor, 40 bit unsigned integer. extra bits will be truncated. - SetDuration(value gots.PTS) - // IsDeliveryNotRestricted returns if the delivery is not restricted - IsDeliveryNotRestricted() bool - // SetIsDeliveryNotRestricted sets a flag that determines if the delivery is not restricted - SetIsDeliveryNotRestricted(bool) - // IsWebDeliveryAllowed returns if web delivery is allowed, this field has no meaning if delivery is not restricted. - IsWebDeliveryAllowed() bool - // SetIsWebDeliveryAllowed sets a flag that determines if web delivery is allowed, this field has no meaning if delivery is not restricted. - SetIsWebDeliveryAllowed(bool) - // HasNoRegionalBlackout returns true if there is no regional blackout, this field has no meaning if delivery is not restricted. - HasNoRegionalBlackout() bool - // SetHasNoRegionalBlackout sets a flag that determines if there is no regional blackout, this field has no meaning if delivery is not restricted. - SetHasNoRegionalBlackout(bool) - // IsArchiveAllowed returns true if there are restrictions to storing/recording this segment, this field has no meaning if delivery is not restricted. - IsArchiveAllowed() bool - // SetIsArchiveAllowed sets a flag that determines if there are restrictions to storing/recording this segment, this field has no meaning if delivery is not restricted. - SetIsArchiveAllowed(bool) - // DeviceRestrictions returns which device group the segment is restriced to, this field has no meaning if delivery is not restricted. - DeviceRestrictions() DeviceRestrictions - // SetDeviceRestrictions sets which device group the segment is restriced to, this field has no meaning if delivery is not restricted. - SetDeviceRestrictions(DeviceRestrictions) - // Components will return components' offsets - Components() []ComponentOffset - // SetComponents will set components' offsets - SetComponents([]ComponentOffset) - // UPIDType returns the type of the upid - UPIDType() SegUPIDType - // SetUPIDType sets the type of upid, only works if UPIDType is not SegUPIDMID - SetUPIDType(value SegUPIDType) - // UPID returns the upid of the descriptor, if the UPIDType is not SegUPIDMID - UPID() []byte - // SetUPID sets the upid of the descriptor - SetUPID(value []byte) - // MID returns multiple UPIDs, if UPIDType is SegUPIDMID - MID() []UPID - // SetMID sets multiple UPIDs, only works if UPIDType is SegUPIDMID - SetMID(value []UPID) - // TypeID returns the segmentation type for descriptor - TypeID() SegDescType - // SetTypeID sets the type id - SetTypeID(value SegDescType) - // SegmentNumber returns the segment number for this descriptor. - SegmentNumber() uint8 - // SetSegmentNumber sets the segment number for this descriptor. - SetSegmentNumber(value uint8) - // SegmentsExpected returns the number of expected segments for this descriptor. - SegmentsExpected() uint8 - // SetSegmentsExpected sets the number of expected segments for this descriptor. - SetSegmentsExpected(value uint8) - // HasSubSegments returns true if this segmentation descriptor has subsegment fields. - HasSubSegments() bool - // SetHasSubSegments sets the field that determines if this segmentation descriptor has sub subsegments. - SetHasSubSegments(bool) - // SubSegmentNumber returns the sub-segment number for this descriptor. - SubSegmentNumber() uint8 - // SetSubSegmentNumber sets the sub-segment number for this descriptor. - SetSubSegmentNumber(value uint8) - // SubSegmentsExpected returns the number of expected sub-segments for this descriptor. - SubSegmentsExpected() uint8 - // SetSubSegmentsExpected sets the number of expected sub-segments for this descriptor. - SetSubSegmentsExpected(value uint8) - // StreamSwitchSignalID returns the signalID of streamswitch signal if - // present in the descriptor - StreamSwitchSignalId() (string, error) - // IsOut returns true if a signal is an out - IsOut() bool - // IsIn returns true if a signal is an in - IsIn() bool - // CanClose returns true if this descriptor can close the passed in descriptor - CanClose(out SegmentationDescriptor) bool - // Equal returns true/false if segmentation descriptor is functionally - // equal (i.e. a duplicate) - Equal(sd SegmentationDescriptor) bool - // Data returns the raw data bytes of the SegmentationDescriptor - Data() []byte - // SegmentNum is deprecated, use SegmentNumber instead. - // SegmentNum returns the segment_num descriptor field. - SegmentNum() uint8 -} - -// State maintains current state for all signals and descriptors. The intended -// usage is to call ParseSCTE35() on raw data to create a signal, and then call -// ProcessSignal with that signal. This returns the list of descriptors closed -// by that signal. If signals have a duration and need to be closed implicitly -// after some timer has passed, then Close() can be used for that. Some -// example code is below. -// s := scte35.NewState() -// scte,_ := scte.ParseSCTE35(bytes) -// for _,d := range(scte.Descriptors()) { -// closed = s.ProcessDescriptor(d) -// ...handle closed signals appropriately here -// if d.HasDuration() { -// time.AfterFunc(d.Duration() + someFudgeDelta, -// func() { closed = s.Close(d) }) -// } -// } -type State interface { - // Open returns a list of open signals - Open() []SegmentationDescriptor - // ProcessDescriptor takes a scte35 descriptor and returns a list of descriptors closed by it - ProcessDescriptor(desc SegmentationDescriptor) ([]SegmentationDescriptor, error) - // Close acts like Process and acts as if an appropriate close has been - // received for this given descriptor. - Close(desc SegmentationDescriptor) ([]SegmentationDescriptor, error) -} - -// SCTE done func is the same as the PMT because they're both psi -func SCTE35AccumulatorDoneFunc(b []byte) (bool, error) { - return psi.PmtAccumulatorDoneFunc(b) -} diff --git a/v2/scte35/modify.go b/v2/scte35/modify.go deleted file mode 100644 index 46927d7..0000000 --- a/v2/scte35/modify.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/psi" -) - -// CreateSCTE35 creates a default SCTE35 message and returns it. -// The default message has the tier 0xFFF and a Splice Null command. -func CreateSCTE35() SCTE35 { - scte35 := - &scte35{ - protocolVersion: 0, // only version 0 exists - encryptedPacket: false, // no support for encryption - encryptionAlgorithm: 0, // no encryption support, no way to change - pts: 0, // no pts - cwIndex: 0, // undefined, without encryption - tier: 0xFFF, // ignore tier value - - commandType: SpliceNull, // null command type by default - commandInfo: &spliceNull{}, // info pooints to null command - - descriptors: []SegmentationDescriptor{}, // empty slice of descriptors - - } - scte35.tableHeader = - psi.TableHeader{ - TableID: 0xFC, // always 0xFC for scte35 - SectionSyntaxIndicator: false, // always false for scte35 - PrivateIndicator: false, // always false for scte35 - SectionLength: 0, // to be calculated when converting to bytes - } - return scte35 -} - -// subtractPTS subtracts the two PTS times and returns a new PTS -// This is different from the durationFrom function since the -// order of operands matters. It will always subtract final -// minus initial. If the result is negative then it will add -// MaxPtsTicks to produce a positive number -// (in this case it assumes rollover happened). -// used for calculating pts adjustment. -func subtractPTS(final gots.PTS, initial gots.PTS) gots.PTS { - if final >= initial { - return final - initial - } else { - return gots.MaxPtsTicks - (initial - final) - } -} - -// UpdateData will encode the SCTE35 information to bytes and return it. -// UpdateData will make the next call to Data() return these new bytes. -func (s *scte35) UpdateData() []byte { - // splice command generate bytes - spliceCommandBytes := s.commandInfo.Data() - // spliceCommandLength can be set as 0xFFF (undefined), but calculate it anyways - spliceCommandLength := len(spliceCommandBytes) - s.spliceCommandLength = uint16(spliceCommandLength) - - // generate bytes for splice descriptors - descriptorBytes := make([]byte, 2) - // append descriptors that are not extracted - descriptorBytes = append(descriptorBytes, s.otherDescriptorBytes...) - // append segmentation descriptors - for i := range s.descriptors { - descriptorBytes = append(descriptorBytes, s.descriptors[i].Data()...) - } - descriptorLoopLength := len(descriptorBytes) - 2 - descriptorBytes[0] = byte(descriptorLoopLength >> 8) - descriptorBytes[1] = byte(descriptorLoopLength) - - const staticFieldsLength = 13 - const crcLength = int(psi.CrcLen) - - sectionLength := staticFieldsLength + spliceCommandLength + descriptorLoopLength + crcLength + int(s.alignmentStuffing) - s.tableHeader.SectionLength = uint16(sectionLength) - - tableHeaderBytes := s.tableHeader.Data() - tableHeaderLength := len(tableHeaderBytes) - - // slices that point to the starting position of their names - data := make([]byte, tableHeaderLength+sectionLength) - tableHeader := data - section := tableHeader[tableHeaderLength:] - spliceCommand := section[11:] - spliceDescriptor := spliceCommand[spliceCommandLength:] - crc := data[len(data)-crcLength:] - - ptsAdj := subtractPTS(s.pts, s.commandInfo.PTS()) - - if s.encryptedPacket { - section[1] = 0x80 // 1000 0000 - } - section[0] = s.protocolVersion // 1111 1111 - section[1] |= (s.encryptionAlgorithm & 0x3F) << 1 // 0111 1110 - section[1] |= byte(ptsAdj>>32) & 0x01 // 0000 0001 - section[2] = byte(ptsAdj >> 24) // 1111 1111 - section[3] = byte(ptsAdj >> 16) // 1111 1111 - section[4] = byte(ptsAdj >> 8) // 1111 1111 - section[5] = byte(ptsAdj) // 1111 1111 - section[6] = s.cwIndex // 1111 1111 - section[7] = byte(s.tier >> 4) // 1111 1111 - section[8] = byte(s.tier << 4) // 1111 0000 - section[8] |= byte(s.spliceCommandLength>>8) & 0x0F // 0000 1111 - section[9] = byte(s.spliceCommandLength) // 1111 1111 - section[10] = byte(s.commandType) // 1111 1111 - - copy(tableHeader, tableHeaderBytes) - copy(spliceCommand, spliceCommandBytes) - copy(spliceDescriptor, descriptorBytes) - - crcBytes := gots.ComputeCRC(tableHeader[:len(data)-crcLength]) - copy(crc, crcBytes) - s.data = data - return data -} - -// SetHasPTS sets if this SCTE35 message has a PTS. -func (s *scte35) SetHasPTS(flag bool) { - s.commandInfo.SetHasPTS(true) -} - -// SetPTS sets the PTS time of the signal's command. There will be no PTS adjustment using this function. -// If HasPTS is false, then it will have no effect until it is set to true. Also this command has no -// effect with a null splice command. -func (s *scte35) SetPTS(pts gots.PTS) { - s.pts = pts - s.commandInfo.SetPTS(s.pts & 0x01ffffffff) // truncate to fit in 33 bits - // pts adjustment will be zero since the difference between adjusted and command pts is zero -} - -// SetAdjustPTS will modify the pts adjustment field. The desired PTS value -// after adjustment should be passed, The adjustment value will be calculated -// during the call to Data(). -func (s *scte35) SetAdjustPTS(pts gots.PTS) { - // adjustment will be done by the function that generates the bytes - s.pts = pts -} - -// SetCommandInfo sets the object describing fields of the signal's splice -// command structure -func (s *scte35) SetCommandInfo(commandInfo SpliceCommand) { - s.commandInfo = commandInfo - s.commandType = s.commandInfo.CommandType() -} - -// SetDescriptors sets a slice of the signals SegmentationDescriptors they -// should be sorted by descriptor weight (least important signals first) -func (s *scte35) SetDescriptors(descriptors []SegmentationDescriptor) { - s.descriptors = descriptors - for i := range s.descriptors { - s.descriptors[i].(*segmentationDescriptor).spliceInfo = s - } -} - -// SetAlignmentStuffing sets how many stuffing bytes will be added to the SCTE35 -// message at the end. -func (s *scte35) SetAlignmentStuffing(alignmentStuffing uint) { - s.alignmentStuffing = alignmentStuffing -} - -// SetTier sets which authorization tier this message was assigned to. -// The tier value of 0XFFF is the default and will ignored. When using tiers, -// the SCTE35 message must fit entirely into the ts payload without being split up. -func (s *scte35) SetTier(tier uint16) { - s.tier = tier & 0xFFF -} diff --git a/v2/scte35/modify_test.go b/v2/scte35/modify_test.go deleted file mode 100644 index a0cff4a..0000000 --- a/v2/scte35/modify_test.go +++ /dev/null @@ -1,419 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "bytes" - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/psi" - "testing" -) - -var testScteCreate = []byte{ - 0x00, 0xFC, 0x30, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFE, - 0x86, 0xDF, 0x75, 0x50, 0x00, 0x11, 0x02, 0x0F, 0x43, 0x55, 0x45, 0x49, 0x41, 0x42, 0x43, 0x44, - 0x7F, 0x8F, 0x00, 0x00, 0x10, 0x01, 0x01, 0x0B, 0xFD, 0xD1, 0x40, -} - -var testScteCreate2 = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x04, 0x54, 0x45, 0x53, 0x54, 0x40, 0x00, 0x00, - 0x25, 0x12, 0xF4, 0x01, -} - -var testScteCreate3 = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x04, 0x54, 0x45, 0x53, 0x54, 0x51, 0x00, 0x00, - 0x39, 0x40, 0x90, 0xF6, -} - -var testScteCreate4 = []byte{ - 0x00, 0xFC, 0x30, 0x53, 0x00, 0x00, 0x00, 0x02, 0xDD, 0x20, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFE, - 0x00, 0x08, 0x95, 0x44, 0x00, 0x3D, 0x02, 0x3B, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7F, 0x1F, 0x02, 0x01, 0xFE, 0x00, 0x2D, 0xD2, 0x00, 0x02, 0xFE, 0x00, 0x00, 0x01, 0xE8, 0x09, - 0x1F, 0x53, 0x49, 0x47, 0x4E, 0x41, 0x4C, 0x3A, 0x59, 0x38, 0x6F, 0x30, 0x44, 0x33, 0x7A, 0x70, - 0x54, 0x78, 0x53, 0x30, 0x4C, 0x54, 0x31, 0x65, 0x77, 0x2B, 0x77, 0x75, 0x69, 0x77, 0x3D, 0x3D, - 0x36, 0x00, 0x00, 0x56, 0x50, 0xE1, 0xED, -} - -var testRollOverScteAdjustment = []byte{ - 0xFC, 0x30, 0x16, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFF, 0xFF, - 0xFF, 0xF1, 0xF1, 0x00, 0x00, 0x1B, 0xD0, 0x87, 0x0B, -} - -func TestPTSAdjustmentWithRollover(t *testing.T) { - target := testRollOverScteAdjustment - scte := CreateSCTE35() - initialPTS := gots.PTS(0x1FFFFF1F1) - adjustmentPTS := gots.PTS(0xF0F) // should be derived by scte35 - finalPTS := initialPTS.Add(adjustmentPTS) // should be 256 (0x100) - scte.SetAdjustPTS(finalPTS) - cmd := CreateTimeSignalCommand() - cmd.SetHasPTS(true) - cmd.SetPTS(initialPTS) - scte.SetCommandInfo(cmd) - generated := scte.UpdateData() - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestDistributorPoStartCreateEncode(t *testing.T) { - target := testScteCreate4 - scte := CreateSCTE35() - scte.SetAdjustPTS(0xB7264) - scte.SetTier(0xFFF) - cmd := CreateTimeSignalCommand() - cmd.SetHasPTS(true) - cmd.SetPTS(0x89544) - scte.SetCommandInfo(cmd) - - descriptor := CreateSegmentationDescriptor() - descriptor.SetEventID(0x2) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(false) - descriptor.SetHasDuration(false) - descriptor.SetIsDeliveryNotRestricted(false) - descriptor.SetIsWebDeliveryAllowed(true) - descriptor.SetHasNoRegionalBlackout(true) - descriptor.SetIsArchiveAllowed(true) - descriptor.SetDeviceRestrictions(RestrictNone) - descriptor.SetUPIDType(SegUPIDADI) - descriptor.SetUPID([]byte("SIGNAL:Y8o0D3zpTxS0LT1ew+wuiw==")) - descriptor.SetTypeID(SegDescDistributorPOStart) - - components := make([]ComponentOffset, 0) - component := CreateComponentOffset() - component.SetComponentTag(0x1) - component.SetPTSOffset(0x2DD200) - components = append(components, component) - component = CreateComponentOffset() - component.SetComponentTag(0x2) - component.SetPTSOffset(0x1E8) - components = append(components, component) - descriptor.SetComponents(components) - - descriptor.SetSegmentNumber(0) - descriptor.SetSegmentsExpected(0) - descriptor.SetSubSegmentNumber(0) - descriptor.SetSubSegmentsExpected(0) - - scte.SetDescriptors([]SegmentationDescriptor{descriptor}) - - generated := append(psi.NewPointerField(0), scte.UpdateData()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestDistributorPoStartDecodeEncode(t *testing.T) { - target := testScteCreate4 - scte, err := NewSCTE35(target) - if err != nil { - t.Error(err.Error()) - } - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestProgramStartDecodeEncode(t *testing.T) { - target := testScteCreate4 - scte, err := NewSCTE35(target) - if err != nil { - t.Error(err.Error()) - } - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestNetworkEndCreateEncode(t *testing.T) { - target := testScteCreate3 - scte := CreateSCTE35() - scte.SetAdjustPTS(0x59CF4) - scte.SetTier(0x0) - cmd := CreateTimeSignalCommand() - cmd.SetHasPTS(true) - cmd.SetPTS(0x2BFD4) - scte.SetCommandInfo(cmd) - - descriptor := CreateSegmentationDescriptor() - descriptor.SetEventID(0x2) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(true) - descriptor.SetHasDuration(true) - descriptor.SetDuration(0xAFF50) - descriptor.SetIsDeliveryNotRestricted(true) - descriptor.SetUPIDType(SegUPIDADI) - descriptor.SetUPID([]byte("TEST")) - descriptor.SetTypeID(SegDescNetworkEnd) - descriptor.SetSegmentNumber(0) - descriptor.SetSegmentsExpected(0) - - scte.SetDescriptors([]SegmentationDescriptor{descriptor}) - - generated := append(psi.NewPointerField(0), scte.UpdateData()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestNetworkEndDecodeEncode(t *testing.T) { - target := testScteCreate3 - scte, err := NewSCTE35(target) - if err != nil { - t.Error(err.Error()) - } - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestUnscheduledEventStartCreateEncode(t *testing.T) { - target := testScteCreate2 - scte := CreateSCTE35() - scte.SetTier(0x0) - cmd := CreateTimeSignalCommand() - cmd.SetHasPTS(true) - cmd.SetPTS(0x2BFD4) - scte.SetCommandInfo(cmd) - scte.SetAdjustPTS(0x59CF4) - - descriptor := CreateSegmentationDescriptor() - descriptor.SetEventID(0x2) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(true) - descriptor.SetHasDuration(true) - descriptor.SetDuration(0xAFF50) - descriptor.SetIsDeliveryNotRestricted(true) - descriptor.SetUPIDType(SegUPIDADI) - descriptor.SetUPID([]byte("TEST")) - descriptor.SetTypeID(SegDescUnscheduledEventStart) - descriptor.SetSegmentNumber(0) - descriptor.SetSegmentsExpected(0) - - scte.SetDescriptors([]SegmentationDescriptor{descriptor}) - - generated := append(psi.NewPointerField(0), scte.UpdateData()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestUnscheduledEventStartDecodeEncode(t *testing.T) { - target := testScteCreate2 - scte, err := NewSCTE35(target) - if err != nil { - t.Error(err.Error()) - } - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestSCPCreateEncode(t *testing.T) { - target := csp - scte := CreateSCTE35() - scte.SetTier(0xFFF) - scte.SetCommandInfo(CreateSpliceNull()) - - descriptor := CreateSegmentationDescriptor() - descriptor.SetEventID(0xC0000000) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(true) - descriptor.SetHasDuration(false) - descriptor.SetIsDeliveryNotRestricted(true) - descriptor.SetUPIDType(SegUPIDURN) - descriptor.SetUPID([]byte("urn:merlin:linear:stream:8987205474424984163")) - descriptor.SetTypeID(SegDescContentIdentification) - descriptor.SetSegmentNumber(0) - descriptor.SetSegmentsExpected(0) - - scte.SetDescriptors([]SegmentationDescriptor{descriptor}) - - generated := append(psi.NewPointerField(0), scte.UpdateData()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestSCPDecodeEncode(t *testing.T) { - target := csp - scte, err := NewSCTE35(target) - if err != nil { - t.Error(err.Error()) - } - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestScteCreateEncode(t *testing.T) { - target := testScteCreate - scte := CreateSCTE35() - scte.SetTier(0xFFF) - cmd := CreateTimeSignalCommand() - scte.SetCommandInfo(cmd) - cmd.SetHasPTS(true) - cmd.SetPTS(0x86DF7550) - scte.SetAdjustPTS(0x86DF7550) - - descriptor := CreateSegmentationDescriptor() - descriptor.SetEventID(0x41424344) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(true) - descriptor.SetHasDuration(false) - descriptor.SetIsDeliveryNotRestricted(false) - descriptor.SetIsWebDeliveryAllowed(false) - descriptor.SetHasNoRegionalBlackout(true) - descriptor.SetIsArchiveAllowed(true) - descriptor.SetDeviceRestrictions(RestrictNone) - descriptor.SetUPIDType(SegUPIDNotUsed) - - descriptor.SetTypeID(0x10) - descriptor.SetSegmentNumber(1) - descriptor.SetSegmentsExpected(1) - - scte.SetDescriptors([]SegmentationDescriptor{descriptor}) - - generated := append(psi.NewPointerField(0), scte.UpdateData()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestScteDecodeEncode(t *testing.T) { - target := testScteCreate - scte, err := NewSCTE35(target) - if err != nil { - t.Error(err.Error()) - } - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestVssCreateEncode(t *testing.T) { - target := vss - scte := CreateSCTE35() - scte.SetTier(0xFFF) - cmd := CreateTimeSignalCommand() - scte.SetCommandInfo(cmd) - cmd.SetHasPTS(true) - cmd.SetPTS(0x00000000) - scte.SetAdjustPTS(0x6D71C7EF) - - descriptors := make([]SegmentationDescriptor, 0, 2) - - descriptor := CreateSegmentationDescriptor() - descriptor.SetEventID(9) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(true) - descriptor.SetHasDuration(false) - descriptor.SetIsDeliveryNotRestricted(false) - descriptor.SetIsWebDeliveryAllowed(true) - descriptor.SetHasNoRegionalBlackout(false) - descriptor.SetIsArchiveAllowed(true) - descriptor.SetDeviceRestrictions(RestrictNone) - descriptor.SetUPIDType(SegUPIDMID) - - mid := make([]UPID, 0, 2) - upid := CreateUPID() - upid.SetUPIDType(SegUPIDADI) - upid.SetUPID([]byte("BLACKOUT:Sq+kY9muQderGNiNtOoN6w==")) - mid = append(mid, upid) - - upid = CreateUPID() - upid.SetUPIDType(SegUPADSINFO) - upid.SetUPID([]byte("comcast:linear:licenserotation")) - mid = append(mid, upid) - descriptor.SetMID(mid) - - descriptor.SetTypeID(0x40) - descriptor.SetSegmentNumber(0) - descriptor.SetSegmentsExpected(0) - descriptors = append(descriptors, descriptor) - - descriptor = CreateSegmentationDescriptor() - descriptor.SetEventID(9) - descriptor.SetIsEventCanceled(false) - descriptor.SetHasProgramSegmentation(true) - descriptor.SetHasDuration(false) - descriptor.SetIsDeliveryNotRestricted(false) - descriptor.SetIsWebDeliveryAllowed(true) - descriptor.SetHasNoRegionalBlackout(false) - descriptor.SetIsArchiveAllowed(true) - descriptor.SetDeviceRestrictions(RestrictNone) - descriptor.SetUPIDType(SegUPIDNotUsed) - descriptor.SetTypeID(0x41) - descriptor.SetSegmentNumber(0) - descriptor.SetSegmentsExpected(0) - descriptors = append(descriptors, descriptor) - - scte.SetDescriptors(descriptors) - - generated := append(psi.NewPointerField(0), scte.UpdateData()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Generated packet data does not match expected data\n Target: %X\nGenerated: %X\n", target, generated) - } -} - -func TestVSSDecodeEncode(t *testing.T) { - target := vss - scte, _ := NewSCTE35(target) - scte.UpdateData() - generated := append(psi.NewPointerField(0), scte.Data()...) - - if !bytes.Equal(target, generated) { - t.Errorf("Original packet data does not match Generated data\n Target: %X\nGenerated: %X\n", target, generated) - } -} diff --git a/v2/scte35/scte35.go b/v2/scte35/scte35.go deleted file mode 100644 index 4c42e0a..0000000 --- a/v2/scte35/scte35.go +++ /dev/null @@ -1,374 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "bytes" - "encoding/binary" - "fmt" - - "github.com/Comcast/gots/v2" - "github.com/Comcast/gots/v2/psi" -) - -// Descriptor tag types and identifiers - only segmentation descriptors are used for now -const ( - segDescTag = 0x02 - segDescID = 0x43554549 -) - -// scte35 is a structure representing a SCTE35 message. -type scte35 struct { - tableHeader psi.TableHeader - protocolVersion uint8 - encryptedPacket bool // not supported - encryptionAlgorithm uint8 // 6 bits - pts gots.PTS // pts is stored adjusted in struct - cwIndex uint8 - tier uint16 // 12 bits - spliceCommandLength uint16 // 12 bits - commandType SpliceCommandType - commandInfo SpliceCommand - descriptors []SegmentationDescriptor - crc32 uint32 - alignmentStuffing uint - - data []byte - - // because there is no support for descriptors other than segmentation descriptors, - // the bytes need to be stored so information is not lost. - otherDescriptorBytes []byte -} - -// NewSCTE35 creates a new SCTE35 signal from the provided byte slice. The byte slice is parsed and relevant info is made available fir the SCTE35 interface. If the message cannot me parsed, an error is returned. -func NewSCTE35(data []byte) (SCTE35, error) { - s := &scte35{} - err := s.parseTable(data) - if err != nil { - return nil, err - } - return s, nil -} - -// parseTable will parse bytes into a scte35 message struct -func (s *scte35) parseTable(data []byte) error { - buf := bytes.NewBuffer(data) - // closure to ignore EOF error from buf.ReadByte(). We've already checked - // the length, we don't need to continually check it after every ReadByte - // call - readByte := func() byte { - b, _ := buf.ReadByte() - return b - } - if buf.Len() < int(uint16(psi.PointerField(data))+psi.PSIHeaderLen+15) { - return gots.ErrInvalidSCTE35Length - } - // read over the pointer field - buf.Next(int(psi.PointerField(data) + 1)) - // read in the TableHeader - var err error - s.tableHeader, err = psi.TableHeaderFromBytes(buf.Next(3)) - if err != nil { - return err - } - if s.tableHeader.TableID == 0xfc { - s.protocolVersion = readByte() - if readByte()&0x80 != 0 { - return gots.ErrSCTE35EncryptionUnsupported - } - - // unread this byte, because it contains the encryptionAlgorithm field - if err := buf.UnreadByte(); err != nil { - return err - } - s.encryptionAlgorithm = (readByte() >> 1) & 0x3F // 0111 1110 - - // unread this byte, because it contains the top bit of our pts offset - err := buf.UnreadByte() - if err != nil { - return err - } - ptsAdjustment := uint40(buf.Next(5)) & 0x01ffffffff - // read cw_index, tier and spliceCommandLength - // spliceCommandLength can be 0xfff(unknown) so it's pretty much useless - s.cwIndex = readByte() - bytes := buf.Next(3) - s.tier = uint16(bytes[0])<<4 | uint16(bytes[1]&0xF0)>>4 - s.spliceCommandLength = uint16(bytes[1]&0x0F)<<8 | uint16(bytes[2]) - s.commandType = SpliceCommandType(readByte()) - switch s.commandType { - case TimeSignal, SpliceInsert: - var cmd SpliceCommand - if s.commandType == TimeSignal { - cmd, err = parseTimeSignal(buf) - } else { - cmd, err = parseSpliceInsert(buf) - } - if err != nil { - return err - } - // add the pts adjustment to get the real value - s.pts = cmd.PTS().Add(ptsAdjustment) - s.commandInfo = cmd - case SpliceNull: - s.commandInfo = &spliceNull{} - default: - return gots.ErrSCTE35UnsupportedSpliceCommand - } - // descriptor_loop_length 2 + CRC 4 - if buf.Len() < 2+int(psi.CrcLen) { - return gots.ErrInvalidSCTE35Length - } - // parse descriptors - descriptorLoopLength := binary.BigEndian.Uint16(buf.Next(2)) - if buf.Len() < int(descriptorLoopLength+psi.CrcLen) { - return gots.ErrInvalidSCTE35Length - } - for bytesRead := uint16(0); bytesRead < descriptorLoopLength; { - descTag := readByte() - descLen := readByte() - // Make sure a bad descriptorLen doesn't kill us - if descriptorLoopLength-bytesRead-2 < uint16(descLen) { - return gots.ErrInvalidSCTE35Length - } - if descTag != segDescTag { - // Not interested in descriptors that are not - // SegmentationDescriptors - // Store their bytes anyways so the data is not lost. - s.otherDescriptorBytes = append(s.otherDescriptorBytes, descTag, descLen) - s.otherDescriptorBytes = append(s.otherDescriptorBytes, buf.Next(int(descLen))...) - } else { - d := &segmentationDescriptor{spliceInfo: s} - err := d.parseDescriptor(buf.Next(int(descLen))) - if err != nil { - return err - } - s.descriptors = append(s.descriptors, d) - } - bytesRead += 2 + uint16(descLen) - } - } else { - return gots.ErrUnknownTableID - } - // Check CRC? - // remove the pointer field and associated data off the top so we only get the - // table data - s.data = data[psi.PointerField(data)+1:] - return nil -} - -// HasPTS returns true if there is a pts time. -func (s *scte35) HasPTS() bool { - return s.commandInfo.HasPTS() -} - -// PTS returns the PTS time of the signal if it exists. Includes adjustment. -func (s *scte35) PTS() gots.PTS { - return s.pts -} - -// Command returns the signal's splice command. -func (s *scte35) Command() SpliceCommandType { - return s.commandType -} - -// CommandInfo returns an object describing fields of the signal's splice -// command structure -func (s *scte35) CommandInfo() SpliceCommand { - return s.commandInfo -} - -// Descriptors returns a slice of the signals SegmentationDescriptors sorted -// by descriptor weight (least important signals first) -func (s *scte35) Descriptors() []SegmentationDescriptor { - return s.descriptors -} - -// Tier returns which authorization tier this message was assigned to. -// The tier value of 0XFFF is the default and will ignored. When using tier values, -// the SCTE35 message must fit entirely into the ts payload without being split up. -func (s *scte35) Tier() uint16 { - return s.tier -} - -// AlignmentStuffing returns how many stuffing bytes will be added to the SCTE35 -// message at the end. -func (s *scte35) AlignmentStuffing() uint { - return s.alignmentStuffing -} - -// Data returns the raw data bytes of the scte signal -func (s *scte35) Data() []byte { - return s.data -} - -func uint40(buf []byte) gots.PTS { - return (gots.PTS(buf[0]&0x1) << 32) | (gots.PTS(buf[1]) << 24) | (gots.PTS(buf[2]) << 16) | (gots.PTS(buf[3]) << 8) | (gots.PTS(buf[4])) -} - -// String returns a string representation of the SCTE35 message. -// String function is for debugging and testing. -func (s *scte35) String() string { - numspaces := 0 - indentString := "" - - indent := func(n int) { - numspaces += n - indentString = "" - for i := 0; i < numspaces; i++ { - indentString += " " - } - } - - indentPrintf := func(format string, a ...interface{}) string { - return fmt.Sprintf(indentString+format, a...) - } - - str := "" - s.UpdateData() - str += indentPrintf("table_id: 0x%X\n", s.tableHeader.TableID) - str += indentPrintf("section_syntax_indicator: %t\n", s.tableHeader.SectionSyntaxIndicator) - str += indentPrintf("private_indicator: %t\n", s.tableHeader.PrivateIndicator) - str += indentPrintf("section_length: %d\n", s.tableHeader.SectionLength) - - str += indentPrintf("protocol_version: 0x%X\n", s.protocolVersion) - str += indentPrintf("encrypted_packet: %t\n", s.encryptedPacket) - str += indentPrintf("encryption_algorithm: 0x%X\n", s.encryptionAlgorithm) - - str += indentPrintf("has_pts: %t\n", s.HasPTS()) - str += indentPrintf("adjusted_pts: %d\n", s.PTS()) - str += indentPrintf("cw_index: 0x%X\n", s.cwIndex) - str += indentPrintf("tier: 0x%X\n", s.tier) - str += indentPrintf("splice_command_type: %s\n", SpliceCommandTypeNames[s.commandType]) - indent(1) - if cmd, ok := s.commandInfo.(SpliceInsertCommand); ok { - str += indentPrintf("splice_event_id: 0x%X\n", cmd.EventID()) - str += indentPrintf("splice_event_cancel_indicator: %t\n", cmd.IsEventCanceled()) - if !cmd.IsEventCanceled() { - str += indentPrintf("out_of_network_indicator: %t\n", cmd.IsOut()) - str += indentPrintf("program_splice_flag: %t\n", cmd.IsProgramSplice()) - str += indentPrintf("duration_flag: %t\n", cmd.HasDuration()) - str += indentPrintf("splice_immediate_flag: %t\n", cmd.SpliceImmediate()) - str += indentPrintf("splice_time_has_pts: %t\n", cmd.HasPTS()) - if cmd.HasPTS() { - str += indentPrintf("splice_time_pts: %d\n", cmd.PTS()) - } - str += indentPrintf("component_count: %d\n", len(cmd.Components())) - for _, comp := range cmd.Components() { - str += indentPrintf("component:\n") - indent(1) - str += indentPrintf("component_tag: 0x%X\n", comp.ComponentTag()) - str += indentPrintf("component_has_pts: %t\n", comp.HasPTS()) - if comp.HasPTS() { - str += indentPrintf("component_pts: %d\n", cmd.PTS()) - } - indent(-1) - - if cmd.HasDuration() { - str += indentPrintf("auto_return: %t\n", cmd.IsAutoReturn()) - str += indentPrintf("duration: %d\n", cmd.Duration()) - } - str += indentPrintf("unique_program_id: %t\n", cmd.UniqueProgramId()) - str += indentPrintf("avail_num: %d\n", cmd.AvailNum()) - str += indentPrintf("avails_expected: %d\n", cmd.AvailsExpected()) - } - } - } - - if cmd, ok := s.commandInfo.(TimeSignalCommand); ok { - str += indentPrintf("time_specified_flag: %t\n", cmd.HasPTS()) - if cmd.HasPTS() { - str += indentPrintf("pts_time: %d\n", cmd.PTS()) - } - } - indent(-1) - - str += indentPrintf("descriptor_count: %d\n", len(s.Descriptors())) - for _, desc := range s.descriptors { - str += indentPrintf("descriptor:\n") - - indent(1) - if desc.IsIn() { - indentPrintf("<--- IN Segmentation Descriptor") - } - if desc.IsOut() { - indentPrintf("---> OUT Segmentation Descriptor") - } - str += indentPrintf("segmentation_event_id: 0x%X\n", desc.EventID()) - str += indentPrintf("segmentation_event_cancel_indicator: %t\n", desc.IsEventCanceled()) - if !desc.IsEventCanceled() { - str += indentPrintf("program_segmentation_flag: %t\n", desc.HasProgramSegmentation()) - str += indentPrintf("segmentation_duration_flag: %t\n", desc.HasDuration()) - str += indentPrintf("delivery_not_restricted_flag: %t\n", desc.IsDeliveryNotRestricted()) - if !desc.IsDeliveryNotRestricted() { - str += indentPrintf("web_delivery_allowed_flag: %t\n", desc.IsWebDeliveryAllowed()) - str += indentPrintf("no_regional_blackout_flag: %t\n", desc.HasNoRegionalBlackout()) - str += indentPrintf("archive_allowed_flag: %t\n", desc.IsArchiveAllowed()) - str += indentPrintf("device_restrictions: %s\n", DeviceRestrictionsNames[desc.DeviceRestrictions()]) - } - if !desc.HasProgramSegmentation() { - str += indentPrintf("component_count: %d\n", len(desc.Components())) - for _, comp := range desc.Components() { - str += indentPrintf("component:\n") - indent(1) - str += indentPrintf("component_tag: 0x%X\n", comp.ComponentTag()) - str += indentPrintf("pts_offset: %d\n", comp.PTSOffset()) - indent(-1) - } - } - if desc.HasDuration() { - str += indentPrintf("segmentation_duration: %d\n", desc.Duration()) - } - str += indentPrintf("segmentation_upid_type: %s\n", SegUPIDTypeNames[desc.UPIDType()]) - if desc.UPIDType() != SegUPIDMID { - str += indentPrintf("segmentation_upid: %s\n", string(desc.UPID())) - } else { - str += indentPrintf("segmentation_mid: \n") - indent(1) - for _, upid := range desc.MID() { - str += indentPrintf("upid:\n") - indent(1) - str += indentPrintf("segmentation_mid_upid_type: %s\n", SegUPIDTypeNames[upid.UPIDType()]) - str += indentPrintf("segmentation_mid_upid: %s\n", string(upid.UPID())) - indent(-1) - } - indent(-1) - } - str += indentPrintf("segmentation_type_id: %s\n", SegDescTypeNames[desc.TypeID()]) - str += indentPrintf("segment_num: 0x%X\n", desc.SegmentNumber()) - str += indentPrintf("segments_expected: 0x%X\n", desc.SegmentsExpected()) - if desc.HasSubSegments() { - str += indentPrintf("sub_segment_num: 0x%X\n", desc.SubSegmentNumber()) - str += indentPrintf("sub_segments_expected: 0x%X\n", desc.SubSegmentsExpected()) - } - } - indent(-1) - } - - str += indentPrintf("alignment_stuffing_byte_count: %d\n", s.alignmentStuffing) - str += indentPrintf("CRC_32: 0x%X", s.data[len(s.data)-4:]) - - return str -} diff --git a/v2/scte35/scte35_test.go b/v2/scte35/scte35_test.go deleted file mode 100644 index 6b51abf..0000000 --- a/v2/scte35/scte35_test.go +++ /dev/null @@ -1,410 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package scte35 - -import ( - "bytes" - "encoding/base64" - "io" - "strings" - "testing" - - "github.com/Comcast/gots/v2" -) - -var testScte = []byte{ - 0x00, 0xfc, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x05, 0x06, 0xfe, - 0x86, 0xdf, 0x75, 0x50, 0x00, 0x11, 0x02, 0x0f, 0x43, 0x55, 0x45, 0x49, 0x41, 0x42, 0x43, 0x44, - 0x7f, 0x8f, 0x00, 0x00, 0x10, 0x01, 0x01, 0x3a, 0x6d, 0xda, 0xee, -} - -// This has a program segmentation flag that is false, caused bugs -// elsewhere -var testScte2 = []byte{ - 0x00, 0xfc, 0x00, 0x53, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0xff, 0xf0, 0x05, 0x06, 0xfe, - 0x00, 0x08, 0x95, 0x44, 0x00, 0x3d, 0x02, 0x3b, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0x1f, 0x02, 0x01, 0xfe, 0x00, 0x2d, 0xd2, 0x00, 0x02, 0xfe, 0x00, 0x00, 0x01, 0xe8, 0x09, - 0x1f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x4c, 0x3a, 0x59, 0x38, 0x6f, 0x30, 0x44, 0x33, 0x7a, 0x70, - 0x54, 0x78, 0x53, 0x30, 0x4c, 0x54, 0x31, 0x65, 0x77, 0x2b, 0x77, 0x75, 0x69, 0x77, 0x3d, 0x3d, - 0x36, 0x00, 0x00, 0xe0, 0xfa, 0x93, 0xc1, -} - -var testScte3 = []byte{ - 0x00, 0xfc, 0x30, 0x55, 0x00, 0x00, 0x00, 0x02, 0xd5, 0xa0, 0x00, 0xff, 0xf0, 0x05, 0x06, 0xfe, - 0x00, 0x04, 0x2b, 0x79, 0x00, 0x3f, 0x02, 0x1b, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0x87, 0x09, 0x0c, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x4c, 0x3a, 0x33, 0x2e, 0x30, 0x35, 0x30, - 0x35, 0x01, 0x01, 0x02, 0x20, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0x00, - 0x00, 0x23, 0x13, 0xac, 0x09, 0x0c, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x4c, 0x3a, 0x33, 0x2e, 0x30, - 0x35, 0x30, 0x34, 0x01, 0x01, 0x22, 0x04, 0xf5, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -} - -var testVss = []byte{ - 0x00, 0xfc, 0x30, 0x7b, 0x00, 0x00, 0x6d, 0x71, 0xc7, 0xef, 0x00, 0xff, 0xf0, 0x05, 0x06, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x02, 0x52, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x09, - 0x7f, 0x97, 0x0d, 0x43, 0x09, 0x21, 0x42, 0x4c, 0x41, 0x43, 0x4b, 0x4f, 0x55, 0x54, 0x3a, 0x53, - 0x71, 0x2b, 0x6b, 0x59, 0x39, 0x6d, 0x75, 0x51, 0x64, 0x65, 0x72, 0x47, 0x4e, 0x69, 0x4e, 0x74, - 0x4f, 0x6f, 0x4e, 0x36, 0x77, 0x3d, 0x3d, 0x0e, 0x1e, 0x63, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, - 0x3a, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x72, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x40, 0x00, 0x00, 0x02, 0x0f, 0x43, 0x55, 0x45, 0x49, - 0x00, 0x00, 0x00, 0x09, 0x7f, 0x97, 0x00, 0x00, 0x41, 0x00, 0x00, 0x7a, 0xd7, 0xa4, 0x65, -} - -func TestSpliceInsertSignal(t *testing.T) { - base64Bytes, _ := base64.StdEncoding.DecodeString("APwwLwAAz6l5ggD///8FYgAgAn/v/1jt40T+AHuYoAM1AAAACgAIQ1VFSQA4MjFRxjDp") - - s1, err := NewSCTE35(base64Bytes) - if err != nil { - t.Error(err) - t.FailNow() - } - - if s1.Command() != SpliceInsert { - t.Error("Expected parsed command to be a SpliceInsert, it was not") - } - - s := s1.CommandInfo().(SpliceInsertCommand) - - if s.IsEventCanceled() != false { - t.Errorf("Unexpected value for IsEventCanceled: %v", s.IsEventCanceled()) - } - if s.IsOut() != true { - t.Errorf("Unexpected value for IsOut: %v", s.IsOut()) - } - if s.EventID() != 1644175362 { - t.Errorf("Unexpected value for EventID: %v", s.EventID()) - } - if s.HasDuration() != true { - t.Errorf("Unexpected value for HasDuration: %v", s.HasDuration()) - } - if s.Duration() != 8100000 { - t.Errorf("Unexpected value for Duration: %v", s.Duration()) - } - if s.IsAutoReturn() != true { - t.Errorf("Unexpected value for IsAutoReturn: %v", s.IsAutoReturn()) - } - if s.UniqueProgramId() != 821 { - t.Errorf("Unexpected value for UniqueProgramId: %v", s.UniqueProgramId()) - } - if s.AvailNum() != 0 { - t.Errorf("Unexpected value for AvailNum: %v", s.AvailNum()) - } - if s.AvailsExpected() != 0 { - t.Errorf("Unexpected value for AvailsExpected: %v", s.AvailsExpected()) - } -} - -func TestBasicSignal(t *testing.T) { - s, err := NewSCTE35(testScte) - if err != nil { - t.Error(err) - t.FailNow() - } - if s.Command() != TimeSignal { - t.Errorf("Invalid command found, expecting TimeSignal(6), got: %v", s.Command()) - } - if !s.HasPTS() { - t.Error("Expecting PTS, but none found") - } else if s.PTS() != 2262791504 { - t.Errorf("Expecting PTS value of 2262791504, but found %v instead", s.PTS()) - } - descs := s.Descriptors() - if descs == nil { - t.Error("Expecting descriptors in signal, but none found") - } else if len(descs) != 1 { - t.Errorf("Only expected one segmentation descriptor, but found %v instead", len(descs)) - } else { - d := descs[0] - if d.TypeID() != SegDescProgramStart { - t.Errorf("Expecting seg descriptor type ProgramStart(0x10), got %x instead", d.TypeID()) - } else if !d.IsOut() { - t.Error("SegDescProgramStart is out, but IsOut() returned false") - } else if d.IsIn() { - t.Error("SegDescProgramStart is out, but IsIn() return true") - } - upid := d.UPID() - if upid == nil { - t.Error("upid not found in descriptor") - } else if len(upid) != 0 { - t.Error("non-zero len upid found, indicating error") - } - } - data := s.Data() - if data[0] != 0xfc { - t.Error("First byte of data is not table id") - } -} - -// splice_null commands are used for CSP. -func TestParseSpliceNull(t *testing.T) { - base64Bytes, _ := base64.StdEncoding.DecodeString("APwwNQAAAAAAAAD/8AEAACQCIkNVRUnAAAAAf78BEzU5MzkwMjY1NjUxNzc3OTIxNjMBAQHrr2Ob") - - s, err := NewSCTE35(base64Bytes) - if err != nil { - t.Error(err) - t.FailNow() - } - - if s.Command() != SpliceNull { - t.Errorf("Invalid command found, expecting SpliceNull(0), got %v", s.Command()) - } - descs := s.Descriptors() - if descs == nil { - t.Error("Expecting descriptors in signal, but none found") - } else if len(descs) != 1 { - t.Errorf("Only expected one segmentation descriptor, but found %v instead", len(descs)) - } else { - d := descs[0] - if d.TypeID() != SegDescContentIdentification { - t.Errorf("Expecting seg descriptor type SegDescContentIdentification(0x1), got %x instead", d.TypeID()) - } - upid := d.UPID() - if upid == nil { - t.Error("csp upid not found in descriptor") - } else { - if d.UPIDType() != SegUPIDUserDefined { - t.Errorf("Expected upid type SegUPIDUserDefined(1), got %v instead", d.UPIDType()) - } - buf := bytes.NewBuffer(upid) - upidStr, err := buf.ReadString(0) - if err != io.EOF { - t.Error(err) - } else if strings.Compare(upidStr, "5939026565177792163") != 0 { - t.Errorf("Invalid UPID found, expected 5939026565177792163, got %s", upidStr) - } - } - } -} - -func TestSCTEExpanded(t *testing.T) { - s1, err1 := NewSCTE35(testScte) - if err1 != nil { - t.Error(err1) - t.FailNow() - } - s2, err2 := NewSCTE35(testScte2) - if err2 != nil { - t.Error(err2) - t.FailNow() - } - if s2.Command() != TimeSignal { - t.Errorf("Invalid command found, expecting TimeSignal(6), got: %v", s2.Command()) - } - if !s2.HasPTS() { - t.Error("Expecting PTS, but none found") - } else if s2.PTS() != 750180 { - t.Errorf("Expecting PTS value of 750180 %v instead", s2.PTS()) - } - descs2 := s2.Descriptors() - var d2, d1 SegmentationDescriptor - if descs2 == nil { - t.Error("Expecting descriptors in signal, but none found") - } else if len(descs2) != 1 { - t.Errorf("Only expected one segmentation descriptor, but found %v instead", len(descs2)) - } else { - d2 := descs2[0] - if d2.TypeID() != SegDescDistributorPOStart { - t.Errorf("Expecting seg descriptor type SegDescDistributorPoStart(0x36), got %x instead", d2.TypeID()) - } else if !d2.IsOut() { - t.Error("SegDescDistributorPoStart is out, but IsOut() returned false") - } else if d2.IsIn() { - t.Error("SegDescDistributorPoStart is out, but IsIn() return true") - } - upid := d2.UPID() - if upid == nil { - t.Error("upid not found in descriptor") - } else { - if d2.UPIDType() != SegUPIDADI { - t.Errorf("Expected upid type SegUPIDADI(9), got %v instead", d2.UPIDType()) - } - buf := bytes.NewBuffer(upid) - upidStr, err := buf.ReadString(0) - if err != io.EOF { - t.Error(err) - } else if strings.Compare(upidStr, "SIGNAL:Y8o0D3zpTxS0LT1ew+wuiw==") != 0 { - t.Errorf("Invalid UPID found, expected SIGNAL:Y8o0D3zpTxS0LT1ew+wuiw==, got %s", upidStr) - } - } - } - descs1 := s1.Descriptors() - if descs1 == nil { - t.Error("expecting descriptors in signal, but none found") - } else if len(descs1) != 1 { - t.Errorf("Only expected one segmentation descriptor, but found %v instead", len(descs1)) - } else { - d1 = descs1[0] - } - if d1 != nil && d2 != nil { - if d1.CanClose(d2) { - t.Errorf("Segmentation type %v shouldn't be able to close %v, but CanClose returned true", d1.UPIDType(), d2.UPIDType()) - } - if d2.CanClose(d1) { - t.Errorf("Segmentation type %v shouldn't be able to close %v, but CanClose returned true", d2.UPIDType(), d1.UPIDType()) - } - } -} - -func TestSCTEMultipleDescriptors(t *testing.T) { - s, err := NewSCTE35(testScte3) - if err != nil { - t.Error(err) - t.FailNow() - } - if s.Command() != TimeSignal { - t.Errorf("Invalid command found, expecting TimeSignal(6), got: %v", s.Command()) - } - if !s.HasPTS() { - t.Error("Expecting PTS, but none found") - } - descs := s.Descriptors() - if descs == nil { - t.Error("expecting descriptors in signal, but none found") - } else if len(descs) != 2 { - t.Error("expecting two descriptors in signal, but found", len(descs)) - t.FailNow() - } - if descs[0].TypeID() != SegDescProviderPOEnd { - t.Error("Invalid seg type found, expected SegDescPlacementOpportunityEnd(0x35), found ", descs[0].TypeID()) - } - if descs[0].IsOut() { - t.Error("descriptor type is an in, but IsOut() returned true") - } - if !descs[0].IsIn() { - t.Error("descriptor type is an in, but IsIn() returned false") - } - if descs[1].TypeID() != SegDescProviderPOStart { - t.Error("Invalid seg type found, expected SegDescPlacementOpportunityStart(0x34), but found", descs[1].TypeID()) - } - if !descs[0].CanClose(descs[1]) { - t.Error("descs[0] should be able to close [1], but CanClose reports false") - } - if descs[1].SCTE35() == nil { - t.Error("descs[1] does not return scte obj") - } - if descs[0].SCTE35() != descs[1].SCTE35() { - t.Error("SCTE obj of both descs is not the same") - } -} - -func TestParseSegmentationDescriptor_EventCancelled(t *testing.T) { - base64Bytes, _ := base64.StdEncoding.DecodeString("APwwKwAATJCc6v//8AUG/vafrY0AFQIJQ1VFSQAAAAD/AAhDVUVJAAAAAEBlk0M=") - - s, err := NewSCTE35(base64Bytes) - if err != nil { - t.Fatal(err) - } - - if s.Command() != TimeSignal { - t.Errorf("Invalid command found, expecting TimeSignal(6), got: %v", s.Command()) - } - - if !s.HasPTS() { - t.Error("Expecting PTS, but none found") - } - - if s.PTS() != gots.PTS(5422205559) { - t.Errorf("Expected PTS 5422205559, got: %v", s.PTS()) - } - - if len(s.Descriptors()) != 1 { - t.Errorf("Expected 1 segmentation descriptor, got %d", s.Descriptors()) - } - - segmentationDescriptor := s.Descriptors()[0] - if segmentationDescriptor.TypeID() != SegDescNotIndicated { - t.Errorf("Expected segmentationtype Not Indicated, got %d", segmentationDescriptor.TypeID()) - } - - if !segmentationDescriptor.IsEventCanceled() { - t.Error("Expected event to be canceled.") - } -} - -func TestSCTEVSS(t *testing.T) { - s, err := NewSCTE35(testVss) - if err != nil { - t.Fatal(err) - } - if !s.HasPTS() { - t.Error("Expecting PTS, but none found") - } - descs := s.Descriptors() - if descs == nil { - t.Error("expecting descriptors in signal, but none found") - } else if len(descs) != 2 { - t.Error("expecting two descriptors in signal, but found", len(descs)) - t.FailNow() - } - if descs[0].TypeID() != SegDescUnscheduledEventStart { - t.Error("Invalid seg type found, expected SegDescUnscheduledEventStart(0x40), found ", descs[0].TypeID()) - } - if descs[0].IsIn() { - t.Error("descriptor type(0x40) is an out, but IsIn() returned true") - } - if !descs[0].IsOut() { - t.Error("descriptor type(0x40) is an out but IsOut() returned false") - } - if descs[1].TypeID() != SegDescUnscheduledEventEnd { - t.Error("Invalid seg type found, expected SegDescUnscheduledEventEnd(0x41), but found", descs[1].TypeID()) - } - if descs[1].IsOut() { - t.Error("descriptor type(0x41) is an in, but IsOut() returned true") - } - if !descs[1].IsIn() { - t.Error("descriptor type(0x41) is an out, but IsIn() returned false") - } - if !descs[1].CanClose(descs[0]) { - t.Error("descs[1] should be able to close desc[0], but CanClose reports false") - } - if descs[0].SCTE35() != descs[1].SCTE35() { - t.Error("SCTE obj of both descs is not the same") - } -} - -func TestParseSegmentationDescriptor_Segments(t *testing.T) { - base64Bytes, _ := base64.StdEncoding.DecodeString("APwwPgAAEH2lcP//8AUG/iuc2acAKAIcQ1VFSUgAAEd/zwAA+Hm0CAgAAAAAJrAlpjQCAAAIQ1VFSQAAAAAOP8i1") - - s, err := NewSCTE35(base64Bytes) - if err != nil { - t.Fatal(err) - } - if s.Command() != TimeSignal { - t.Errorf("Invalid command found, expecting TimeSignal(6), got: %v", s.Command()) - } - if len(s.Descriptors()) != 1 { - t.Errorf("Expected 1 segmentation descriptor, got %d", s.Descriptors()) - } - want := uint8(2) // this particular signal should have segment_num=2 - got := s.Descriptors()[0].SegmentNum() - if got == 0 { - t.Error("segment_num not found in descriptor") - } else if want != got { - t.Errorf("want segment_num %d, got %d", want, got) - } -} diff --git a/v2/scte35/segmentationdescriptor.go b/v2/scte35/segmentationdescriptor.go deleted file mode 100644 index 2a85c6f..0000000 --- a/v2/scte35/segmentationdescriptor.go +++ /dev/null @@ -1,561 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "bytes" - "encoding/binary" - "strings" - - "github.com/Comcast/gots/v2" -) - -// upidSt is the struct used for creating a Multiple UPID (MID) -type upidSt struct { - upidType SegUPIDType - upidLen int - upid []byte -} - -// CreateUPID will create a default UPID. (SegUPIDNotUsed) -func CreateUPID() UPID { - return &upidSt{} -} - -// UPIDType returns the type of UPID stored -func (u *upidSt) UPIDType() SegUPIDType { - return u.upidType -} - -// UPID returns the actual UPID -func (u *upidSt) UPID() []byte { - return u.upid -} - -// componentOffset is a structure in SegmentationDescriptor. -type componentOffset struct { - componentTag byte - ptsOffset gots.PTS -} - -// CreateComponentOffset will create a ComponentOffset structure that -// belongs in a SegmentationDescriptor. -func CreateComponentOffset() ComponentOffset { - return &componentOffset{} -} - -// ComponentTag returns the tag of the component. -func (c *componentOffset) ComponentTag() byte { - return c.componentTag -} - -// PTSOffset returns the PTS offset of the component. -func (c *componentOffset) PTSOffset() gots.PTS { - return c.ptsOffset -} - -// data returns the raw data bytes of the componentOffset -func (c *componentOffset) data() []byte { - bytes := make([]byte, 6) - bytes[0] = c.componentTag - bytes[1] = 0xFE - bytes[1] |= byte(c.ptsOffset>>32) & 0x01 // 0000 0001 - bytes[2] = byte(c.ptsOffset >> 24) // 1111 1111 - bytes[3] = byte(c.ptsOffset >> 16) // 1111 1111 - bytes[4] = byte(c.ptsOffset >> 8) // 1111 1111 - bytes[5] = byte(c.ptsOffset) // 1111 1111 - return bytes -} - -// componentFromBytes will create a componentOffset from a byte slice. -// length of byte slice must be 5 or larger. -func componentFromBytes(bytes []byte) componentOffset { - c := componentOffset{} - c.componentTag = bytes[0] - pts := (uint64(bytes[1]) << 32 & 0x01) | (uint64(bytes[2]) << 24) | - (uint64(bytes[3]) << 16) | (uint64(bytes[4]) << 8) | uint64(bytes[5]) - c.ptsOffset = gots.PTS(pts) - return c -} - -// segmentationDescriptor is a strurture representing a segmentation descriptor in SCTE35 -type segmentationDescriptor struct { - // common fields we care about for sorting/identifying, but is not necessarily needed for users of this lib - typeID SegDescType - eventID uint32 - hasDuration bool - duration gots.PTS - upidType SegUPIDType - upid []byte - mid []upidSt //A MID can contains `n` UPID uids in it. - segNum uint8 - segsExpected uint8 - subSegNum uint8 - subSegsExpected uint8 - spliceInfo SCTE35 - eventCancelIndicator bool - deliveryNotRestricted bool - hasSubSegments bool - - programSegmentationFlag bool - webDeliveryAllowedFlag bool - noRegionalBlackoutFlag bool - archiveAllowedFlag bool - deviceRestrictions DeviceRestrictions - - components []componentOffset -} - -type segCloseType uint8 - -// conditions for closing specific descriptor types -const ( - segCloseNormal segCloseType = iota - segCloseNoBreakaway - segCloseEventID - segCloseBreakaway - segCloseDiffPTS - segCloseNotNested - segCloseEventIDNotNested - segCloseUnconditional -) - -var segCloseRules map[SegDescType]map[SegDescType]segCloseType - -// initialize the SegCloseRules map -func init() { - segCloseRules = map[SegDescType]map[SegDescType]segCloseType{ - 0x10: {0x10: segCloseNoBreakaway, 0x14: segCloseNormal, 0x17: segCloseNoBreakaway, 0x19: segCloseNoBreakaway, 0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x40: segCloseNormal, 0x42: segCloseNormal, 0x44: segCloseNormal}, - 0x11: {0x10: segCloseEventID, 0x14: segCloseEventID, 0x17: segCloseEventID, 0x19: segCloseEventID, 0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x40: segCloseNormal, 0x42: segCloseNormal, 0x44: segCloseNormal}, - 0x12: {0x10: segCloseEventID, 0x14: segCloseEventID, 0x17: segCloseEventID, 0x19: segCloseEventID, 0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal}, - 0x13: {0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal}, - 0x14: {0x10: segCloseBreakaway, 0x17: segCloseBreakaway, 0x19: segCloseBreakaway, 0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal}, - 0x19: {0x10: segCloseNoBreakaway, 0x14: segCloseNormal, 0x17: segCloseNoBreakaway, 0x19: segCloseNoBreakaway, 0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal}, - 0x20: {0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal}, - 0x21: {0x20: segCloseEventID, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal}, - 0x22: {0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x23: {0x22: segCloseEventID, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x24: {0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x25: {0x24: segCloseEventID, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x26: {0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x27: {0x26: segCloseEventID, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x30: {0x30: segCloseNormal, 0x32: segCloseNormal}, - 0x31: {0x30: segCloseEventID}, - 0x32: {0x30: segCloseNormal, 0x32: segCloseNormal}, - 0x33: {0x32: segCloseEventID}, - 0x34: {0x30: segCloseDiffPTS, 0x3C: segCloseDiffPTS, 0x44: segCloseDiffPTS}, - 0x35: {0x30: segCloseNormal, 0x34: segCloseEventIDNotNested, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x36: {0x30: segCloseDiffPTS, 0x3C: segCloseDiffPTS, 0x44: segCloseDiffPTS}, - 0x37: {0x30: segCloseNormal, 0x36: segCloseEventIDNotNested, 0x3C: segCloseNormal, 0x44: segCloseNormal}, - 0x3C: {0x30: segCloseNormal, 0x3C: segCloseNormal}, - 0x3D: {0x3C: segCloseEventID}, - 0x40: {0x40: segCloseNormal}, - 0x41: {0x40: segCloseEventID}, - 0x42: {0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x42: segCloseNormal, 0x44: segCloseNormal}, - 0x43: {0x20: segCloseNormal, 0x22: segCloseNormal, 0x24: segCloseNormal, 0x26: segCloseNormal, 0x30: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x3C: segCloseNormal, 0x42: segCloseEventID, 0x44: segCloseNormal}, - 0x44: {0x30: segCloseDiffPTS, 0x3C: segCloseDiffPTS, 0x44: segCloseNormal}, - 0x45: {0x30: segCloseNormal, 0x3C: segCloseNormal, 0x44: segCloseEventID}, - 0x50: {0x10: segCloseNormal, 0x14: segCloseNormal, 0x17: segCloseNormal, 0x19: segCloseNormal, 0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x40: segCloseUnconditional, 0x50: segCloseNormal}, - 0x51: {0x10: segCloseNormal, 0x14: segCloseNormal, 0x17: segCloseNormal, 0x19: segCloseNormal, 0x20: segCloseNormal, 0x30: segCloseNormal, 0x32: segCloseNormal, 0x34: segCloseNormal, 0x36: segCloseNormal, 0x40: segCloseUnconditional, 0x50: segCloseEventID}, - } - // add program breakaway rules to the close map. Only ProgramResumption, - // Unscheduled Event and Network signals can exit breakaway. - // Program resumption should not close ProgramBreakaway as this logic will be - // handled in state. We want processing to stop so that it won't close - // events after the breakaway - segCloseRules[SegDescUnscheduledEventStart][0x13] = segCloseNormal - segCloseRules[SegDescUnscheduledEventEnd][0x13] = segCloseNormal - segCloseRules[SegDescNetworkStart][0x13] = segCloseNormal - segCloseRules[SegDescNetworkEnd][0x13] = segCloseNormal -} - -// parseDescriptor will parse a slice of bytes and store the information in a -// parseDescriptor, if possible. If not it will return an error. -func (d *segmentationDescriptor) parseDescriptor(data []byte) error { - buf := bytes.NewBuffer(data) - // closure to ignore EOF error from buf.ReadByte(). We've already checked - // the length, we don't need to continually check it after every ReadByte - // call - readByte := func() byte { - b, _ := buf.ReadByte() - return b - } - if binary.BigEndian.Uint32(buf.Next(4)) != segDescID { - return gots.ErrSCTE35InvalidDescriptorID - } - d.eventID = binary.BigEndian.Uint32(buf.Next(4)) - d.eventCancelIndicator = readByte()&0x80 != 0 - if !d.eventCancelIndicator { - flags := readByte() - // 3rd bit in the byte - // if delivery_not_restricted = 0 -> deliveryNotRestricted = false - d.deliveryNotRestricted = flags&0x20 != 0 - d.hasDuration = flags&0x40 != 0 - d.programSegmentationFlag = flags&0x80 != 0 - if !d.deliveryNotRestricted { - d.webDeliveryAllowedFlag = flags&0x10 != 0 - d.noRegionalBlackoutFlag = flags&0x08 != 0 - d.archiveAllowedFlag = flags&0x04 != 0 - d.deviceRestrictions = DeviceRestrictions(flags & 0x03) - } - if !d.programSegmentationFlag { - // read component info - ct := readByte() - if int(ct)*6 > buf.Len()-5 { - return gots.ErrInvalidSCTE35Length - } - for ; ct > 0; ct-- { - d.components = append(d.components, componentFromBytes(buf.Next(6))) - } - } - if d.hasDuration { - if buf.Len() < 10 { - return gots.ErrInvalidSCTE35Length - } - d.duration = uint40(buf.Next(5)) - } - // Upid unneeded now... - d.upidType = SegUPIDType(readByte()) - segUpidLen := int(readByte()) - d.mid = []upidSt{} - // This is a Multiple PID, consisting of `n` UPID's - if d.upidType == SegUPIDMID { - // SCTE35 signal will either have an UPID or a MID - // When we have a MID, UPID value in struct will be 0. - d.upid = []byte{} - // Iterate over the whole MID len(segUpidLen) to get all `n` UPIDs - // segUpidLen is in bytes. - for segUpidLen != 0 { - UpidElem := upidSt{} - UpidElem.upidType = SegUPIDType(readByte()) - segUpidLen -= 1 - UpidElem.upidLen = int(readByte()) - segUpidLen -= 1 - UpidElem.upid = buf.Next(UpidElem.upidLen) - segUpidLen -= UpidElem.upidLen - d.mid = append(d.mid, UpidElem) - } - } else { - // This is a UPID, not a MID - // MID should be 0 as SCTE35 can either have a UPID or a MID - if buf.Len() < segUpidLen+3 { - return gots.ErrInvalidSCTE35Length - } - d.upid = buf.Next(segUpidLen) - } - d.typeID = SegDescType(readByte()) - d.segNum = readByte() - d.segsExpected = readByte() - - // Backwards compatible support for the 2016 spec - if buf.Len() > 0 && (d.typeID == 0x34 || d.typeID == 0x36) { - d.subSegNum = readByte() - d.subSegsExpected = readByte() - d.hasSubSegments = true - } - } - return nil -} - -// SCTE35 returns the SCTE35 signal this segmentation descriptor was found in. -func (d *segmentationDescriptor) SCTE35() SCTE35 { - return d.spliceInfo -} - -// EventID returns the event id -func (d *segmentationDescriptor) EventID() uint32 { - return d.eventID -} - -// TypeID returns the segmentation type for descriptor -func (d *segmentationDescriptor) TypeID() SegDescType { - return d.typeID -} - -// SetIsEventCanceled sets the the event cancel indicator -func (d *segmentationDescriptor) IsEventCanceled() bool { - return d.eventCancelIndicator -} - -// IsOut returns true if a signal is an out -func (d *segmentationDescriptor) IsOut() bool { - switch d.TypeID() { - case SegDescProgramStart, - SegDescProgramResumption, - SegDescProgramOverlapStart, - SegDescProgramStartInProgress, - SegDescChapterStart, - SegDescBreakStart, - SegDescProviderAdvertisementStart, - SegDescDistributorAdvertisementStart, - SegDescProviderPOStart, - SegDescDistributorPOStart, - SegDescUnscheduledEventStart, - SegDescProviderAdBlockStart, - SegDescNetworkStart: - return true - default: - return false - } -} - -// IsIn returns true if a signal is an in -func (d *segmentationDescriptor) IsIn() bool { - switch d.TypeID() { - case SegDescProgramEnd, - SegDescProgramEarlyTermination, - SegDescProgramBreakaway, - SegDescProgramRunoverPlanned, - SegDescProgramRunoverUnplanned, - SegDescProgramBlackoutOverride, - SegDescChapterEnd, - SegDescBreakEnd, - SegDescProviderAdvertisementEnd, - SegDescDistributorAdvertisementEnd, - SegDescProviderPOEnd, - SegDescDistributorPOEnd, - SegDescUnscheduledEventEnd, - SegDescProviderAdBlockEnd, - SegDescNetworkEnd: - return true - default: - return false - } -} - -// HasDuration returns true if there is a duration associated with the descriptor -func (d *segmentationDescriptor) HasDuration() bool { - return d.hasDuration -} - -// Duration returns the duration of the descriptor, 40 bit unsigned integer. -func (d *segmentationDescriptor) Duration() gots.PTS { - return d.duration -} - -// UPIDType returns the type of the upid -func (d *segmentationDescriptor) UPIDType() SegUPIDType { - return d.upidType -} - -// UPID returns the upid of the descriptor, if the UPIDType is not SegUPIDMID -func (d *segmentationDescriptor) UPID() []byte { - // Check if this data should exist - if d.upidType == SegUPIDMID { - return []byte{} - } - return d.upid -} - -// StreamSwitchSignalId returns the signalID of streamswitch signal if -// present in the descriptor -func (d *segmentationDescriptor) StreamSwitchSignalId() (string, error) { - var signalId string - var err error - // The VSS SignalId is present in the MID of len 2. - // SignalId is the UPID value in the MID which has - // delivery_not_restricted flag = 0 and - // contains "BLACKOUT" at UpidType of 0x09 and - // comcast:linear:licenserotation at 0x0E - if len(d.mid) == 2 && - !d.deliveryNotRestricted && - (d.mid[0].upidType == SegUPIDADI) && - (strings.Contains(string(d.mid[0].upid), "BLACKOUT")) && - (d.mid[1].upidType == SegUPADSINFO) && - (strings.Contains(string(d.mid[1].upid), "comcast:linear:licenserotation")) { - signalId = strings.TrimPrefix(string(d.mid[0].upid), "BLACKOUT:") - } else { - err = gots.ErrVSSSignalIdNotFound - } - return signalId, err -} - -// SegmentNum is deprecated, use SegmentNumber instead. -// SegmentNum returns the segment_num descriptor field. -func (d *segmentationDescriptor) SegmentNum() uint8 { - return d.segNum -} - -// CanClose returns true if this descriptor can close the passed in descriptor -func (d *segmentationDescriptor) CanClose(out SegmentationDescriptor) bool { - inRules, ok := segCloseRules[d.TypeID()] - // No rules associated with this signal means it can't close anything - if !ok { - return false - } - closeType, ok := inRules[out.TypeID()] - // out signal type not found in rule set means d can't close out - if !ok { - return false - } - switch closeType { - case segCloseNormal, segCloseUnconditional: - return true - // These close rules around what can/cannot go past a ProgramBreakaway. - // Since we will handle program breakaway logic in State, we leave these as - // automatic trues - case segCloseBreakaway, segCloseNoBreakaway: - return true - case segCloseEventID: - if d.EventID() == out.EventID() { - return true - } - case segCloseDiffPTS: - if d.SCTE35().PTS() != out.SCTE35().PTS() { - return true - } - case segCloseEventIDNotNested: - // this should also consider segnum == segexpected for IN signals closing an out signal. - if d.IsIn() && d.EventID() == out.EventID() && d.SegmentNumber() == d.SegmentsExpected() { - return true - } - case segCloseNotNested: // only applies to 0x34 and 0x36 with subsegments. - if d.HasSubSegments() { - if d.SubSegmentNumber() == d.SubSegmentsExpected() { - return true - } else { - return false - } - } - return true - } - return false -} - -// Equal determines equality for two segmentation descriptors -// Equality in this sense means that two "in" events are duplicates -// A lot of debate went in to what actually constitutes a "duplicate". We get -// all sorts of things from different providers/transcoders, so in the end, we -// just settled on PTS, event id, and segmentation type -func (d *segmentationDescriptor) Equal(c SegmentationDescriptor) bool { - if d == nil || c == nil { - return false - } - if d.TypeID() != c.TypeID() { - return false - } - if !d.SCTE35().HasPTS() || !c.SCTE35().HasPTS() { - return false - } - if d.SCTE35().PTS() != c.SCTE35().PTS() { - return false - } - if d.EventID() != c.EventID() { - return false - } - if d.SegmentNumber() != c.SegmentNumber() { - return false - } - if d.SegmentsExpected() != c.SegmentsExpected() { - return false - } - if d.HasSubSegments() != c.HasSubSegments() { - return false - } - if d.HasSubSegments() && c.HasSubSegments() && (d.SubSegmentNumber() != c.SubSegmentNumber()) { - return false - } - if d.HasSubSegments() && c.HasSubSegments() && (d.SubSegmentsExpected() != c.SubSegmentsExpected()) { - return false - } - return true -} - -// HasProgramSegmentation returns if the descriptor has program segmentation -func (d *segmentationDescriptor) HasProgramSegmentation() bool { - return d.programSegmentationFlag -} - -// SegmentNumber returns the segment number for this descriptor. -func (d *segmentationDescriptor) SegmentNumber() uint8 { - return d.segNum -} - -// SegmentsExpected returns the number of expected segments for this descriptor. -func (d *segmentationDescriptor) SegmentsExpected() uint8 { - return d.segsExpected -} - -// HasSubSegments returns true if this segmentation descriptor has subsegment fields. -func (d *segmentationDescriptor) HasSubSegments() bool { - return d.hasSubSegments -} - -// SubSegmentNumber returns the sub-segment number for this descriptor. -func (d *segmentationDescriptor) SubSegmentNumber() uint8 { - return d.subSegNum -} - -// SubSegmentsExpected returns the number of expected sub-segments for this descriptor. -func (d *segmentationDescriptor) SubSegmentsExpected() uint8 { - return d.subSegsExpected -} - -// SetIsDeliveryNotRestricted sets a flag that determines if the delivery is not restricted -func (d *segmentationDescriptor) IsDeliveryNotRestricted() bool { - return d.deliveryNotRestricted -} - -// IsWebDeliveryAllowed returns if web delivery is allowed, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) IsWebDeliveryAllowed() bool { - return d.webDeliveryAllowedFlag -} - -// IsArchiveAllowed returns true if there are restrictions to storing/recording this segment, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) IsArchiveAllowed() bool { - return d.archiveAllowedFlag -} - -// HasNoRegionalBlackout returns true if there is no regional blackout, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) HasNoRegionalBlackout() bool { - return d.noRegionalBlackoutFlag -} - -// DeviceRestrictions returns which device group the segment is restriced to, this field has no meaning if delivery is not restricted. -func (d *segmentationDescriptor) DeviceRestrictions() DeviceRestrictions { - return d.deviceRestrictions -} - -// MID returns multiple UPIDs, if UPIDType is SegUPIDMID -func (d *segmentationDescriptor) MID() []UPID { - // Check if this data should exist. - if d.upidType != SegUPIDMID { - return nil - } - mid := make([]UPID, len(d.mid)) - for i := range d.mid { - mid[i] = &d.mid[i] - } - return mid -} - -// Components will return components' offsets -func (d *segmentationDescriptor) Components() []ComponentOffset { - components := make([]ComponentOffset, len(d.components)) - for i := range d.components { - components[i] = &d.components[i] - } - return components -} diff --git a/v2/scte35/segmentationdescriptor_test.go b/v2/scte35/segmentationdescriptor_test.go deleted file mode 100644 index 69bff3f..0000000 --- a/v2/scte35/segmentationdescriptor_test.go +++ /dev/null @@ -1,272 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package scte35 - -import ( - "strings" - "testing" -) - -var csp = []byte{ - 0x00, 0xfc, 0x30, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x00, - 0x3d, 0x02, 0x3b, 0x43, 0x55, 0x45, 0x49, 0xc0, 0x00, 0x00, 0x00, 0x7f, 0xbf, 0x0f, 0x2c, 0x75, - 0x72, 0x6e, 0x3a, 0x6d, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x3a, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, - 0x3a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x3a, 0x38, 0x39, 0x38, 0x37, 0x32, 0x30, 0x35, 0x34, - 0x37, 0x34, 0x34, 0x32, 0x34, 0x39, 0x38, 0x34, 0x31, 0x36, 0x33, 0x01, 0x00, 0x00, 0x1a, 0x3f, - 0x5c, 0x92, -} - -var unscheduled_event_start = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x04, 0x54, 0x45, 0x53, 0x54, 0x40, 0x00, 0x00, - 0xff, 0xcb, 0x8c, 0xcc, -} - -var network_end = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x04, 0x54, 0x45, 0x53, 0x54, 0x51, 0x00, 0x00, - 0xfd, 0xfd, 0x2c, 0x21, -} - -var program_start = []byte{ - 0x00, 0xfc, 0x30, 0x35, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1f, 0x02, 0x1d, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x09, 0xa7, 0xec, 0x80, 0x09, 0x09, 0x50, 0x72, 0x6f, 0x67, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x10, 0x01, 0x01, 0xfd, 0xbe, 0x65, 0x8c, -} - -var program_end = []byte{ - 0x00, 0xfc, 0x30, 0x2e, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x18, 0x02, 0x16, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xbf, 0x09, 0x07, 0x50, 0x72, 0x6f, 0x67, 0x45, 0x6e, 0x64, 0x11, 0x01, 0x01, 0xfc, 0xbe, - 0x04, 0x2a, -} - -var provider_ad_start = []byte{ - 0x00, 0xfc, 0x30, 0x3b, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x25, 0x02, 0x23, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x00, 0x52, 0x65, 0xc0, 0x09, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x41, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x30, 0x00, 0x00, 0xfa, 0xa9, 0xe1, 0x3f, -} - -var distributor_po_start = []byte{ - 0x00, 0xfc, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x2a, 0x02, 0x28, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x00, 0x52, 0x65, 0xc0, 0x09, 0x12, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x6f, 0x72, 0x50, 0x4f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x36, 0x00, 0x00, 0x00, 0x00, - 0xfd, 0xea, 0xaf, 0xb8, -} - -var program_resumption = []byte{ - 0x00, 0xfc, 0x30, 0x3a, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x20, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x24, 0x02, 0x22, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x02, - 0x7f, 0xff, 0x00, 0x09, 0xa7, 0xec, 0x80, 0x09, 0x0e, 0x50, 0x72, 0x6f, 0x67, 0x52, 0x65, 0x73, - 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x14, 0x01, 0x01, 0xf9, 0x12, 0xba, 0x59, -} - -var vss = []byte{ - 0x00, 0xfc, 0x30, 0x7b, 0x00, 0x00, 0x6d, 0x71, 0xc7, 0xef, 0x00, 0xff, 0xf0, 0x05, 0x06, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x02, 0x52, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x09, - 0x7f, 0x97, 0x0d, 0x43, 0x09, 0x21, 0x42, 0x4c, 0x41, 0x43, 0x4b, 0x4f, 0x55, 0x54, 0x3a, 0x53, - 0x71, 0x2b, 0x6b, 0x59, 0x39, 0x6d, 0x75, 0x51, 0x64, 0x65, 0x72, 0x47, 0x4e, 0x69, 0x4e, 0x74, - 0x4f, 0x6f, 0x4e, 0x36, 0x77, 0x3d, 0x3d, 0x0e, 0x1e, 0x63, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, - 0x3a, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x72, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x40, 0x00, 0x00, 0x02, 0x0f, 0x43, 0x55, 0x45, 0x49, - 0x00, 0x00, 0x00, 0x09, 0x7f, 0x97, 0x00, 0x00, 0x41, 0x00, 0x00, 0x7a, 0xd7, 0xa4, 0x65, -} - -func TestNoInRules(t *testing.T) { - out, err := NewSCTE35(poOpen1) - if err != nil { - t.Error("NewSCTE35(poOpen1) returned err:", err.Error()) - t.FailNow() - } - - csp, err := NewSCTE35(csp) - if err != nil { - t.Error("NewSCTE35(csp) returned err:", err.Error()) - t.FailNow() - } - - canClose := csp.Descriptors()[0].CanClose(out.Descriptors()[0]) - if canClose { - t.Error("CanClose returned true") - } -} - -func TestOutNoRules(t *testing.T) { - out, err := NewSCTE35(poOpen1) - if err != nil { - t.Error("NewSCTE35(poOpen1) returned err:", err.Error()) - t.FailNow() - } - - csp, err := NewSCTE35(csp) - if err != nil { - t.Error("NewSCTE35(csp) returned err:", err.Error()) - t.FailNow() - } - - canClose := out.Descriptors()[0].CanClose(csp.Descriptors()[0]) - if canClose { - t.Error("CanClose returned true") - } -} - -func TestCloseUnconditional(t *testing.T) { - // Create a 0x40 (unscheduled event start) - unschedEvent, err := NewSCTE35(unscheduled_event_start) - if err != nil { - t.Error("NewSCTE35(unscheduled_event_start) returned err:", err.Error()) - t.FailNow() - } - - // Create a 0x51 (network end) - networkEnd, err := NewSCTE35(network_end) - if err != nil { - t.Error("NewSCTE35(network_end) returned err:", err.Error()) - t.FailNow() - } - - canClose := networkEnd.Descriptors()[0].CanClose(unschedEvent.Descriptors()[0]) - if !canClose { - t.Errorf("CanClose returned false.") - } -} - -func TestCloseEventId(t *testing.T) { - // Create a 0x10 (program start) - programStart, err := NewSCTE35(program_start) - if err != nil { - t.Error("NewSCTE35(program_start) returned err:", err.Error()) - t.FailNow() - } - - // Create a 0x11 (program end) - programEnd, err := NewSCTE35(program_end) - if err != nil { - t.Error("NewSCTE35(program_end) returned err:", err.Error()) - t.FailNow() - } - - canClose := programEnd.Descriptors()[0].CanClose(programStart.Descriptors()[0]) - if !canClose { - t.Errorf("CanClose returned false.") - } - - progEndSegDesc := programEnd.Descriptors()[0].(*segmentationDescriptor) - progEndSegDesc.eventID = 4 - - canClose = programEnd.Descriptors()[0].CanClose(programStart.Descriptors()[0]) - if canClose { - t.Errorf("CanClose returned true.") - } -} - -func TestCloseDifferentPTS(t *testing.T) { - // Create a 0x30 (provider ad start) - adStart, err := NewSCTE35(provider_ad_start) - if err != nil { - t.Error("NewSCTE35(provider_ad_start) returned err:", err.Error()) - t.FailNow() - } - - // Create a 0x36 (distributor PO start) - poStart, err := NewSCTE35(distributor_po_start) - if err != nil { - t.Error("NewSCTE35(distributor_po_start) returned err:", err.Error()) - t.FailNow() - } - - canClose := poStart.Descriptors()[0].CanClose(adStart.Descriptors()[0]) - if !canClose { - t.Errorf("CanClose returned false.") - } - - poStartSignal := poStart.(*scte35) - poStartSignal.pts = 367860 - - canClose = poStart.Descriptors()[0].CanClose(adStart.Descriptors()[0]) - if canClose { - t.Errorf("CanClose returned trues.") - } -} - -func TestCloseBreakaway(t *testing.T) { - // Create a 0x10 (program start) - programStart, err := NewSCTE35(program_start) - if err != nil { - t.Error("NewSCTE35(program_start) returned err:", err.Error()) - t.FailNow() - } - - // Create a 0x14 (program resumption) - programResumption, err := NewSCTE35(program_resumption) - if err != nil { - t.Error("NewSCTE35(program_resumption) returned err:", err.Error()) - t.FailNow() - } - - canClose := programResumption.Descriptors()[0].CanClose(programStart.Descriptors()[0]) - if !canClose { - t.Errorf("CanClose returned false") - } -} - -func TestCloseVSS(t *testing.T) { - vssScte35, err := NewSCTE35(vss) - if err != nil { - t.Error("NewSCTE35(vss) returned err:", err.Error()) - t.FailNow() - } - - // 0x41 from vss signal should close 0x40 from the same signal - canClose := vssScte35.Descriptors()[1].CanClose(vssScte35.Descriptors()[0]) - if !canClose { - t.Error("CanClose returned false") - } -} - -func TestVSSSignalId(t *testing.T) { - expectedSignalId := "Sq+kY9muQderGNiNtOoN6w==" - vssScte35, err := NewSCTE35(vss) - - if err != nil { - t.Error("NewSCTE35(vss) returned err:", err.Error()) - t.FailNow() - } - - signalID, err := vssScte35.Descriptors()[0].StreamSwitchSignalId() - if err != nil { - t.Fatal(err) - } - - // Compare against the Signal ID we should see in the vssScte35 signal. - if strings.Compare(signalID, expectedSignalId) != 0 { - t.Error("SignalID parsed, not as expected") - t.FailNow() - } -} diff --git a/v2/scte35/splicecommand.go b/v2/scte35/splicecommand.go deleted file mode 100644 index 0f8b809..0000000 --- a/v2/scte35/splicecommand.go +++ /dev/null @@ -1,327 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "bytes" - "encoding/binary" - - "github.com/Comcast/gots/v2" -) - -// timeSignal is a struct that represents a time signal splice command in SCTE35 -type timeSignal struct { - hasPTS bool - pts gots.PTS -} - -// CommandType returns the signal's splice command type value. -func (c *timeSignal) CommandType() SpliceCommandType { - return TimeSignal -} - -// parseTimeSignal extracts a time_signal() command from a bytes buffer. It -// returns a timeSignal describing the command. -func parseTimeSignal(buf *bytes.Buffer) (cmd *timeSignal, err error) { - cmd = &timeSignal{} - hasPTS, pts, err := parseSpliceTime(buf) - if !hasPTS { - return nil, gots.ErrSCTE35UnsupportedSpliceCommand - } - cmd.hasPTS = hasPTS - cmd.pts = pts - return cmd, nil -} - -// HasPTS returns true if there is a pts time on the command. -func (c *timeSignal) HasPTS() bool { - return c.hasPTS -} - -// PTS returns the PTS time of the command, not including adjustment. -func (c *timeSignal) PTS() gots.PTS { - return c.pts -} - -// spliceNull is a struct that represents a null splice command in SCTE35 -type spliceNull struct { -} - -// CommandType returns the signal's splice command type value. -func (c *spliceNull) CommandType() SpliceCommandType { - return SpliceNull -} - -// HasPTS returns true if there is a pts time on the command. -func (c *spliceNull) HasPTS() bool { - return false -} - -// PTS returns the PTS time of the command, not including adjustment. -func (c *spliceNull) PTS() gots.PTS { - return 0 -} - -// component is a structure in a spliceCommand. -type component struct { - componentTag byte - - hasPts bool - pts gots.PTS -} - -// CreateComponent will create a component that is used in SpliceInsertCommand. -func CreateComponent() Component { - return &component{} -} - -// ComponentTag returns the tag of the component. -func (c *component) ComponentTag() byte { - return c.componentTag -} - -// HasPTS returns true if there is a pts time on the command. -func (c *component) HasPTS() bool { - return c.hasPts -} - -// PTS returns the PTS time of the command, not including adjustment. -func (c *component) PTS() gots.PTS { - return c.pts -} - -// spliceInsert is a struct that represents a splice insert command in SCTE35 -type spliceInsert struct { - eventID uint32 - eventCancelIndicator bool - outOfNetworkIndicator bool - - isProgramSplice bool - spliceImmediate bool - - hasPTS bool - pts gots.PTS - - components []Component - - hasDuration bool - duration gots.PTS - autoReturn bool - uniqueProgramId uint16 - availNum uint8 - availsExpected uint8 -} - -// CommandType returns the signal's splice command type value. -func (c *spliceInsert) CommandType() SpliceCommandType { - return SpliceInsert -} - -// parseSpliceInsert extracts a splice_insert() command from a bytes buffer. -// It returns a spliceInsert describing the command. -func parseSpliceInsert(buf *bytes.Buffer) (*spliceInsert, error) { - cmd := &spliceInsert{} - if err := cmd.parse(buf); err != nil { - return nil, err - } - return cmd, nil -} - -// parse will parse bytes in the form of bytes.Buffer into a splice insert struct -func (c *spliceInsert) parse(buf *bytes.Buffer) error { - baseFields := buf.Next(5) - if len(baseFields) < 5 { // length of required fields - return gots.ErrInvalidSCTE35Length - } - c.eventID = binary.BigEndian.Uint32(baseFields[:4]) - // splice_event_cancel_indicator 1 - // reserved 7 - c.eventCancelIndicator = baseFields[4]&0x80 == 0x80 - if c.eventCancelIndicator { - return nil - } - // out_of_network_indicator 1 - // program_splice_flag 1 - // duration_flag 1 - // splice_immediate_flag 1 - // reserved 4 - flags, err := buf.ReadByte() - if err != nil { - return gots.ErrInvalidSCTE35Length - } - c.outOfNetworkIndicator = flags&0x80 == 0x80 - c.isProgramSplice = flags&0x40 == 0x40 - c.hasDuration = flags&0x20 == 0x20 - c.spliceImmediate = flags&0x10 == 0x10 - - if c.isProgramSplice && !c.spliceImmediate { - hasPTS, pts, err := parseSpliceTime(buf) - if err != nil { - return err - } - if !hasPTS { - return gots.ErrSCTE35UnsupportedSpliceCommand - } - c.hasPTS = hasPTS - c.pts = pts - } - if !c.isProgramSplice { - cc, err := buf.ReadByte() - if err != nil { - return gots.ErrInvalidSCTE35Length - } - // read components - for ; cc > 0; cc-- { - // component_tag - tag, err := buf.ReadByte() - if err != nil { - return gots.ErrInvalidSCTE35Length - } - comp := &component{componentTag: tag} - if !c.spliceImmediate { - hasPts, pts, err := parseSpliceTime(buf) - if err != nil { - return err - } - comp.hasPts = hasPts - comp.pts = pts - } - c.components = append(c.components, comp) - } - } - if c.hasDuration { - data := buf.Next(5) - if len(data) < 5 { - return gots.ErrInvalidSCTE35Length - } - // break_duration() structure: - c.autoReturn = data[0]&0x80 == 0x80 - c.duration = uint40(data) & 0x01ffffffff - } - progInfo := buf.Next(4) - if len(progInfo) < 4 { - return gots.ErrInvalidSCTE35Length - } - c.uniqueProgramId = binary.BigEndian.Uint16(progInfo[:2]) - c.availNum = progInfo[2] - c.availsExpected = progInfo[3] - return nil -} - -// EventID returns the event id -func (c *spliceInsert) EventID() uint32 { - return c.eventID -} - -// IsOut returns the value of the out of network indicator -func (c *spliceInsert) IsOut() bool { - return c.outOfNetworkIndicator -} - -// IsEventCanceled returns the event cancel indicator -func (c *spliceInsert) IsEventCanceled() bool { - return c.eventCancelIndicator -} - -// HasPTS returns true if there is a pts time on the command. -func (c *spliceInsert) HasPTS() bool { - return c.hasPTS -} - -// PTS returns the PTS time of the command, not including adjustment. -func (c *spliceInsert) PTS() gots.PTS { - return c.pts -} - -// HasDuration returns true if there is a duration -func (c *spliceInsert) HasDuration() bool { - return c.hasDuration -} - -// Duration returns the PTS duration of the command -func (c *spliceInsert) Duration() gots.PTS { - return c.duration -} - -// Components returns the components of the splice command -func (c *spliceInsert) Components() []Component { - return c.components -} - -// IsAutoReturn returns the boolean value of the auto return field -func (c *spliceInsert) IsAutoReturn() bool { - return c.autoReturn -} - -// UniqueProgramId returns the unique_program_id field -func (c *spliceInsert) UniqueProgramId() uint16 { - return c.uniqueProgramId -} - -// AvailNum returns the avail_num field, index of this avail or zero if unused -func (c *spliceInsert) AvailNum() uint8 { - return c.availNum -} - -// AvailsExpected returns avails_expected field, number of avails for program -func (c *spliceInsert) AvailsExpected() uint8 { - return c.availsExpected -} - -// IsProgramSplice returns if the program_splice_flag is set -func (c *spliceInsert) IsProgramSplice() bool { - return c.isProgramSplice -} - -// SpliceImmediate returns if the splice_immediate_flag is set -func (c *spliceInsert) SpliceImmediate() bool { - return c.spliceImmediate -} - -// parseSpliceTime parses a splice_time() structure and returns the values of -// time_specified_flag and pts_time. -// If the time_specified_flag is 0, pts will have a value of gots.PTS(0). -func parseSpliceTime(buf *bytes.Buffer) (timeSpecified bool, pts gots.PTS, err error) { - flags, err := buf.ReadByte() - if err != nil { - err = gots.ErrInvalidSCTE35Length - return false, gots.PTS(0), err - } - timeSpecified = flags&0x80 == 0x80 - if !timeSpecified { - // Time isn't specified, assume PTS of 0. - return false, gots.PTS(0), nil - } - // unread prev byte because it contains the top bit of the pts offset - if err = buf.UnreadByte(); err != nil { - return true, gots.PTS(0), err - } - if buf.Len() < 5 { - err = gots.ErrInvalidSCTE35Length - return - } - pts = uint40(buf.Next(5)) & 0x01ffffffff - return true, pts, nil -} diff --git a/v2/scte35/splicecommandmodify.go b/v2/scte35/splicecommandmodify.go deleted file mode 100644 index 8f7ea0b..0000000 --- a/v2/scte35/splicecommandmodify.go +++ /dev/null @@ -1,250 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "github.com/Comcast/gots/v2" -) - -// CreateSpliceInsertCommand will create a default SpliceInsertCommand. -func CreateSpliceInsertCommand() SpliceInsertCommand { - return &spliceInsert{ - // enable this to have less variable sized fields by - // default, do not use component splice mode by default - isProgramSplice: true, - } -} - -// CreateTimeSignalCommand will create a default TimeSignalCommand -func CreateTimeSignalCommand() TimeSignalCommand { - return &timeSignal{} -} - -// CreateSpliceNull will create a Null SpliceCommand -func CreateSpliceNull() SpliceCommand { - return &spliceNull{} -} - -// SetComponentTag sets the component tag, which is used for the identification of the component. -func (c *component) SetComponentTag(value byte) { - c.componentTag = value -} - -// SetHasPTS sets a flag that determines if the component has a PTS. -func (c *component) SetHasPTS(value bool) { - c.hasPts = value -} - -// SetPTS sets the PTS of the component. -func (c *component) SetPTS(value gots.PTS) { - c.pts = value & 0x01ffffffff -} - -// Data returns the bytes of this splice command. -func (c *spliceNull) Data() []byte { - return []byte{} // return empty slice -} - -// SetHasPTS sets the flag that indicates if there is a pts time on the command. -func (c *spliceNull) SetHasPTS(value bool) { - // do nothing -} - -// SetPTS sets the PTS. -func (c *spliceNull) SetPTS(value gots.PTS) { - // do nothing -} - -// spliceTimeBytes returns the raw data bytes of a splice time. -func spliceTimeBytes(hasPTS bool, pts gots.PTS) []byte { - if hasPTS { - bytes := make([]byte, 5) - bytes[0] = 0xFE // 1111 1110 - bytes[0] |= byte(pts>>32) & 0x01 // 0000 0001 - bytes[1] = byte(pts >> 24) // 1111 1111 - bytes[2] = byte(pts >> 16) // 1111 1111 - bytes[3] = byte(pts >> 8) // 1111 1111 - bytes[4] = byte(pts) // 1111 1111 - return bytes - } - // return 0111 1110 - return []byte{0x7E} // only reserved bits are set -} - -// Data returns the bytes of this splice command. -func (c *timeSignal) Data() []byte { - return spliceTimeBytes(c.hasPTS, c.pts) -} - -// SetHasPTS sets the flag that indicates if there is a pts time on the command. -func (c *timeSignal) SetHasPTS(value bool) { - c.hasPTS = value -} - -// SetPTS sets the PTS. -func (c *timeSignal) SetPTS(value gots.PTS) { - c.pts = value & 0x01ffffffff -} - -// Data returns the bytes of this splice command. -func (c *spliceInsert) Data() []byte { - bytes := make([]byte, 6) - bytes[0] = byte(c.eventID >> 24) - bytes[1] = byte(c.eventID >> 16) - bytes[2] = byte(c.eventID >> 8) - bytes[3] = byte(c.eventID) - - bytes[4] = 0x7F // reserved - - if c.eventCancelIndicator { - bytes[4] |= 0x80 - } - - bytes[5] = 0x0F // reserved - - if c.outOfNetworkIndicator { - bytes[5] |= 0x80 - } - if c.isProgramSplice { - bytes[5] |= 0x40 - } - if c.isProgramSplice { - bytes[5] |= 0x40 - } - if c.hasDuration { - bytes[5] |= 0x20 - } - if c.spliceImmediate { - bytes[5] |= 0x10 - } - - if c.isProgramSplice && !c.spliceImmediate { - bytes = append(bytes, spliceTimeBytes(c.hasPTS, c.pts)...) - } - - if !c.isProgramSplice { - componentCount := byte(len(c.components)) - componentsBytes := []byte{componentCount} - for _, component := range c.components { - componentBytes := make([]byte, 1) - componentBytes[0] = component.ComponentTag() - if c.spliceImmediate { - componentBytes = append(componentBytes, spliceTimeBytes(component.HasPTS(), component.PTS())...) - } - componentsBytes = append(componentsBytes, componentBytes...) - } - bytes = append(bytes, componentsBytes...) - } - - if c.hasDuration { - durationBytes := make([]byte, 5) - // break_duration() structure: - - durationBytes[0] |= 0x7E // reserved - if c.autoReturn { - durationBytes[0] |= 0x80 - } - durationBytes[0] |= byte(c.duration>>32) & 0x01 // 0000 0001 - durationBytes[1] = byte(c.duration >> 24) // 1111 1111 - durationBytes[2] = byte(c.duration >> 16) // 1111 1111 - durationBytes[3] = byte(c.duration >> 8) // 1111 1111 - durationBytes[4] = byte(c.duration) // 1111 1111 - bytes = append(bytes, durationBytes...) - } - - endingBytes := make([]byte, 4) - endingBytes[0] = byte(c.uniqueProgramId >> 8) - endingBytes[1] = byte(c.uniqueProgramId) - endingBytes[2] = byte(c.availNum) - endingBytes[3] = byte(c.availsExpected) - - bytes = append(bytes, endingBytes...) - return bytes -} - -// SetEventID sets the event id. -func (c *spliceInsert) SetEventID(value uint32) { - c.eventID = value -} - -// SetIsOut sets the value of the out of network indicator -func (c *spliceInsert) SetIsOut(value bool) { - c.outOfNetworkIndicator = value -} - -// SetIsEventCanceled sets the the event cancel indicator -func (c *spliceInsert) SetIsEventCanceled(value bool) { - c.eventCancelIndicator = value -} - -// SetHasPTS sets the flag that indicates if there is a pts time on the command. -func (c *spliceInsert) SetHasPTS(value bool) { - c.hasPTS = value -} - -// SetPTS sets the PTS. -func (c *spliceInsert) SetPTS(value gots.PTS) { - c.pts = value & 0x01ffffffff -} - -// SetHasDuration sets the duration flag -func (c *spliceInsert) SetHasDuration(value bool) { - c.hasDuration = value -} - -// SetDuration sets the PTS duration of the command -func (c *spliceInsert) SetDuration(value gots.PTS) { - c.duration = value -} - -// SetIsAutoReturn sets the boolean value of the auto return field -func (c *spliceInsert) SetIsAutoReturn(value bool) { - c.autoReturn = value -} - -// SetUniqueProgramId sets the unique program Id -func (c *spliceInsert) SetUniqueProgramId(value uint16) { - c.uniqueProgramId = value -} - -// SetAvailNum sets the avail_num field, zero if unused. otherwise index of the avail -func (c *spliceInsert) SetAvailNum(value uint8) { - c.availNum = value -} - -// SetAvailsExpected sets the avails_expected field, number of avails for program -func (c *spliceInsert) SetAvailsExpected(value uint8) { - c.availsExpected = value -} - -// SetIsProgramSplice sets the program splice flag -func (c *spliceInsert) SetIsProgramSplice(value bool) { - c.isProgramSplice = value -} - -// SetSpliceImmediate sets the splice immediate flag -func (c *spliceInsert) SetSpliceImmediate(value bool) { - c.spliceImmediate = value -} diff --git a/v2/scte35/state.go b/v2/scte35/state.go deleted file mode 100644 index 07392bd..0000000 --- a/v2/scte35/state.go +++ /dev/null @@ -1,217 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package scte35 - -import ( - "github.com/Comcast/gots/v2" - "strings" -) - -const receivedRingLen = 10 - -type receivedElem struct { - pts gots.PTS - descs []SegmentationDescriptor -} - -type state struct { - open []SegmentationDescriptor - received []*receivedElem - receivedHead int - blackoutIdx int - inBlackout bool -} - -// NewState returns an initialized state object -func NewState() State { - return &state{received: make([]*receivedElem, receivedRingLen)} -} - -func (s *state) Open() []SegmentationDescriptor { - open := make([]SegmentationDescriptor, len(s.open)) - copy(open, s.open) - if s.inBlackout { - return append(open[0:s.blackoutIdx], open[s.blackoutIdx+1:]...) - } else { - return open - } -} - -func (s *state) ProcessDescriptor(desc SegmentationDescriptor) ([]SegmentationDescriptor, error) { - var err error - var closed []SegmentationDescriptor - // check if desc has a pts because we can't handle if it doesn't - if !desc.SCTE35().HasPTS() { - return nil, gots.ErrSCTE35UnsupportedSpliceCommand - } - // check if this is a duplicate - if not, add it to the received list and - // drop the old received if we're over the length limit - descAdded := false - pts := desc.SCTE35().PTS() - for _, e := range s.received { - if e != nil { - for _, d := range e.descs { - if e.pts == pts { - if desc.Equal(d) { - // Duplicate desc found - return nil, gots.ErrSCTE35DuplicateDescriptor - } - e.descs = append(e.descs, desc) - descAdded = true - } - // check if we have seen a VSS signal with the same signalId and - // same eventId before. - if desc.EventID() == d.EventID() && - d.TypeID() == SegDescUnscheduledEventStart && desc.TypeID() == SegDescUnscheduledEventStart { - descStreamSwitchSignalId, err := desc.StreamSwitchSignalId() - if err != nil { - return nil, err - } - - dStreamSwitchSignalId, err := d.StreamSwitchSignalId() - if err != nil { - return nil, err - } - - if strings.Compare(descStreamSwitchSignalId, dStreamSwitchSignalId) == 0 && - (d.EventID() == desc.EventID()) { - // desc and d contain same signalId and same eventID - // we should not be processing this desc. - return nil, gots.ErrSCTE35DuplicateDescriptor - } - descAdded = true - } - } - } - } - if !descAdded { - s.received[s.receivedHead] = &receivedElem{pts: pts, descs: []SegmentationDescriptor{desc}} - s.receivedHead = (s.receivedHead + 1) % receivedRingLen - } - - // first close signals until one returns false, then handle the breakaway - for i := len(s.open) - 1; i >= 0; i-- { - d := s.open[i] - if desc.CanClose(d) { - closed = append(closed, d) - } else { - break - } - } - // remove all closed descriptors - s.open = s.open[0 : len(s.open)-len(closed)] - - // validation logic - switch desc.TypeID() { - // breakaway handling - case SegDescProgramBreakaway: - s.inBlackout = true - s.blackoutIdx = len(s.open) - // append breakaway to match against resumption even though it's an in - s.open = append(s.open, desc) - case SegDescProgramResumption: - if s.inBlackout { - s.inBlackout = false - s.open = s.open[0:s.blackoutIdx] - // TODO: verify that there is a program start that has a matching event id - } else { - // ProgramResumption can only come after a breakaway - err = gots.ErrSCTE35InvalidDescriptor - } - fallthrough - // out signals - case SegDescProgramStart, - SegDescChapterStart, - SegDescBreakStart, - SegDescProviderAdvertisementStart, - SegDescDistributorAdvertisementStart, - SegDescProviderPOStart, - SegDescDistributorPOStart, - SegDescProviderAdBlockStart, - SegDescUnscheduledEventStart, - SegDescNetworkStart, - SegDescProgramOverlapStart, - SegDescProgramStartInProgress: - s.open = append(s.open, desc) - // in signals - case SegDescProgramEnd: - if len(closed) == 0 { - err = gots.ErrSCTE35MissingOut - break - } - case SegDescChapterEnd, - SegDescProviderAdvertisementEnd, - SegDescProviderPOEnd, - SegDescDistributorAdvertisementEnd, - SegDescDistributorPOEnd, - SegDescUnscheduledEventEnd, - SegDescNetworkEnd: - var openDesc SegmentationDescriptor - // We already closed a descriptor - // and have no other open descriptors - // so break and return closed descriptors - if len(closed) != 0 && len(s.open) == 0 { - break - } - // descriptor matches out, but doesn't close it. Check event id against open - if len(closed) == 0 || closed[len(closed)-1].TypeID() != desc.TypeID()-1 { - if len(s.open) == 0 { - err = gots.ErrSCTE35MissingOut - break - } else { - openDesc = s.open[len(s.open)-1] - } - } else { - openDesc = closed[len(closed)-1] - } - if openDesc.EventID() != desc.EventID() { - err = gots.ErrSCTE35MissingOut - } - default: - // no validating - } - return closed, err -} - -func (s *state) Close(desc SegmentationDescriptor) ([]SegmentationDescriptor, error) { - // back off list until we reach the descriptor we are closing. If we don't - // find it, return error - var closed []SegmentationDescriptor - for i := len(s.open) - 1; i >= 0; i-- { - d := s.open[i] - if desc.Equal(d) { - // found our descriptor at index i, remove it - // Shift s.open left by one index. - copy(s.open[i:], s.open[i+1:]) - // Delete last element - s.open[len(s.open)-1] = nil - // Truncate slice - s.open = s.open[:len(s.open)-1] - closed = append(closed, d) - return closed, nil - } - } - return nil, gots.ErrSCTE35DescriptorNotFound -} diff --git a/v2/scte35/state_test.go b/v2/scte35/state_test.go deleted file mode 100644 index 04e91c3..0000000 --- a/v2/scte35/state_test.go +++ /dev/null @@ -1,1608 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package scte35 - -import ( - "encoding/base64" - "fmt" - "testing" - - "github.com/Comcast/gots/v2" -) - -// All signal data generated with scte_creator: https://github.comcast.com/mniebu200/scte_creator -var poOpen1 = []byte{ - 0x00, 0xfc, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1d, 0x02, 0x1b, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xff, 0x00, 0x00, 0x0a, 0xff, 0x50, 0x09, 0x05, 0x54, 0x65, 0x73, 0x74, 0x31, 0x34, 0x01, - 0x01, 0x00, 0x00, 0xff, 0x31, 0x22, 0x36, -} -var poClose1 = []byte{ - 0x00, 0xfc, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x0d, 0xbf, 0x24, 0x00, 0x1d, 0x02, 0x1b, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x31, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x35, 0x01, - 0x01, 0x00, 0x00, 0xfc, 0x53, 0xaf, 0x44, -} - -var poClose12 = []byte{ - 0x00, 0xfc, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x0a, 0xff, 0x50, 0x00, 0x1d, 0x02, 0x1b, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x31, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x35, 0x01, - 0x02, 0x00, 0x00, 0xfd, 0x6f, 0xe8, 0xc7, -} -var poClose22 = []byte{ - 0x00, 0xfc, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x0d, 0xbf, 0x24, 0x00, 0x1d, 0x02, 0x1b, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x31, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x35, 0x02, - 0x02, 0x00, 0x00, 0xfb, 0x7c, 0x2e, 0xe1, -} -var progStart = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x09, 0x50, 0x72, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x10, 0x01, 0x01, - 0xf9, 0x43, 0xc2, 0x2f, -} -var progEnd = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x09, 0x50, 0x72, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x11, 0x01, 0x01, - 0xfa, 0x95, 0x2c, 0xcf, -} -var progBreakaway = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x05, 0x7f, 0xa8, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x09, 0x50, 0x72, 0x6f, 0x67, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x13, 0x01, 0x01, - 0xf8, 0xd9, 0x85, 0xa7, -} -var progResumption = []byte{ - 0x00, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x0d, 0xbf, 0x24, 0x00, 0x1a, 0x02, 0x18, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xbf, 0x09, 0x09, 0x50, 0x72, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x14, 0x01, 0x01, - 0xfb, 0x4f, 0x7b, 0x70, -} - -var ppoStartSubsegments = []byte{ - 0x00, 0xfc, 0x30, 0x3d, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x27, 0x02, 0x25, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xff, 0x00, 0x00, 0xa4, 0xcb, 0x80, 0x09, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x50, 0x4f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x34, 0x01, 0x03, 0x01, 0x02, 0xfa, 0x06, 0x95, - 0x8f, -} - -var dpoStartSubsegments = []byte{ - 0x00, 0xfc, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x2a, 0x02, 0x28, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xff, 0x00, 0x00, 0xa4, 0xcb, 0x80, 0x09, 0x12, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x6f, 0x72, 0x50, 0x4f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x36, 0x01, 0x01, 0x02, 0x02, - 0xfb, 0x2f, 0xe6, 0x7c, -} - -var dpoFirstEndSubsegments = []byte{ - 0x00, 0xfc, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x26, 0x02, 0x24, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xff, 0x00, 0x00, 0xa4, 0xcb, 0x80, 0x09, 0x10, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x6f, 0x72, 0x50, 0x4f, 0x45, 0x6e, 0x64, 0x37, 0x01, 0x02, 0xfa, 0x60, 0x45, 0xdd, -} - -var dpoSecondEndSubsegments = []byte{ - 0x00, 0xfc, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x26, 0x02, 0x24, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xff, 0x00, 0x00, 0xa4, 0xcb, 0x80, 0x09, 0x10, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x6f, 0x72, 0x50, 0x4f, 0x45, 0x6e, 0x64, 0x37, 0x02, 0x02, 0xfe, 0x65, 0x89, 0x11, -} - -var ppoEndSubsegments = []byte{ - 0x00, 0xfc, 0x30, 0x3b, 0x00, 0x00, 0x00, 0x02, 0xdd, 0x21, 0x00, 0x00, 0x00, 0x05, 0x06, 0xfe, - 0x00, 0x02, 0xbf, 0xd4, 0x00, 0x25, 0x02, 0x23, 0x43, 0x55, 0x45, 0x49, 0x00, 0x00, 0x00, 0x01, - 0x7f, 0xff, 0x00, 0x00, 0xa4, 0xcb, 0x80, 0x09, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x50, 0x4f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x35, 0x00, 0x00, 0xfa, 0x0b, 0x30, 0xf0, -} - -// 0x10 ProgramStart -var scteDesc10Signal = "/DBFAAAAABeOAP/wBQb+WxdHNQAvAi1DVUVJACSqm3/9ABNP17wMGURJU0MyMjA1NjVfMDAyXzAxXzU3MUEtMDEQAQEeP8NH" -// 0x11 ProgramEnd -var scteDesc11Signal = "/DBAAAAAABeOAP/wBQb+bmg4eQAqAihDVUVJACSqm3+9DBlESVNDMjIwNTY1XzAwMl8wMV81NzFBLTA2EQEB/G8f5w==" -// 0x20 ChapterStart -var scteDesc20Signal = "/DBAAAAAAtaWAAAABQb/d5cVcgAqAihDVUVJ/////3//AALEr24BFGJyYXZvX0VQMDExMzQ0MTkwMTIyIAEA4BXDFw==" -var scteDesc20Signal1= "/DA/AAB6tBn0AP/wBQb/+aPMhwApAh1DVUVJYh5C/n+fAQ5FUDAxMjMxOTU3MDk1MCABZAAIQ1VFSQAAAABYSAjC" -// 0x21 ChapterEnd -var scteDesc21Signal = "/DBAAAAAAtaWAAAABQb/elu5JQAqAihDVUVJ/////3//AALEr24BFGJyYXZvX0VQMDExMzQ0MTkwMTIyIQEA1Kv4MQ==" -// 0x22 BreakStart -var scteDesc22Signal = "/DBAAAAAAtaWAAAABQb/elu5JQAqAihDVUVJ/////3//AAEgZ5IBFGJyYXZvX0VQMDExMzQ0MTkwMTIyIgIERTkzgA==" -var scteDesc22Signal1 ="/DAsAAAAAAAAAP/wBQb+Uq7jaQAWAhRDVUVJAAABAH//AAGhbPAAACIAAL5jGRA=" -// 0x23 BreakEnd -var scteDesc23Signal = "/DBAAAAAAtaWAAAABQb/e3rvuQAqAihDVUVJ/////3//AAEgZ5IBFGJyYXZvX0VQMDExMzQ0MTkwMTIyIwIEzcrAYA==" -// 0x30 ProviderAdStart -var scteDesc30Signal = "/DBPAAAAAAAAAP/wBQb/xHJKjwA5AAVTQVBTCwIwQ1VFSf////9//wAAKSd/Dxx1cm46bmJjdW5pLmNvbTpicmM6NTM5NjM4MjY2MAYCNMFiCw==" -var scteDesc30Signal1 = "/DA2AAB6tBn0AP/wBQb//bDBCQAgAhRDVUVJYh5C/n+fAQVDMjQ3MDABAQAIQ1VFSQAAAACc3JW8" -// 0x31 ProviderAdEnd -var scteDesc31Signal = "/DBPAAAAAAAAAP/wBQb/xJt8QAA5AAVTQVBTCwIwQ1VFSf////9//wAAKSd/Dxx1cm46bmJjdW5pLmNvbTpicmM6NTM5NjM4MjY2MQYCW++KKQ==" -// 0x34 ProviderPlacementOpportunityStart -var scteDesc34Signal = "/DBfAAAAAAAA///wBQb/iRp43QBJAhxDVUVJ6tzJ0n//AAEhrJQICAAFH4Lq3MnSNAIDAilDVUVJAAAAAH+/DBpWTU5VAWCXNGVv9BHsmxsOQM8vwoUB+olIUQEAAKn6Lds=" -// 0x35 ProviderPlacementOpportunityEnd -var scteDesc35Signal = "/DBaAAAAAAAA///wBQb/ijwo4wBEAhdDVUVJ6tzJ0n+/CAgABR+C6tzJ0jUAAAIpQ1VFSQAAAAB/vwwaVk1OVQFglzRlb/QR7JsbDkDPL8KFAPqJSFEBAABl+tWe" -// 0x36 DistributorPlacementOpportunityStart -var scteDesc36Signal ="/DBLAAEs/S0UAP/wBQb+AAAAAAA1AjNDVUVJT///9X//AACky4AJH1NJR05BTDoyWURWeCtSKzlWc0FBQUFBQUFBQkFRPT02AAD9DXQ/" -//0x40 UnscheduledEventStart -var scteDesc40Signal = "/DB7AAFfzZzVAP/wBQb+AAAAAABlAlJDVUVJAABeUX+XDUMJIUJMQUNLT1VUOjI1dU5ZeEl3UXVXclI5WUxDR2I0Y2c9PQ4eY29tY2FzdDpsaW5lYXI6bGljZW5zZXJvdGF0aW9uQAAAAg9DVUVJAABeUX+XAABBAAB1H+6A" -// 0x44 ProviderAdBlockStart -var scteDesc44Signal = "/DCJAAGqtdGtAP/wBQb/AAAAAQBzAnFDVUVJQAFfVH//AAApMuANXQ8edXJuOmNvbWNhc3Q6YWx0Y29uOmFkZHJlc3NhYmxlDyx1cm46bWVybGluOmxpbmVhcjpzdHJlYW06Mzg0OTU3MzQ4MjgzNzU2NTE2MwkNUE86MTA3MzgzMTc2NEQAAAAA5o/+Nw==" -var scteDesc44Signal1 ="/DCfAAGab0HiAP/wBQb/AAAAAQCJAodDVUVJQAFfVH//AAApMuANcw8edXJuOmNvbWNhc3Q6YWx0Y29uOmFkZHJlc3NhYmxlDyx1cm46bWVybGluOmxpbmVhcjpzdHJlYW06Mzg0OTU3MzQ4MjgzNzU2NTE2MwkNUE86MTA3MzgzMTc2NAkKQlJFQUs6MTIzNAgIAAAAADeUEfJEAQMAAAu+e9A=" -// 0x45 ProviderAdBlockEnd -var scteDesc45Signal = "/DCEAAGq3wk7AP/wBQb/AAAAAQBuAmxDVUVJQAFfVH+/DV0PHnVybjpjb21jYXN0OmFsdGNvbjphZGRyZXNzYWJsZQ8sdXJuOm1lcmxpbjpsaW5lYXI6c3RyZWFtOjM4NDk1NzM0ODI4Mzc1NjUxNjMJDVBPOjEwNzM4MzE3NjRFAAAAAHbwDu4=" -// 0x50 NetworkStart -var scteDesc50Signal = "/DBQAAAAAAAAAABwBQb/LrIZ4QA6AhtDVUVJQAAAAH+fCgwUd4vl4/YAAAAAAABQAAACG0NVRUlAAAABf48KDBR3vd4u/wAAAAAAAFEAAF4PZmg=" -// 0x51 NetworkEnd -var scteDesc51Signal = "DBQAAAAAAAAAABwBQb/LrIZ4QA6AhtDVUVJQAAAAH+fCgwUd4vl4/YAAAAAAABQAAACG0NVRUlAAAABf48KDBR3vd4u/wAAAAAAAFEAAF4PZmg=" - -func TestOutIn(t *testing.T) { - st := NewState() - open, e := NewSCTE35(poOpen1) - if e != nil { - t.Fatal("NewSCTE35(poOpen1) returned err:", e) - } - c, e := st.ProcessDescriptor(open.Descriptors()[0]) - if e != nil { - t.Error("ProcessDescriptor of out returned unexpected err:", e) - } - if len(c) != 0 { - t.Error("ProcessDescriptor returned closed signals when none should exist") - } - if len(st.Open()) != 1 { - t.Error("Open() returned unexpected number of descriptors") - } else if st.Open()[0] != open.Descriptors()[0] { - t.Error("Open returned unexpected descriptor") - } - close, e := NewSCTE35(poClose1) - if e != nil { - t.Fatal("NewSCTE35(poClose1) returned err:", e) - } - c, e = st.ProcessDescriptor(close.Descriptors()[0]) - if e != nil { - t.Error("ProcessDescriptor of in returned unexpected err:", e) - } - if len(c) != 1 { - t.Error("ProcessDescriptor returned unexpected number of closed descriptors") - } else if c[0] != open.Descriptors()[0] { - t.Error("ProcessDescriptor returned unexpected close descriptor") - } - if len(st.Open()) != 0 { - t.Error("Unexpectedly signals are still open") - } -} - -func TestOutInIn(t *testing.T) { - st := NewState() - open, e := NewSCTE35(poOpen1) - if e != nil { - t.Fatal("NewSCTE35(poOpen1) returned err:", e) - } - c, e := st.ProcessDescriptor(open.Descriptors()[0]) - if e != nil { - t.Error("ProcessDescriptor of out returned unexpected err:", e) - } - if len(c) != 0 { - t.Error("ProcessDescriptor returned closed signals when none should exist") - } - if len(st.Open()) != 1 { - t.Error("Open() returned unexpected number of descriptors") - } else if st.Open()[0] != open.Descriptors()[0] { - t.Error("Open returned unexpected descriptor") - } - close1, e := NewSCTE35(poClose12) - if e != nil { - t.Fatal("NewSCTE35(poClose12) returned unexpected err:", e) - } - c, e = st.ProcessDescriptor(close1.Descriptors()[0]) - if e != nil { - t.Error("ProcessDescriptor of in 1 returned unexpected err:", e) - } - if len(c) != 0 { - t.Fatal("Close 1/2 closed open, which is not correct behavior") - } - if len(st.Open()) != 1 { - t.Error("Open() returned unexpected number of descriptors") - } else if st.Open()[0] != open.Descriptors()[0] { - t.Error("Open returned unexpected descriptor") - } - close2, e := NewSCTE35(poClose22) - if e != nil { - t.Fatal("NewSCTE35(poClose22) returned unexpected err:", e) - } - c, e = st.ProcessDescriptor(close2.Descriptors()[0]) - if e != nil { - t.Error("ProcessDescriptor of in 2 returned unexpected err:", e) - } - if len(c) != 1 { - t.Error("ProcessDescriptor returned unexpected number of closed descriptors") - } else if c[0] != open.Descriptors()[0] { - t.Error("ProcessDescriptor returned unexpected close descriptor") - } - if len(st.Open()) != 0 { - t.Error("Unexpectedly signals are still open") - } -} - -func TestDuplicateOut(t *testing.T) { - st := NewState() - open, e := NewSCTE35(poOpen1) - if e != nil { - t.Fatal("NewSCTE35(poOpen1) returned err:", e) - } - _, e = st.ProcessDescriptor(open.Descriptors()[0]) - if e != nil { - t.Error("ProcessDescriptor of out returned unexpected err:", e) - } - _, e = st.ProcessDescriptor(open.Descriptors()[0]) - if e != gots.ErrSCTE35DuplicateDescriptor { - t.Error("ProcessDescriptor of out returned unexpected err:", e) - } - if len(st.Open()) != 1 { - t.Error("There should have been 1 open signal, instead num(open):", len(st.Open())) - } -} - -func TestOutOutIn(t *testing.T) { - st := NewState() - - // 0x30 - padOpenSignalBytes, _ := base64.StdEncoding.DecodeString("/DBsAAH/7m1eAAKQBQb+sX6o+wBWAlRDVUVJAAAAJ3//AAApMuANQAwOQU1DTiBMMDAxMjM0NTYJCFBPOjEyMzQ1DiRiY2IxZGQ4ZS1kMzIzLTQ1ODktOWQ3OC1hM2QxMzYyYWJiYjYwAQGtc8xr") - padOpenSignal, err := NewSCTE35(append([]byte{0x0}, padOpenSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - _, err = st.ProcessDescriptor(padOpenSignal.Descriptors()[0]) - if err != nil { - t.Error("ProcessDescriptor of out returned unexpected err:", err) - } - if len(st.Open()) != 1 { - t.Error("There should have been 1 open signal, instead num(open):", len(st.Open())) - } - - // 0x34 - ppoStartSignalBytes, _ := base64.StdEncoding.DecodeString("/DA0AAG3wyKO///wBQb/yRLo+gAeAhxDVUVJQAAAZH/PAAEOFNwICAAAAAArJkYdNAAAMcFkEw==") - ppoStartSignal, err := NewSCTE35(append([]byte{0x0}, ppoStartSignalBytes...)) - if err != nil { - t.Fatal("NewSCTE35(ppoStartSignal) return err:", err) - } - - c, err := st.ProcessDescriptor(ppoStartSignal.Descriptors()[0]) - if err != nil { - t.Error("ProcessDescriptor of out 2 returned unexpected err:", err) - } - if len(c) != 1 { - t.Error("ppoStartSignal unexpectedly did not close the first signal") - } - if len(st.Open()) != 1 { - t.Error("There should have been 1 open signal, instead num(open):", len(st.Open())) - } - // now pass through the close signal and check - // 0x35 - close the 0x34 - closeSignalBytes, _ := base64.StdEncoding.DecodeString("/DAvAAG2uS3c///wBQb/yiD8XAAZAhdDVUVJQAAAZH+fCAgAAAAAKyZGHTUAAMzqBnE=") - close, err := NewSCTE35(append([]byte{0x0},closeSignalBytes...)) - if err != nil { - t.Fatal("NewSCTE35(closeSignalBytes) return unexpected err:", err) - } - - c, err = st.ProcessDescriptor(close.Descriptors()[0]) - if err != nil { - t.Error("Processing first desc of close returned unexpected err:", err) - } - - if len(c) != 1 { - t.Error("First desc unexpectedly did not close inner out") - } - if len(st.Open()) != 0 { - t.Error("There should have been 0 open signals, instead num(open):", len(st.Open())) - } -} - -func TestOutOut(t *testing.T) { - state := NewState() - // 0x30 - Out signal 1 - padOpenSignalBytes, _ := base64.StdEncoding.DecodeString("/DBsAAH/7m1eAAKQBQb+sX6o+wBWAlRDVUVJAAAAJ3//AAApMuANQAwOQU1DTiBMMDAxMjM0NTYJCFBPOjEyMzQ1DiRiY2IxZGQ4ZS1kMzIzLTQ1ODktOWQ3OC1hM2QxMzYyYWJiYjYwAQGtc8xr") - padOpenSignal, err := NewSCTE35(append([]byte{0x0}, padOpenSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - _, err = state.ProcessDescriptor(padOpenSignal.Descriptors()[0]) - if err != nil { - t.Error("ProcessDescriptor of out returned unexpected err:", err) - } - if len(state.Open()) != 1 { - t.Error("There should have been 1 open signal, instead num(open):", len(state.Open())) - } - - // 0x36 - event_id: 1342177266 - seg_num: 0 - seg_expected: 0 - // Should close the earlier Out - secondOutSignalBytes, _ := base64.StdEncoding.DecodeString("/DBLAAF0QXOWAP/wBQb+AAAAAAA1AjNDVUVJT///8n//AACky4AJH1NJR05BTDozR1NOanl3cE1sb0FBQUFBQUFBQkFRPT02AAA9gIK2") - secondOutSignal, err := NewSCTE35(append([]byte{0x0}, secondOutSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(secondOutSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("One event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } -} - -//Creates a PPO start, DPO start, DPO end, DPO end, PPO end. -func TestSubsegments(t *testing.T) { - state := NewState() - - // 0x34 - ppoStart, err := NewSCTE35(ppoStartSubsegments) - if err != nil { - t.Fatal("NewSCTE35(ppoStartSubsegments) return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(ppoStart.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x36 - dpoStart, err := NewSCTE35(dpoStartSubsegments) - if err != nil { - t.Fatal("NewSCTE35(dpoStartSubsegments) return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(dpoStart.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be two open signals (%d)", len(state.Open())) - } - - // 0x37 - dpoFirstEnd, err := NewSCTE35(dpoFirstEndSubsegments) - if err != nil { - t.Fatal("NewSCTE35(dpoFirstEndSubsegments) return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(dpoFirstEnd.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be two open signals (%d)", len(state.Open())) - } - - // 0x37 - dpoSecondEnd, err := NewSCTE35(dpoSecondEndSubsegments) - if err != nil { - t.Fatal("NewSCTE35(dpoSecondEndSubsegments) return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(dpoSecondEnd.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("One event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x35 - ppoEnd, err := NewSCTE35(ppoEndSubsegments) - if err != nil { - t.Fatal("NewSCTE35(ppoEndSubsegments) return err:", err.Error()) - } - closed, err = state.ProcessDescriptor(ppoEnd.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("One event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be no open signals (%d)", len(state.Open())) - } -} - -// Test the logic for when a closing IN signal occurs after another OUT signal. -// 0x36 -> 0x37 (1/3) -> 0x37 (2/3) -> 0x30 -> 0x37 (3/3) -// End state should be just 0x30. -func TestOutInInOutIn(t *testing.T) { - state := NewState() - - // 0x36 - event_id:0 - seg_num: 0 - seg_expected: 0 - outSignalBytes, _ := base64.StdEncoding.DecodeString("/DBLAAFztMbuAP/wBQb+AAAAAAA1AjNDVUVJAAAAAH//AACky4AJH1NJR05BTDozR1NOajNnb01sb0FBQUFBQUFBQkFRPT02AADO/OgI") - outSignal, err := NewSCTE35(append([]byte{0x0}, outSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(outSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x37 - event_id: 0 - seg_num: 1 - seg_expected: 3 - firstInSignalBytes, _ := base64.StdEncoding.DecodeString("/DBGAAF0ByyuAP/wBQb+AAAAAAAwAi5DVUVJAAAAAH+/CR9TSUdOQUw6M0dTTmozZ29NbG9BQUFBQUFBQUJBZz09NwEDfTeSVQ==") - firstInSignal, err := NewSCTE35(append([]byte{0x0}, firstInSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(firstInSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x37 - event_id: 0 - seg_num: 2 - seg_expected: 3 - secondInSignalBytes, _ := base64.StdEncoding.DecodeString("/DBGAAF0MF+OAP/wBQb+AAAAAAAwAi5DVUVJAAAAAH+/CR9TSUdOQUw6M0dTTmozZ29NbG9BQUFBQUFBQUJBdz09NwIDvefEqg==") - secondInSignal, err := NewSCTE35(append([]byte{0x0}, secondInSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(secondInSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x30 Another Out - padOpenSignalBytes, _ := base64.StdEncoding.DecodeString("/DBsAAH/7m1eAAKQBQb+sX6o+wBWAlRDVUVJAAAAJ3//AAApMuANQAwOQU1DTiBMMDAxMjM0NTYJCFBPOjEyMzQ1DiRiY2IxZGQ4ZS1kMzIzLTQ1ODktOWQ3OC1hM2QxMzYyYWJiYjYwAQGtc8xr") - padOpenSignal, err := NewSCTE35(append([]byte{0x0}, padOpenSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(padOpenSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be two open signals (%d)", len(state.Open())) - } - - // 0x37 = event_id: 0 - seg_num: 3 - seg_expected: 3 - // This closed the 0x30 and the 0x36 - thirdInSignalBytes, _ := base64.StdEncoding.DecodeString("/DBGAAF0WZJuAP/wBQb+AAAAAAAwAi5DVUVJAAAAAH+/CR9TSUdOQUw6M0dTTmozZ29NbG9BQUFBQUFBQUJCQT09NwMDFkn/Gw==") - thirdInSignal, err := NewSCTE35(append([]byte{0x0}, thirdInSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(thirdInSignal.Descriptors()[0]) - if err != nil { - t.Error("ProcessDescriptor of out returned unexpected err:", err) - } - if len(closed) != 2 { - t.Errorf("Two events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be no open signal (%d)", len(state.Open())) - } -} - -// Test the logic for when we recieve a VSS signal -func TestVSS(t *testing.T) { - state := NewState() - - outSignalBytes, _ := base64.StdEncoding.DecodeString("/DB7AAFe1ms7AP/wBQb+AAAAAABlAlJDVUVJAABeT3+XDUMJIUJMQUNLT1VUOlEza2dMYmx4UzlhTmh4S24wY1N0MlE9PQ4eY29tY2FzdDpsaW5lYXI6bGljZW5zZXJvdGF0aW9uQAAAAg9DVUVJAABeT3+XAABBAAC9uy+v") - outSignal, err := NewSCTE35(append([]byte{0x0}, outSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - // 0x40 - closed, err := state.ProcessDescriptor(outSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - if state.Open()[0].TypeID() != SegDescUnscheduledEventStart { - t.Errorf("Expected segmentation_type_id 0x40 but got %x", state.Open()[0].TypeID()) - } - if state.Open()[0].EventID() != 24143 { - t.Errorf("Expected event_id 24143 but got %d", state.Open()[0].EventID()) - } - - // 0x41 - closed, err = state.ProcessDescriptor(outSignal.Descriptors()[1]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should not be any open signals (%d)", len(state.Open())) - } -} - -// Test the logic for when we recieve a VSS signal -// with same signalID and eventID as the earlier one -// and that we drop it. -func TestVSSSameSignalIdBackToBack(t *testing.T) { - state := NewState() - - vss_1 := "/DB7AAH//y+UAP/wBQb+AAAAAABlAlJDVUVJAAALkn+XDUMJIUJMQUNLT1VUOnZUaDZqMUNDRFZ3QUFBQUFBQUFCQVE9PQ4eY29tY2FzdDpsaW5lYXI6bGljZW5zZXJvdGF0aW9uQAAAAg9DVUVJAAALkn+XAABBAABupj9l" //PTS: 8589881236 , EventID: 2962, SignalID:vTh6j1CCDVwAAAAAAAABAQ== - vss_2 := "/DB7AAAAAOBaAP/wBQb+AAAAAABlAlJDVUVJAAALkn+XDUMJIUJMQUNLT1VUOnZUaDZqMUNDRFZ3QUFBQUFBQUFCQVE9PQ4eY29tY2FzdDpsaW5lYXI6bGljZW5zZXJvdGF0aW9uQAAAAg9DVUVJAAALkn+XAABBAAAuynMR" //PTS:57434, EventID:2962, SignalID:vTh6j1CCDVwAAAAAAAABAQ== - - outSignalBytes1, _ := base64.StdEncoding.DecodeString(vss_1) - outSignal1, err := NewSCTE35(append([]byte{0x0}, outSignalBytes1...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - // 0x40 - closed1, err := state.ProcessDescriptor(outSignal1.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed1) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed1)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - if state.Open()[0].TypeID() != SegDescUnscheduledEventStart { - t.Errorf("Expected segmentation_type_id 0x40 but got %x", state.Open()[0].TypeID()) - } - if state.Open()[0].EventID() != 2962 { - t.Errorf("Expected event_id 2962 but got %d", state.Open()[0].EventID()) - } - - // 0x41 - closed1, err = state.ProcessDescriptor(outSignal1.Descriptors()[1]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed1) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed1)) - } - if len(state.Open()) != 0 { - t.Errorf("There should not be any open signals (%d)", len(state.Open())) - } - - // Send another VSS signal with the same signalID and same eventID - // Check that we drop the signal. - outSignalBytes2, _ := base64.StdEncoding.DecodeString(vss_2) - outSignal2, err := NewSCTE35(append([]byte{0x0}, outSignalBytes2...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - // 0x40 - closed2, err := state.ProcessDescriptor(outSignal2.Descriptors()[0]) - if err != gots.ErrSCTE35DuplicateDescriptor { - t.Errorf("ProcessDescriptor should have dropped this as it is a duplicate descriptor: %s", err.Error()) - } - if len(state.Open()) != 0 { - t.Errorf("This signal should have been dropped, there should be 0 open signals (%d)", len(state.Open())) - } - // 0x41 - closed2, err = state.ProcessDescriptor(outSignal2.Descriptors()[1]) - if err != gots.ErrSCTE35MissingOut { - t.Errorf("0x40 was dropped so 0x41 should have been dropped as there was no matching out: %s", err.Error()) - } - if len(closed2) != 0 { - t.Errorf("0 events should have been closed (%d were)", len(closed2)) - } - if len(state.Open()) != 0 { - t.Errorf("There should not be any open signals (%d)", len(state.Open())) - } -} - -func printState(s State, header string) { - fmt.Printf("\n%s\n", header) - for _, open := range s.Open() { - fmt.Printf("%X - %d - (%d/%d) - %s\n", open.TypeID(), open.EventID(), open.SegmentNumber(), open.SegmentsExpected(), base64.StdEncoding.EncodeToString(open.SCTE35().Data())) - } - println() -} - -func TestOutInInOutIn36_37_10_37_37(t *testing.T) { - state := NewState() - - // 0x36 - event_id:0 - seg_num: 0 - seg_expected: 0 - outSignalBytes, _ := base64.StdEncoding.DecodeString("/DBLAAEs/S0UAP/wBQb+AAAAAAA1AjNDVUVJT///9X//AACky4AJH1NJR05BTDoyWURWeCtSKzlWc0FBQUFBQUFBQkFRPT02AAD9DXQ/") - outSignal, err := NewSCTE35(append([]byte{0x0}, outSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(outSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x37 - event_id: 0 - seg_num: 1 - seg_expected: 3 - firstInSignalBytes, _ := base64.StdEncoding.DecodeString("/DBGAAEtT5LUAP/wBQb+AAAAAAAwAi5DVUVJT///9X+/CR9TSUdOQUw6MllEVngrUis5VnNBQUFBQUFBQUJBZz09NwEDU/ktPg==") - firstInSignal, err := NewSCTE35(append([]byte{0x0}, firstInSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(firstInSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (%d)", len(state.Open())) - } - - // 0x10 closes 0x36 as per the SCTE-35 2020 spec - In35SignalBytes, _ := base64.StdEncoding.DecodeString("/DBFAAAAABeOAP/wBQb+WxdHNQAvAi1DVUVJACSqm3/9ABNP17wMGURJU0MyMjA1NjVfMDAyXzAxXzU3MUEtMDEQAQEeP8NH") - In35Signal, err := NewSCTE35(append([]byte{0x0}, In35SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(In35Signal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 signal (%d)", len(state.Open())) - } - - // 0x37 - event_id: 0 - seg_num: 2 - seg_expected: 3 - secondInSignalBytes, _ := base64.StdEncoding.DecodeString("/DBGAAEteMW0AP/wBQb+AAAAAAAwAi5DVUVJT///9X+/CR9TSUdOQUw6MllEVngrUis5VnNBQUFBQUFBQUJBdz09NwID1nPQRg==") - secondInSignal, err := NewSCTE35(append([]byte{0x0}, secondInSignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(secondInSignal.Descriptors()[0]) - // ErrSCTE35MissingOut expected as 0x30 closed the 0x36, the 0x37 has nothing to close. - if err != nil && err != gots.ErrSCTE35MissingOut { - t.Errorf("ProcessDescriptor returned an error: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should have been 1 open signal, but we saw (%d) open signals instead.", len(state.Open())) - } -} - -// Tests closing logic of 0x11 with 0x10, 0x20 ,0x30 -func Test11ClosingLogic(t *testing.T) { - state := NewState() - - // 0x10 - scteDesc10SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc10Signal) - outSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc10SignalBytes ...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(outSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be one open signal (have %d)", len(state.Open())) - } - - // 0x20 - scteDesc20SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc20Signal) - outSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc20SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - closed, err = state.ProcessDescriptor(outSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be two open signals (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 3 { - t.Errorf("There should be three open signals (have %d)", len(state.Open())) - } - - // 0x11, it should close the open 0x10, 0x20 ,0x30 - scteDesc11SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc11Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc11SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 3 { - t.Errorf("Three events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be no open signal (have %d)", len(state.Open())) - } -} - -func Test11Closing22(t *testing.T) { - state := NewState() - - // 0x22 - scteDesc22SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc22Signal) - outSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc22SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(outSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x11, it should close the open 0x22 - scteDesc11SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc11Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc11SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("One event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be no open signal (have %d)", len(state.Open())) - } -} - -func Test11Closing34_36_40_44(t *testing.T) { - state := NewState() - - //0x34 - scteDesc34SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc34Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc34SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x36 - scteDesc36SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc36Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc36SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // 0x40 - scteDesc40SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc40Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc40SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 3 { - t.Errorf("There should be 3 open signals (have %d)", len(state.Open())) - } - - // 0x44 - scteDesc44SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc44Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc44SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 4 { - t.Errorf("There should be 4 open signals (have %d)", len(state.Open())) - } - - // 0x11 - it should close the open 0x34, 0x36, 0x40, 0x44 - scteDesc11SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc11Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc11SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 4 { - t.Errorf("4 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be no open signal (have %d)", len(state.Open())) - } -} - -// Tests closing logic of 0x45 -func Test45ClosingLogic(t *testing.T) { - state := NewState() - - //0x44 - scteDesc44SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc44Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc44SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // 0x45 - closes 0x44 and 0x30 - scteDesc45SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc45Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc45SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 2 { - t.Errorf("2 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be no open signals (have %d)", len(state.Open())) - } -} -// Tests closing logic of 0x44 -func Test44ClosingLogic(t *testing.T) { - state := NewState() - - // 0x44 - scteDesc44SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc44Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc44SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes, _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // Different 0x44 - closes the earlier 0x44 and 0x30 - scteDesc44SignalBytes1, _ := base64.StdEncoding.DecodeString(scteDesc44Signal1) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc44SignalBytes1...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 2 { - t.Errorf("2 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { // the last 0x44 sent should still be open - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } -} - -// Tests closing logic of 0x23 -func Test23ClosingLogic_30_34_36_44(t *testing.T) { - state := NewState() - - // 0x34 - scteDesc34SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc34Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc34SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x36 - scteDesc36SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc36Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc36SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // 0x44 - scteDesc44SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc44Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc44SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 3 { - t.Errorf("There should be 3 open signals (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 4 { - t.Errorf("There should be 4 open signals (have %d)", len(state.Open())) - } - - // 0x23 - This should close 0x30, 0x34, 0x36, 0x44 - scteDesc23SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc23Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc23SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 4 { - t.Errorf("4 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be 0 open signals (have %d)", len(state.Open())) - } -} - -func Test23Closing22Logic(t *testing.T){ - state := NewState() - - // 0x22 - scteDesc22SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc22Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc22SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x23 - This should close 0x22 - scteDesc23SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc23Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc23SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be 0 open signals (have %d)", len(state.Open())) - } -} - -func Test22ClosingLogic_30_34_36_44_22(t *testing.T){ - state := NewState() - - // 0x36 - scteDesc36SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc36Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc36SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x34 - scteDesc34SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc34Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc34SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // 0x44 - scteDesc44SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc44Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc44SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 3 { - t.Errorf("There should be 3 open signals (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 4 { - t.Errorf("There should be 4 open signals (have %d)", len(state.Open())) - } - - // 0x22 - should close 0x30, 0x34, 0x36, 0x44 - scteDesc22SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc22Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc22SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 4 { - t.Errorf("4 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // New 0x22 - should close the last 0x22 - scteDesc22SignalBytes1 , _ := base64.StdEncoding.DecodeString(scteDesc22Signal1) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc22SignalBytes1...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } -} - -func Test31ClosingLogic(t *testing.T){ - state := NewState() - - // 0x30 - scteDesc30SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x31 - scteDesc31SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc31Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc31SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be 0 open signals (have %d)", len(state.Open())) - } -} - -func Test30Closing30Logic(t *testing.T){ - state := NewState() - - // 0x30 - scteDesc30SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x30 - new one, will close the old 0x30 - scteDesc30SignalBytes1 , _ := base64.StdEncoding.DecodeString(scteDesc30Signal1) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes1...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } -} - -func Test21ClosingLogic_30_34_36(t *testing.T){ - state := NewState() - - // 0x34 - scteDesc34SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc34Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc34SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x36 - scteDesc36SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc36Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc36SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 3 { - t.Errorf("There should be 3 open signals (have %d)", len(state.Open())) - } - - // 0x21 - This should close 0x30, 0x34, 0x36 - scteDesc21SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc21Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc21SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 3 { - t.Errorf("3 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be 0 open signals (have %d)", len(state.Open())) - } -} - -func Test21Closing20Logic(t *testing.T){ - state := NewState() - - // 0x20 - scteDesc20SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc20Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc20SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x21 - This should close 0x20 - scteDesc21SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc21Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc21SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 0 { - t.Errorf("There should be 0 open signals (have %d)", len(state.Open())) - } -} - -func Test20ClosingLogic_20_30_34_36(t *testing.T){ - state := NewState() - - // 0x36 - scteDesc36SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc36Signal) - inSignal, err := NewSCTE35(append([]byte{0x0}, scteDesc36SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err := state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // 0x34 - scteDesc34SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc34Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc34SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 2 { - t.Errorf("There should be 2 open signals (have %d)", len(state.Open())) - } - - // 0x30 - scteDesc30SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc30Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc30SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 0 { - t.Errorf("No events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 3 { - t.Errorf("There should be 3 open signals (have %d)", len(state.Open())) - } - - // 0x20 - should close 0x30, 0x34, 0x36 - scteDesc20SignalBytes , _ := base64.StdEncoding.DecodeString(scteDesc20Signal) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc20SignalBytes...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 3 { - t.Errorf("3 events should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } - - // New 0x20 - should close the last 0x20 - scteDesc20SignalBytes1 , _ := base64.StdEncoding.DecodeString(scteDesc20Signal1) - inSignal, err = NewSCTE35(append([]byte{0x0}, scteDesc20SignalBytes1...)) - if err != nil { - t.Fatal("Error creating SCTE-35 signal, return err:", err.Error()) - } - - closed, err = state.ProcessDescriptor(inSignal.Descriptors()[0]) - if err != nil { - t.Errorf("ProcessDescriptor returned an error unexpectedly: %s", err.Error()) - } - if len(closed) != 1 { - t.Errorf("1 event should have been closed (%d were)", len(closed)) - } - if len(state.Open()) != 1 { - t.Errorf("There should be 1 open signal (have %d)", len(state.Open())) - } -} \ No newline at end of file diff --git a/v2/tsutils.go b/v2/tsutils.go deleted file mode 100644 index 33870f6..0000000 --- a/v2/tsutils.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -MIT License - -Copyright 2016 Comcast Cable Communications Management, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package gots - -import "encoding/binary" - -// ComputeCRC computes the CRC hash for the provided byte slice -func ComputeCRC(input []byte) []byte { - var mask uint32 = 0xffffffff - var msb uint32 = 0x80000000 - var poly uint32 = 0x04c11db7 - var crc uint32 = 0x46af6449 - - for i := 0; i < len(input); i++ { - item := uint32(input[i]) - - for j := 0; j < 8; j++ { - top := crc & msb - crc = ((crc << 1) & mask) | ((item >> uint32(7-j)) & 0x1) - if top != 0 { - crc ^= poly - } - } - } - - for i := 0; i < 32; i++ { - top := crc & msb - crc = ((crc << 1) & mask) - if top != 0 { - crc ^= poly - } - } - - crcBytes := make([]byte, 4) - binary.BigEndian.PutUint32(crcBytes, crc) - return crcBytes -}