From ea1458014c3f971693d5efb373927947c24b0cff Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 24 Jan 2019 22:29:43 +1030 Subject: [PATCH 1/8] mts: fixing cc --- cmd/ts-repair/main.go | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 cmd/ts-repair/main.go diff --git a/cmd/ts-repair/main.go b/cmd/ts-repair/main.go new file mode 100644 index 00000000..bcb1ebc1 --- /dev/null +++ b/cmd/ts-repair/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "flag" + "io" + "os" + + "bitbucket.org/ausocean/av/stream/mts" + "github.com/Comcast/gots/packet" +) + +const ( + errBadInPath = "No file path provided, or file does not exist" + errCantCreateOut = "Can't create output file" + errCantGetPid = "Can't get pid from packet" + errReadFail = "Read failed" + errWriteFail = "Write to file failed" + usage = "The path to the file to be repaired" +) + +var ccMap = map[int]byte{ + mts.PatPid: 1, + mts.PmtPid: 1, + mts.VideoPid: 0, +} + +type Packet [mts.PacketSize]byte + +func (p *Packet) setCC(cc byte) { + (*p)[3] |= cc & 0xf +} + +func main() { + // Deal with input flags + inPtr := flag.String("path", "", usage) + outPtr := flag.String("out", "out.ts", usage) + flag.Parse() + + // Try and open the given input file, otherwise panic - we can't do anything + inFile, err := os.Open(*inPtr) + defer inFile.Close() + if err != nil { + panic(errBadInPath) + } + + // Try and create output file, otherwise panic - we can't do anything + outFile, err := os.Create(*outPtr) + defer outFile.Close() + if err != nil { + panic(errCantCreateOut) + } + + // Read each packet from the input file reader + var p Packet + for { + // If we get an end of file then return, otherwise we panic - can't do anything else + if _, err := inFile.Read(p[:mts.PacketSize]); err == io.EOF { + return + } else if err != nil { + panic(errReadFail + ": " + err.Error()) + } + + // Get the pid from the packet and set the cc based on this pid using our map + pid, err := packet.Pid((*packet.Packet)(&p)) + if err != nil { + panic(errCantGetPid) + } + p.setCC(ccFor(int(pid))) + + // Write this packet to the output file + if _, err := outFile.Write(p[:]); err != nil { + panic(errWriteFail + ": " + err.Error()) + } + } +} + +// ccFor gets the next cc for the given pid +func ccFor(pid int) byte { + cc := ccMap[pid] + ccMap[pid] = (cc + 1) & 0xf + return cc +} From 0fe5affc00221d7c4b978ecac9eb296cb6984d24 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 25 Jan 2019 15:03:57 +1030 Subject: [PATCH 2/8] cmd/ts-repair: got setting of discontinuity indicators working and also adding adaptation fields to pat and pmt for this reason. --- cmd/ts-repair/main.go | 125 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 10 deletions(-) diff --git a/cmd/ts-repair/main.go b/cmd/ts-repair/main.go index bcb1ebc1..f34da350 100644 --- a/cmd/ts-repair/main.go +++ b/cmd/ts-repair/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "io" "os" @@ -15,25 +16,94 @@ const ( errCantGetPid = "Can't get pid from packet" errReadFail = "Read failed" errWriteFail = "Write to file failed" - usage = "The path to the file to be repaired" + errBadMode = "Bad fix mode" +) + +const ( + inUsage = "The path to the file to be repaired" + outUsage = "Output file path" + modeUsage = "Fix mode: 0 = cc-shift, 1 = di-update" +) + +const ( + ccShift = iota + diUpdate ) var ccMap = map[int]byte{ - mts.PatPid: 1, - mts.PmtPid: 1, - mts.VideoPid: 0, + mts.PatPid: 16, + mts.PmtPid: 16, + mts.VideoPid: 16, } +var packetNo int + +type Option func(p *Packet) + type Packet [mts.PacketSize]byte +func (p *Packet) CC() byte { + return (*p)[3] & 0x0f +} + func (p *Packet) setCC(cc byte) { (*p)[3] |= cc & 0xf } +func (p *Packet) setDI(di bool) { + if di { + p[5] |= 0x80 + } else { + p[5] &= 0x7f + } +} + +func (p *Packet) addAdaptationField(options ...Option) { + // Create space for adaptation field + copy(p[mts.HeadSize+mts.DefaultAdaptationSize:], p[mts.HeadSize:len(p)-mts.DefaultAdaptationSize]) + + // TODO: seperate into own function + // Update adaptation field control + p[mts.AdaptationControlIdx] &= 0xff ^ mts.AdaptationControlMask + p[mts.AdaptationControlIdx] |= mts.AdaptationControlMask + // Default the adaptationfield + p.resetAdaptation() + + for _, option := range options { + option(p) + } +} + +func (p *Packet) resetAdaptation() { + p[mts.AdaptationIdx] = mts.DefaultAdaptationBodySize + p[mts.AdaptationBodyIdx] = 0x00 +} + +func (p *Packet) hasAdaptation() bool { + afc := p[mts.AdaptationControlIdx] & mts.AdaptationControlMask + if afc == 0x20 || afc == 0x30 { + return true + } else { + return false + } +} + +func DiscontinuityIndicator(f bool) Option { + return func(p *Packet) { + set := byte(mts.DiscontinuityIndicatorMask) + if !f { + set = 0x00 + } + p[mts.DiscontinuityIndicatorIdx] &= 0xff ^ mts.DiscontinuityIndicatorMask + p[mts.DiscontinuityIndicatorIdx] |= mts.DiscontinuityIndicatorMask & set + } +} + func main() { // Deal with input flags - inPtr := flag.String("path", "", usage) - outPtr := flag.String("out", "out.ts", usage) + inPtr := flag.String("in", "", inUsage) + outPtr := flag.String("out", "out.ts", outUsage) + modePtr := flag.Int("mode", diUpdate, modeUsage) flag.Parse() // Try and open the given input file, otherwise panic - we can't do anything @@ -59,14 +129,42 @@ func main() { } else if err != nil { panic(errReadFail + ": " + err.Error()) } - + packetNo++ // Get the pid from the packet and set the cc based on this pid using our map pid, err := packet.Pid((*packet.Packet)(&p)) if err != nil { panic(errCantGetPid) } - p.setCC(ccFor(int(pid))) + cc := p.CC() + expect, exists := expectedCC(int(pid)) + if !exists { + updateCCMap(int(pid), cc) + } else { + switch *modePtr { + case ccShift: + p.setCC(expect) + + case diUpdate: + + if cc != expect { + fmt.Printf("packetNo: %v pid: %v, cc: %v, expect: %v\n", packetNo, pid, cc, expect) + if p.hasAdaptation() { + fmt.Println("hasAdaptation") + p.setDI(true) + } else { + fmt.Println("doesn't have adaptation") + fmt.Println(p) + p.addAdaptationField(DiscontinuityIndicator(true)) + fmt.Println(p) + } + updateCCMap(int(pid), p.CC()) + } + + default: + panic(errBadMode) + } + } // Write this packet to the output file if _, err := outFile.Write(p[:]); err != nil { panic(errWriteFail + ": " + err.Error()) @@ -75,8 +173,15 @@ func main() { } // ccFor gets the next cc for the given pid -func ccFor(pid int) byte { +func expectedCC(pid int) (byte, bool) { cc := ccMap[pid] + if cc == 16 { + return 16, false + } + ccMap[pid] = (cc + 1) & 0xf + return cc, true +} + +func updateCCMap(pid int, cc byte) { ccMap[pid] = (cc + 1) & 0xf - return cc } From e6671c5772f84618c84a0c399ecf064c09bc3913 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 25 Jan 2019 15:18:27 +1030 Subject: [PATCH 3/8] cmd/ts-repair: got rid of debug prints --- cmd/ts-repair/main.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/ts-repair/main.go b/cmd/ts-repair/main.go index f34da350..039ada9b 100644 --- a/cmd/ts-repair/main.go +++ b/cmd/ts-repair/main.go @@ -148,15 +148,11 @@ func main() { case diUpdate: if cc != expect { - fmt.Printf("packetNo: %v pid: %v, cc: %v, expect: %v\n", packetNo, pid, cc, expect) + fmt.Printf("***** Discontinuity found (packetNo: %v pid: %v, cc: %v, expect: %v)\n", packetNo, pid, cc, expect) if p.hasAdaptation() { - fmt.Println("hasAdaptation") p.setDI(true) } else { - fmt.Println("doesn't have adaptation") - fmt.Println(p) p.addAdaptationField(DiscontinuityIndicator(true)) - fmt.Println(p) } updateCCMap(int(pid), p.CC()) } From c4d68f15623688bd79f1aad8d8fed448417ad1c5 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 25 Jan 2019 16:10:13 +1030 Subject: [PATCH 4/8] cmd/ts-repair: updated comments and made funcs more robust --- cmd/ts-repair/main.go | 68 ++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/cmd/ts-repair/main.go b/cmd/ts-repair/main.go index 039ada9b..f3f73bb2 100644 --- a/cmd/ts-repair/main.go +++ b/cmd/ts-repair/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "fmt" "io" @@ -10,21 +11,26 @@ import ( "github.com/Comcast/gots/packet" ) +// Various errors that we can encounter. const ( - errBadInPath = "No file path provided, or file does not exist" - errCantCreateOut = "Can't create output file" - errCantGetPid = "Can't get pid from packet" - errReadFail = "Read failed" - errWriteFail = "Write to file failed" - errBadMode = "Bad fix mode" + errBadInPath = "No file path provided, or file does not exist" + errCantCreateOut = "Can't create output file" + errCantGetPid = "Can't get pid from packet" + errReadFail = "Read failed" + errWriteFail = "Write to file failed" + errBadMode = "Bad fix mode" + errAdaptationPresent = "Adaptation field is already present in packet" + errNoAdaptationField = "No adaptation field in this packet" ) +// Consts describing flag usage. const ( inUsage = "The path to the file to be repaired" outUsage = "Output file path" modeUsage = "Fix mode: 0 = cc-shift, 1 = di-update" ) +// Repair modes. const ( ccShift = iota diUpdate @@ -36,20 +42,27 @@ var ccMap = map[int]byte{ mts.VideoPid: 16, } +// packetNo will keep track of the ts packet number for reference. var packetNo int +// Option defines a func that performs an action on p in order to change a ts option. type Option func(p *Packet) +// Packet is a byte array of size mts.PacketSize i.e. 188 bytes. We define this +// to allow us to write receiver funcs for the [mts.PacketSize]byte type. type Packet [mts.PacketSize]byte +// CC returns the CC of p. func (p *Packet) CC() byte { return (*p)[3] & 0x0f } +// setCC sets the CC of p. func (p *Packet) setCC(cc byte) { (*p)[3] |= cc & 0xf } +// setDI sets the discontinuity counter of p. func (p *Packet) setDI(di bool) { if di { p[5] |= 0x80 @@ -58,27 +71,41 @@ func (p *Packet) setDI(di bool) { } } -func (p *Packet) addAdaptationField(options ...Option) { - // Create space for adaptation field +// addAdaptationField adds an adaptation field to p, and applys the passed options to this field. +// TODO: this will probably break if we already have adaptation field. +func (p *Packet) addAdaptationField(options ...Option) error { + if p.hasAdaptation() { + return errors.New(errAdaptationPresent) + } + // Create space for adaptation field. copy(p[mts.HeadSize+mts.DefaultAdaptationSize:], p[mts.HeadSize:len(p)-mts.DefaultAdaptationSize]) // TODO: seperate into own function - // Update adaptation field control + // Update adaptation field control. p[mts.AdaptationControlIdx] &= 0xff ^ mts.AdaptationControlMask p[mts.AdaptationControlIdx] |= mts.AdaptationControlMask - // Default the adaptationfield + // Default the adaptationfield. p.resetAdaptation() + // Apply and options that have bee passed. for _, option := range options { option(p) } + return nil } -func (p *Packet) resetAdaptation() { +// resetAdaptation sets fields in ps adaptation field to 0 if the adaptation field +// exists, otherwise an error is returned. +func (p *Packet) resetAdaptation() error { + if !p.hasAdaptation() { + return errors.New(errNoAdaptationField) + } p[mts.AdaptationIdx] = mts.DefaultAdaptationBodySize p[mts.AdaptationBodyIdx] = 0x00 + return nil } +// hasAdaptation returns true if p has an adaptation field and false otherwise. func (p *Packet) hasAdaptation() bool { afc := p[mts.AdaptationControlIdx] & mts.AdaptationControlMask if afc == 0x20 || afc == 0x30 { @@ -88,6 +115,8 @@ func (p *Packet) hasAdaptation() bool { } } +// DiscontinuityIndicator returns and Option that will set p's discontinuity +// indicator according to f. func DiscontinuityIndicator(f bool) Option { return func(p *Packet) { set := byte(mts.DiscontinuityIndicatorMask) @@ -130,23 +159,26 @@ func main() { panic(errReadFail + ": " + err.Error()) } packetNo++ - // Get the pid from the packet and set the cc based on this pid using our map + + // Get the pid from the packet pid, err := packet.Pid((*packet.Packet)(&p)) if err != nil { panic(errCantGetPid) } + // Get the cc from the packet and also the expected cc (if exists) cc := p.CC() expect, exists := expectedCC(int(pid)) if !exists { updateCCMap(int(pid), cc) } else { switch *modePtr { + // ccShift mode shifts all CC regardless of presence of Discontinuities or not case ccShift: p.setCC(expect) - + // diUpdate mode finds discontinuities and sets the discontinuity indicator to true. + // If we have a pat or pmt then we need to add an adaptation field and then set the DI. case diUpdate: - if cc != expect { fmt.Printf("***** Discontinuity found (packetNo: %v pid: %v, cc: %v, expect: %v)\n", packetNo, pid, cc, expect) if p.hasAdaptation() { @@ -156,19 +188,20 @@ func main() { } updateCCMap(int(pid), p.CC()) } - default: panic(errBadMode) } } - // Write this packet to the output file + + // Write this packet to the output file. if _, err := outFile.Write(p[:]); err != nil { panic(errWriteFail + ": " + err.Error()) } } } -// ccFor gets the next cc for the given pid +// expectedCC returns the expected cc for the given pid. If the cc hasn't been +// used yet, then 16 and false is returned. func expectedCC(pid int) (byte, bool) { cc := ccMap[pid] if cc == 16 { @@ -178,6 +211,7 @@ func expectedCC(pid int) (byte, bool) { return cc, true } +// updateCCMap updates the cc for the passed pid. func updateCCMap(pid int, cc byte) { ccMap[pid] = (cc + 1) & 0xf } From aea41fb71064a6b67f502c92a960e3fccc842ae6 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 25 Jan 2019 16:25:01 +1030 Subject: [PATCH 5/8] stream/mts: adding some constants --- stream/mts/encoder.go | 39 ++++++++++++---------------------- stream/mts/mpegts.go | 49 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 60393285..53342624 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -171,14 +171,6 @@ var ( pmtTable = standardPmtTimeLocation.Bytes() ) -const ( - sdtPid = 17 - patPid = 0 - pmtPid = 4096 - videoPid = 256 - streamID = 0xe0 // First video stream ID. -) - // Time related constants. const ( // ptsOffset is the offset added to the clock to determine @@ -213,18 +205,13 @@ func NewEncoder(dst io.Writer, fps float64) *Encoder { ptsOffset: ptsOffset, continuity: map[int]byte{ - patPid: 0, - pmtPid: 0, - videoPid: 0, + PatPid: 0, + PmtPid: 0, + VideoPid: 0, }, } } -const ( - hasPayload = 0x1 - hasAdaptationField = 0x2 -) - const ( hasDTS = 0x1 hasPTS = 0x2 @@ -241,7 +228,7 @@ func (e *Encoder) Encode(nalu []byte) error { } // Prepare PES data. pesPkt := pes.Packet{ - StreamID: streamID, + StreamID: StreamID, PDI: hasPTS, PTS: e.pts(), Data: nalu, @@ -253,10 +240,10 @@ func (e *Encoder) Encode(nalu []byte) error { for len(buf) != 0 { pkt := Packet{ PUSI: pusi, - PID: videoPid, + PID: VideoPid, RAI: pusi, - CC: e.ccFor(videoPid), - AFC: hasAdaptationField | hasPayload, + CC: e.ccFor(VideoPid), + AFC: HasAdaptationField | HasPayload, PCRF: pusi, } n := pkt.FillPayload(buf) @@ -286,9 +273,9 @@ func (e *Encoder) writePSI() error { // Write PAT. patPkt := Packet{ PUSI: true, - PID: patPid, - CC: e.ccFor(patPid), - AFC: hasPayload, + PID: PatPid, + CC: e.ccFor(PatPid), + AFC: HasPayload, Payload: patTable, } _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PacketSize])) @@ -309,9 +296,9 @@ func (e *Encoder) writePSI() error { // Create mts packet from pmt table. pmtPkt := Packet{ PUSI: true, - PID: pmtPid, - CC: e.ccFor(pmtPid), - AFC: hasPayload, + PID: PmtPid, + CC: e.ccFor(PmtPid), + AFC: HasPayload, Payload: pmtTable, } _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PacketSize])) diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 0bef80d2..71849513 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -37,6 +37,53 @@ const ( PayloadSize = 176 ) +const ( + SdtPid = 17 + PatPid = 0 + PmtPid = 4096 + VideoPid = 256 + StreamID = 0xe0 // First video stream ID. + HeadSize = 4 + DefaultAdaptationSize = 2 +) + +const ( + AdaptationIdx = 4 + AdaptationControlIdx = 3 + AdaptationBodyIdx = AdaptationIdx + 1 + AdaptationControlMask = 0x30 + DefaultAdaptationBodySize = 1 +) + +const ( + HasPayload = 0x1 + HasAdaptationField = 0x2 +) + +// Adaptation field body masks. +const ( + DiscontinuityIndicatorMask = 0x80 + RandomAccessIndicatorMask = 0x40 + ElementaryStreamPriorityIndicatorMask = 0x20 + ProgramClockReferenceFlagMask = 0x10 + OriginalProgramClockReferenceFlagMask = 0x08 + SplicingPointFlagMask = 0x04 + TransportPrivateDataFlagMask = 0x02 + AdaptationFieldExtensionMask = 0x01 +) + +// Adaptation field body indexes. +const ( + DiscontinuityIndicatorIdx = AdaptationIdx + 1 + RandomAccessIndicatorIdx = AdaptationIdx + 1 + ElementaryStreamPriorityIndicatorIdx = AdaptationIdx + 1 + ProgramClockReferenceFlagIdx = AdaptationIdx + 1 + OriginalProgramClockReferenceFlagIdx = AdaptationIdx + 1 + SplicingPointFlagIdx = AdaptationIdx + 1 + TransportPrivateDataFlagIdx = AdaptationIdx + 1 + AdaptationFieldExtensionFlagIdx = AdaptationIdx + 1 +) + /* The below data struct encapsulates the fields of an MPEG-TS packet. Below is the formatting of an MPEG-TS packet for reference! @@ -135,7 +182,7 @@ func FindPMT(d []byte) (p []byte, i int, err error) { } for i = 0; i < len(d); i += PacketSize { pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2]) - if pid == pmtPid { + if pid == PmtPid { p = d[i+4 : i+PacketSize] return } From 700328627dcdce15c04f32c705e8533d1733103e Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 8 Feb 2019 00:14:22 +1030 Subject: [PATCH 6/8] cmd/ts-repair: added required consts and undid changes to mts pkg --- cmd/ts-repair/main.go | 43 ++++++++++++++++++++++++------------- stream/mts/encoder.go | 39 ++++++++++++++++++++++------------ stream/mts/mpegts.go | 49 +------------------------------------------ 3 files changed, 56 insertions(+), 75 deletions(-) diff --git a/cmd/ts-repair/main.go b/cmd/ts-repair/main.go index f3f73bb2..f95d16fe 100644 --- a/cmd/ts-repair/main.go +++ b/cmd/ts-repair/main.go @@ -11,6 +11,21 @@ import ( "github.com/Comcast/gots/packet" ) +const ( + PatPid = 0 + PmtPid = 4096 + VideoPid = 256 + HeadSize = 4 + DefaultAdaptationSize = 2 + AdaptationIdx = 4 + AdaptationControlIdx = 3 + AdaptationBodyIdx = AdaptationIdx + 1 + AdaptationControlMask = 0x30 + DefaultAdaptationBodySize = 1 + DiscontinuityIndicatorMask = 0x80 + DiscontinuityIndicatorIdx = AdaptationIdx + 1 +) + // Various errors that we can encounter. const ( errBadInPath = "No file path provided, or file does not exist" @@ -37,9 +52,9 @@ const ( ) var ccMap = map[int]byte{ - mts.PatPid: 16, - mts.PmtPid: 16, - mts.VideoPid: 16, + PatPid: 16, + PmtPid: 16, + VideoPid: 16, } // packetNo will keep track of the ts packet number for reference. @@ -48,8 +63,8 @@ var packetNo int // Option defines a func that performs an action on p in order to change a ts option. type Option func(p *Packet) -// Packet is a byte array of size mts.PacketSize i.e. 188 bytes. We define this -// to allow us to write receiver funcs for the [mts.PacketSize]byte type. +// Packet is a byte array of size PacketSize i.e. 188 bytes. We define this +// to allow us to write receiver funcs for the [PacketSize]byte type. type Packet [mts.PacketSize]byte // CC returns the CC of p. @@ -78,12 +93,12 @@ func (p *Packet) addAdaptationField(options ...Option) error { return errors.New(errAdaptationPresent) } // Create space for adaptation field. - copy(p[mts.HeadSize+mts.DefaultAdaptationSize:], p[mts.HeadSize:len(p)-mts.DefaultAdaptationSize]) + copy(p[HeadSize+DefaultAdaptationSize:], p[HeadSize:len(p)-DefaultAdaptationSize]) // TODO: seperate into own function // Update adaptation field control. - p[mts.AdaptationControlIdx] &= 0xff ^ mts.AdaptationControlMask - p[mts.AdaptationControlIdx] |= mts.AdaptationControlMask + p[AdaptationControlIdx] &= 0xff ^ AdaptationControlMask + p[AdaptationControlIdx] |= AdaptationControlMask // Default the adaptationfield. p.resetAdaptation() @@ -100,14 +115,14 @@ func (p *Packet) resetAdaptation() error { if !p.hasAdaptation() { return errors.New(errNoAdaptationField) } - p[mts.AdaptationIdx] = mts.DefaultAdaptationBodySize - p[mts.AdaptationBodyIdx] = 0x00 + p[AdaptationIdx] = DefaultAdaptationBodySize + p[AdaptationBodyIdx] = 0x00 return nil } // hasAdaptation returns true if p has an adaptation field and false otherwise. func (p *Packet) hasAdaptation() bool { - afc := p[mts.AdaptationControlIdx] & mts.AdaptationControlMask + afc := p[AdaptationControlIdx] & AdaptationControlMask if afc == 0x20 || afc == 0x30 { return true } else { @@ -119,12 +134,12 @@ func (p *Packet) hasAdaptation() bool { // indicator according to f. func DiscontinuityIndicator(f bool) Option { return func(p *Packet) { - set := byte(mts.DiscontinuityIndicatorMask) + set := byte(DiscontinuityIndicatorMask) if !f { set = 0x00 } - p[mts.DiscontinuityIndicatorIdx] &= 0xff ^ mts.DiscontinuityIndicatorMask - p[mts.DiscontinuityIndicatorIdx] |= mts.DiscontinuityIndicatorMask & set + p[DiscontinuityIndicatorIdx] &= 0xff ^ DiscontinuityIndicatorMask + p[DiscontinuityIndicatorIdx] |= DiscontinuityIndicatorMask & set } } diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index e49d57b5..02761b91 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -171,6 +171,14 @@ var ( pmtTable = standardPmtTimeLocation.Bytes() ) +const ( + sdtPid = 17 + patPid = 0 + pmtPid = 4096 + videoPid = 256 + streamID = 0xe0 // First video stream ID. +) + // Time related constants. const ( // ptsOffset is the offset added to the clock to determine @@ -205,13 +213,18 @@ func NewEncoder(dst io.Writer, fps float64) *Encoder { ptsOffset: ptsOffset, continuity: map[int]byte{ - PatPid: 0, - PmtPid: 0, - VideoPid: 0, + patPid: 0, + pmtPid: 0, + videoPid: 0, }, } } +const ( + hasPayload = 0x1 + hasAdaptationField = 0x2 +) + const ( hasDTS = 0x1 hasPTS = 0x2 @@ -231,7 +244,7 @@ func (e *Encoder) Encode(nalu []byte) error { // Prepare PES data. pesPkt := pes.Packet{ - StreamID: StreamID, + StreamID: streamID, PDI: hasPTS, PTS: e.pts(), Data: nalu, @@ -243,10 +256,10 @@ func (e *Encoder) Encode(nalu []byte) error { for len(buf) != 0 { pkt := Packet{ PUSI: pusi, - PID: VideoPid, + PID: videoPid, RAI: pusi, - CC: e.ccFor(VideoPid), - AFC: HasAdaptationField | HasPayload, + CC: e.ccFor(videoPid), + AFC: hasAdaptationField | hasPayload, PCRF: pusi, } n := pkt.FillPayload(buf) @@ -275,9 +288,9 @@ func (e *Encoder) writePSI() error { // Write PAT. patPkt := Packet{ PUSI: true, - PID: PatPid, - CC: e.ccFor(PatPid), - AFC: HasPayload, + PID: patPid, + CC: e.ccFor(patPid), + AFC: hasPayload, Payload: patTable, } _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PacketSize])) @@ -298,9 +311,9 @@ func (e *Encoder) writePSI() error { // Create mts packet from pmt table. pmtPkt := Packet{ PUSI: true, - PID: PmtPid, - CC: e.ccFor(PmtPid), - AFC: HasPayload, + PID: pmtPid, + CC: e.ccFor(pmtPid), + AFC: hasPayload, Payload: pmtTable, } _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PacketSize])) diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 71849513..0bef80d2 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -37,53 +37,6 @@ const ( PayloadSize = 176 ) -const ( - SdtPid = 17 - PatPid = 0 - PmtPid = 4096 - VideoPid = 256 - StreamID = 0xe0 // First video stream ID. - HeadSize = 4 - DefaultAdaptationSize = 2 -) - -const ( - AdaptationIdx = 4 - AdaptationControlIdx = 3 - AdaptationBodyIdx = AdaptationIdx + 1 - AdaptationControlMask = 0x30 - DefaultAdaptationBodySize = 1 -) - -const ( - HasPayload = 0x1 - HasAdaptationField = 0x2 -) - -// Adaptation field body masks. -const ( - DiscontinuityIndicatorMask = 0x80 - RandomAccessIndicatorMask = 0x40 - ElementaryStreamPriorityIndicatorMask = 0x20 - ProgramClockReferenceFlagMask = 0x10 - OriginalProgramClockReferenceFlagMask = 0x08 - SplicingPointFlagMask = 0x04 - TransportPrivateDataFlagMask = 0x02 - AdaptationFieldExtensionMask = 0x01 -) - -// Adaptation field body indexes. -const ( - DiscontinuityIndicatorIdx = AdaptationIdx + 1 - RandomAccessIndicatorIdx = AdaptationIdx + 1 - ElementaryStreamPriorityIndicatorIdx = AdaptationIdx + 1 - ProgramClockReferenceFlagIdx = AdaptationIdx + 1 - OriginalProgramClockReferenceFlagIdx = AdaptationIdx + 1 - SplicingPointFlagIdx = AdaptationIdx + 1 - TransportPrivateDataFlagIdx = AdaptationIdx + 1 - AdaptationFieldExtensionFlagIdx = AdaptationIdx + 1 -) - /* The below data struct encapsulates the fields of an MPEG-TS packet. Below is the formatting of an MPEG-TS packet for reference! @@ -182,7 +135,7 @@ func FindPMT(d []byte) (p []byte, i int, err error) { } for i = 0; i < len(d); i += PacketSize { pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2]) - if pid == PmtPid { + if pid == pmtPid { p = d[i+4 : i+PacketSize] return } From 7d85b78b6d5b546e0defc7952654990fb71b2fcc Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 8 Feb 2019 00:16:19 +1030 Subject: [PATCH 7/8] moved cmd/ts-repair to experimentation --- {cmd => experimentation}/ts-repair/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {cmd => experimentation}/ts-repair/main.go (100%) diff --git a/cmd/ts-repair/main.go b/experimentation/ts-repair/main.go similarity index 100% rename from cmd/ts-repair/main.go rename to experimentation/ts-repair/main.go From b2150cf8ded9d84ed051092d13bfd91f9cbdefd9 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 8 Feb 2019 00:25:47 +1030 Subject: [PATCH 8/8] experimentation/ts-repair: added description to file header --- experimentation/ts-repair/main.go | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/experimentation/ts-repair/main.go b/experimentation/ts-repair/main.go index f95d16fe..bed81f19 100644 --- a/experimentation/ts-repair/main.go +++ b/experimentation/ts-repair/main.go @@ -1,3 +1,36 @@ +/* +NAME + ts-repair/main.go + +DESCRIPTION + This program attempts to repair mpegts discontinuities using one of two methods + as selected by the mode flag. Setting the mode flag to 0 will result in repair + by shifting all CCs such that they are continuous. Setting the mode flag to 1 + will result in repair through setting the discontinuity indicator to true at + packets where a discontinuity exists. + + Specify the input file with the in flag, and the output file with out flag. + +AUTHOR + Saxon A. Nelson-Milton + +LICENSE + mpegts.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses). +*/ + package main import (